diff --git a/README.adoc b/README.adoc index fa50028b99d0..9b0fed606140 100644 --- a/README.adoc +++ b/README.adoc @@ -1,10 +1,8 @@ -= Address Book (Level 4) += Marketing Morph ifdef::env-github,env-browser[:relfileprefix: docs/] https://travis-ci.org/se-edu/addressbook-level4[image:https://travis-ci.org/se-edu/addressbook-level4.svg?branch=master[Build Status]] -https://ci.appveyor.com/project/damithc/addressbook-level4[image:https://ci.appveyor.com/api/projects/status/3boko2x2vr5cc3w2?svg=true[Build status]] -https://coveralls.io/github/se-edu/addressbook-level4?branch=master[image:https://coveralls.io/repos/github/se-edu/addressbook-level4/badge.svg?branch=master[Coverage Status]] -https://www.codacy.com/app/damith/addressbook-level4?utm_source=github.com&utm_medium=referral&utm_content=se-edu/addressbook-level4&utm_campaign=Badge_Grade[image:https://api.codacy.com/project/badge/Grade/fc0b7775cf7f4fdeaf08776f3d8e364a[Codacy Badge]] + ifdef::env-github[] image::docs/images/Ui.png[width="600"] @@ -14,19 +12,13 @@ ifndef::env-github[] image::images/Ui.png[width="600"] endif::[] -* This is a desktop Address Book application. It has a GUI but most of the user interactions happen using a CLI (Command Line Interface). -* It is a Java sample application intended for students learning Software Engineering while using Java as the main programming language. -* It is *written in OOP fashion*. It provides a *reasonably well-written* code example that is *significantly bigger* (around 6 KLoC)than what students usually write in beginner-level SE modules. -* What's different from https://github.com/se-edu/addressbook-level3[level 3]: -** A more sophisticated GUI that includes a list panel and an in-built Browser. -** More test cases, including automated GUI testing. -** Support for _Build Automation_ using Gradle and for _Continuous Integration_ using Travis CI. +* This is a desktop marketing application. It has a GUI but most of the user interactions happen using a CLI (Command Line Interface). +* It is meant to be utilized by a marketing company in order to effectively organize *potential targets* and *potential clients*. == Site Map * <> * <> -* <> * <> * <> @@ -35,5 +27,6 @@ endif::[] * Some parts of this sample application were inspired by the excellent http://code.makery.ch/library/javafx-8-tutorial/[Java FX tutorial] by _Marco Jakob_. * Libraries used: https://github.com/TestFX/TestFX[TextFX], https://github.com/FasterXML/jackson[Jackson], https://github.com/google/guava[Guava], https://github.com/junit-team/junit5[JUnit5] +* AddressBook-Level4 project created by SE-EDU initiative at https://github.com/se-edu/ == Licence : link:LICENSE[MIT] diff --git a/build.gradle b/build.gradle index 4f2949b6e774..39f78668c334 100644 --- a/build.gradle +++ b/build.gradle @@ -77,7 +77,7 @@ dependencies { } shadowJar { - archiveName = 'addressbook.jar' + archiveName = 'MarketingMorph.jar' destinationDir = file("${buildDir}/jar/") } diff --git a/docs/AboutUs.adoc b/docs/AboutUs.adoc index e647ed1e715a..32b0f181d979 100644 --- a/docs/AboutUs.adoc +++ b/docs/AboutUs.adoc @@ -4,53 +4,44 @@ :imagesDir: images :stylesDir: stylesheets -AddressBook - Level 4 was developed by the https://se-edu.github.io/docs/Team.html[se-edu] team. + -_{The dummy content given below serves as a placeholder to be used by future forks of the project.}_ + -{empty} + +Marketing Morph was developed by the https://github.com/orgs/cs2103-ay1819s2-w16-3/teams/developers[w16-3] team. + We are a team based in the http://www.comp.nus.edu.sg[School of Computing, National University of Singapore]. == Project Team -=== John Doe -image::damithc.jpg[width="150", align="left"] -{empty}[http://www.comp.nus.edu.sg/~damithch[homepage]] [https://github.com/damithc[github]] [<>] +=== Dongzhe Fan +image::FanDongzhe.png[width="150", align="left"] +{empty}[http://github.com/yijinl[github]] [<>] -Role: Project Advisor +Role: Developer +Responsibilities: UG/DG maintenance ''' -=== John Roe -image::lejolly.jpg[width="150", align="left"] -{empty}[http://github.com/lejolly[github]] [<>] +=== Jiayu Zhang +image::zhangjiayu0303.png[width="150", align="left"] +{empty}[http://github.com/ZhangJiayu0303[github]] [<>] -Role: Team Lead + -Responsibilities: UI +Role: Developer +Responsibilities: UG/DG maintenance, ''' -=== Johnny Doe -image::yijinl.jpg[width="150", align="left"] -{empty}[http://github.com/yijinl[github]] [<>] +=== Jitesh Khiani +image::jkhiani.png[width="150", align="left"] +{empty}[https://github.com/jkhiani[github]] [<>] -Role: Developer + -Responsibilities: Data +Role: Developer +Responsibilities: UG/DG maintenance, GH Repo maintenance, Favorites System, Maps Addition ''' -=== Johnny Roe -image::m133225.jpg[width="150", align="left"] -{empty}[http://github.com/m133225[github]] [<>] - -Role: Developer + -Responsibilities: Dev Ops + Threading - -''' +=== Roan Urquhart +image::roanurquhart.png[width="150", align="left"] +{empty}[https://github.com/roanurquhart[github]] [<>] -=== Benson Meier -image::yl_coder.jpg[width="150", align="left"] -{empty}[http://github.com/yl-coder[github]] [<>] +Role: Developer +Responsibilities: UG/DG maintenance, GH Repo maintenance, Company Implementation -Role: Developer + -Responsibilities: UI ''' diff --git a/docs/DeveloperGuide.adoc b/docs/DeveloperGuide.adoc index 8b92d5fb7e62..f037b26a09e1 100644 --- a/docs/DeveloperGuide.adoc +++ b/docs/DeveloperGuide.adoc @@ -1,4 +1,4 @@ -= AddressBook Level 4 - Developer Guide += Marketing Morph - Developer Guide :site-section: DeveloperGuide :toc: :toc-title: @@ -15,7 +15,7 @@ ifdef::env-github[] endif::[] :repoURL: https://github.com/se-edu/addressbook-level4/tree/master -By: `Team SE-EDU`      Since: `Jun 2016`      Licence: `MIT` +By: `Team W16-3`      Since: `Jan 2019`      Licence: `MIT` == Setting up @@ -191,6 +191,7 @@ Given below is the Sequence Diagram for interactions within the `Logic` componen .Interactions Inside the Logic Component for the `delete 1` Command image::DeletePersonSdForLogic.png[width="800"] +// tag::modelc[] [[Design-Model]] === Model component @@ -206,10 +207,19 @@ The `Model`, * exposes an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. * does not depend on any of the other three components. +The addition of the 'Company' class has altered the 'Model Class Diagram' in several ways + +* In order to store the list of companies required for the application, a 'UniqueCompanyList' class was added +* The company class was added in order to distinguish between company and person objects within the UI +* Each class has the same attributes but each with a different connotations + +At the current time, with the commands 'addcpny' and 'listcpny' the user can add a company to the address book and list all of the companies added respectively + [NOTE] As a more OOP model, we can store a `Tag` list in `Address Book`, which `Person` can reference. This would allow `Address Book` to only require one `Tag` object per unique `Tag`, instead of each `Person` needing their own `Tag` object. An example of how such a model may look like is given below. + + image:ModelClassBetterOopDiagram.png[width="800"] +// end::modelc[] [[Design-Storage]] === Storage component @@ -313,12 +323,60 @@ image::UndoRedoActivityDiagram.png[width="650"] ** Cons: Requires dealing with commands that have already been undone: We must remember to skip these commands. Violates Single Responsibility Principle and Separation of Concerns as `HistoryManager` now needs to do two different things. // end::undoredo[] -// tag::dataencryption[] -=== [Proposed] Data Encryption +// tag::favoriteSystem[] +=== Favorite System +==== Current Implementation + +The favorite system is facilitated by `AddressBook`, +it extends the address book system by allowing the user to save/delete/list favorites, stored internally as a `UniquePersonList`. +Additionally, it implements the following operations: + +* `AddressBook#addFavorites()` -- Saves the person into the favorites list. +* `AddressBook#removeFavorite()` -- Removes the person from the favorites list. +* `AddressBook#hasFavorite()` -- Checks if the person exists in the favorites list. +* `AddressBook#getFavoritesList()` -- Returns a list of all favorite persons. + +These operations are exposed in the `Model` interface as `Model#addFavorites()`, `Model#removeFavorite()`, `Model#hasFavorite()`, and `Model#getFavoritesList()`. + +Given below is an example usage scenario of how the favorite mechanism behaves at each step. + +Step 1. The user launches the application for the first time. An empty UniquePersonList is created in `AddressBook`. + +Step 2. The user executes `favorite Alex` to save the person Alex into their favorites list. The favorite command creates a new `NameContainsKeywordsPredicate` updating the filteredPersonList and calls `Model#addFavorites()` and `Model#commitAddressBook` thereby updating the favorites list stored in `AddressBook` and updating the filteredPersonList. + +Step 3. The user repeats this a few times, adding a few other persons. + +Step 4. The user wants to view all favorites and executes `listFav` to view all favorites. This creates a new `PersonIsFavoritePredicate` which is passed in a model and calls `Model#getFavoritesList` and check if it contains the person. The command class then calls `Model#updateFilteredPersonList` with the predicate to only display all filtered (favorite) persons. + +Step 5. The user notices they accidentally added the wrong person (Alex for example) into the favorites list and executes `delFav Alex Yeoh` to remove them from the favorites list. + +The following is a sequence diagram describing the flow of how the favorite command works behind the scenes. + +image::favoriteActivityDiagram.png[width="650"] + +Favorites are stored in addressbook.json as another entry into the existing dictionary of key-value pairs and follows identical architecture to that of adding/removing persons from the personsList. + +==== Design Considerations + +===== Aspect: Data structure to save the favorites + +* **Alternative 1 (current choice):** Save all favorites using a simple list. +** Pros: Easy to implement. +** Cons: May have runtime issues in deleting if the list gets large. +* **Alternative 2:** Linked list to store favorites. +** Pros: Efficient to delete favorites from anywhere in the list. +** Cons: We must ensure that the implementation is proper as there is a lot of complexity. -_{Explain here how the data encryption feature will be implemented}_ +===== Aspect: Method of marking favorites -// end::dataencryption[] +* **Alternative 1 (current choice):** Create another entry into the dictionary within addressbook.json to store favorites over multiple sessions. +** Pros: Easy to implement, clean seperate commands/functions related to adding/deleting from favorites (doesn't affect addressbook contacts in any way). +** Cons: A lot of data is repeated when storing favorites. +* **Alternative 2:** Create an extra boolean attribute within each person to store favorite status. +** Pros: More space effective. +** Cons: We must ensure that the implementation is proper as we can accidentally affect the persons that are regular contacts, harder to visually inspect our json file to see who favorites are/aren't. + +// end::favoriteSystem[] === Logging @@ -340,6 +398,26 @@ We are using `java.util.logging` package for logging. The `LogsCenter` class is Certain properties of the application can be controlled (e.g user prefs file location, logging level) through the configuration file (default: `config.json`). + +=== Three new attributes: Salary, Occupation, Relationship +==== Current Implementation +The add Command is modified in this enhancement. Previously, user can only add a person's name, phone number, address and tags to the address book. +After this enhancement, following attributes are added to the person's information: + +* `SALARY`: salary must be a string with more than one digits +* `OCCUPATION`: occupation can be a string with any characters +* `RELATIONSHIP`: relationship must be a string with one word (no blank allowed) + +===== How to implement the command +Users just input: +add n/NAME p/PHONE e/EMAIL a/ADDRESS s/SALARY o/OCCUPATION r/RELATIONSHIP [t/TAG] +Example: add n/John Doe p/98765432 e/johnd@example.com a/311, Clementi Ave 2, #02-25 s/100000o/bankerr/singlet/friends t/owesMoney + + +===== Pros and Cons +** Pros: Easy to implement, and the add format just follows the previous version +** Cons: When implementing add Command, Users must add all information except the optional TAG attribute, this makes the command not flexible enough + == Documentation We use asciidoc for writing documentation. @@ -818,7 +896,7 @@ See this https://github.com/se-edu/addressbook-level4/pull/599[PR] for the step- * is reasonably comfortable using CLI apps *Value proposition*: manage contacts faster than a typical mouse/GUI driven app - +// tag::users[] [appendix] == User Stories @@ -827,20 +905,44 @@ Priorities: High (must have) - `* * \*`, Medium (nice to have) - `* \*`, Low (un [width="59%",cols="22%,<23%,<25%,<30%",options="header",] |======================================================================= |Priority |As a ... |I want to ... |So that I can... -|`* * *` |new user |see usage instructions |refer to instructions when I forget how to use the App +|`* * *` |new user |see usage instructions |refer to instructions when I forget how to use the Marketing Morph App + +|`* * *` |user |add a new person or company | keep track of potential target demographics and clients + +|`* * *` |user |delete a person or company |remove entries that I no longer need + +|`* * *` |user |find a person or company by name |locate details of persons without having to go through the entire list + +|`* * *` |user |sort persons by name |locate a person easily + +|`* * *` |user |sort persons by occupation |locate a cohort of persons easily + +|`* * *` |user |sort companies by sector |locate a cohort of companies easily + +|`* * *` |user |create a group of clients |to organize potential clients into a separate group -|`* * *` |user |add a new person | +|`* * *` |user |add contacts to a favorites list |to have quick access to the most used contacts -|`* * *` |user |delete a person |remove entries that I no longer need +|`* * *` |user |delete contacts to a favorites list |to have quick access to the most used contacts -|`* * *` |user |find a person by name |locate details of persons without having to go through the entire list +|`* * *` |user |update person or company data |to change information that is no longer correct -|`* *` |user |hide <> by default |minimize chance of someone else seeing them by accident +|`* * *` |user |analyze salary for all persons |know the average and median salary for all persons -|`*` |user with many persons in the address book |sort persons by name |locate a person easily +|`* * *` |user |analyze revenue for all companies |know the average and median revenue for all companies + +|`* *` |user |can analyze which people can afford a product |to effectively target potential consumers + +|`* *` |user |can determine which people can shop at particular businesses |to sell information to companies + +|`* *` |user |view a persons address on a map |visualize where they live + +|`* *` |user |see number of entries in the addressbook | to know how many entries I have + +|`*` |user |hide <> by default |minimize chance of someone else seeing them by accident |======================================================================= -_{More to be added}_ +// end::users[] [appendix] == Use Cases @@ -873,6 +975,93 @@ Use case ends. + Use case resumes at step 2. +// tag::jiteshUseCase[] +[discrete] +=== Use case: Favorite person + +*MSS* + +1. User requests to list persons +2. AddressBook shows a list of persons +3. User requests to add a specific person in the list to their favorites +4. AddressBook saves the person in the favorites list ++ +Use case ends. + +*Extensions* + +[none] +* 2a. The list is empty. ++ +Use case ends. + +* 3a. The given name does not exist. ++ +[none] +** 3a1. AddressBook shows an error message. ++ +* 3b. The given name is not specific enough. ++ +[none] +** 3b1. AddressBook shows an error message and displays potential options. ++ +* 3c. The given name is already a favorite. ++ +[none] +** 3c1. AddressBook shows an error message. ++ +Use case resumes at step 1. + +[discrete] +=== Use case: Delete favorite person + +*MSS* + +1. User requests to list favorites +2. AddressBook shows a list of favorites persons +3. User requests to delete a specific person in the favorites list +4. AddressBook deletes the person from the favorites list ++ +Use case ends. + +*Extensions* + +[none] +* 2a. The list is empty. ++ +Use case ends. + +* 3a. The given name does not exist. ++ +[none] +** 3a1. AddressBook shows an error message. ++ +* 3b. The given name is not specific enough. ++ +[none] +** 3b1. AddressBook shows an error message and displays potential options. ++ +* 3c. The given name isn't a favorite. ++ +[none] +** 3c1. AddressBook shows an error message. ++ +Use case resumes at step 1. + +[discrete] +=== Use case: View person's location + +*MSS* + +1. User requests to list persons +2. AddressBook shows a list of persons +3. User clicks on a specific persons card on the list +4. AddressBook displays a Google Maps window of their address in the Main Window ++ +Use case ends. +// end::jiteshUseCase[] + + _{More to be added}_ [appendix] @@ -911,13 +1100,11 @@ Cons: * ... [appendix] + == Instructions for Manual Testing Given below are instructions to test the app manually. -[NOTE] -These instructions only provide a starting point for testers to work on; testers are expected to do more _exploratory_ testing. - === Launch and Shutdown . Initial launch @@ -934,6 +1121,51 @@ These instructions only provide a starting point for testers to work on; testers _{ more test cases ... }_ +// tag::jiteshAppendix[] +Testing `Favorite` + +[NOTE] +These instructions only provide a starting point for testers to work on; testers are expected to do more _exploratory_ testing. + +=== Adding a favorite + +. Adding a favorite to the list + +.. Prerequisites: List all persons using the `list` command. Multiple persons in the list. +.. Test case: `favorite Alex` + + Expected: Alex Yeoh not added to the favorites list. Status message requests more specificity, if there are multiple 'Alex' in our client list the cards will update to show them all. +.. Test case: `favorite Alex Yeoh` + + Expected: Alex Yeoh added to the favorites list. Status message confirms addition. Timestamp in the status bar is updated. +.. Test case: `favorite Alex Yeoh` + + Expected: Favorite is not added (since you just added them). Status message updated to reflect current standing. Status bar remains the same. +.. Incorrect favorite commands to try: `favorite blah`, `favorite` both cases the person is not a client and therefore cannot be added + + Expected: Person does not exist. + +=== List all favorites + +. Listing all favorites within the application + +.. Prerequisites: None (will show an empty panel if there are no favorites) +.. Test Case: `listFav` + +=== Deleting a favorite + +. Deleting a favorite while all companies are listed + +.. Prerequisites: List all persons using the `listFav` command. +.. Test case: `delFav Alex` + + Expected: Alex Yeoh not removed from the favorites list. Status message requests more specificity. +.. Test case: `delFav Alex Yeoh` + + Expected: Alex Yeoh removed from the favorites list. Status message confirms addition. Timestamp in the status bar is updated. +.. Incorrect favorite commands to try: `delFav blah`, `delFav` both cases the person is not a client and therefore cannot be added + + Expected: Person does not exist. +// end::jiteshAppendix[] + +Testing `Company` + +[NOTE] +These instructions only provide a starting point for testers to work on; testers are expected to do more _exploratory_ testing. + === Deleting a person . Deleting a person while all persons are listed @@ -948,10 +1180,81 @@ _{ more test cases ... }_ _{ more test cases ... }_ -=== Saving data +=== Adding a company -. Dealing with missing/corrupted data files +. Adding a company to the application -.. _{explain how to simulate a missing/corrupted file and the expected behavior}_ +.. Prerequisites: None +.. Test Case: `addcpny n/Walmart p/98765432 e/walmart@gmail.com a/John street, block 123, #01-01 s/234525223 o/Retail r/Corporation` -_{ more test cases ... }_ +=== List all companies + +. Listing companies within the application + +.. Prerequisites: There must already be at least one company stored in the application +.. Test Case: `listcpny` + +=== Editing a company + +. Editing a company + +.. Prerequisites: There must be at least one company stored in the application +.. Test Case: `editcpny 1 p/91234567 e/monay@example.com` + + Expected: edits the first company in the company column + +=== Finding a company + +. Finding a company + +.. Prerequisites: The company that is searched for must exist +.. Test Case: `find Burger` + + Expected: May return `Burger Spot` and `Burger King` if both exist as companies + +// tag::manualJiayu[] +=== Finding a person by occupation + +. Finding a person by occupation + +.. Prerequisites: The person that is searched for must exist +.. Test Case: `findOccupation Professor` + + Expected: May return `Alice` and `Bob` if both exist as persons whose occupation is professor + +=== Finding a company by sector + +. Finding a company by sector + +.. Prerequisites: The company that is searched for must exist +.. Test Case: `findSector Bank` + + Expected: May return `DSS` and `OCCC` if both exist as companies whose sector is bank + +// end::manualJiayu[] + +=== Deleting a company + +. Deleting a company while all companies are listed + +.. Prerequisites: List all companies using the `listcpny` command. +.. Test case: `delete 1` + + Expected: First company is deleted from the list. Details of the deleted company shown in the status message. +// end::manualTesting[] + +// tag::SortPerson[] +Testing `Sort Person` + +=== Sort Person + +. Sort clients by some attributes + +.. Prerequisites: Must exist a list of clients and each of them has the attribute to be sorted. +.. Test case: `sortper name seq` + Expected: Sort the list of clients by their name in sequence order. +.. Test case: `sortper salary seq` + Expected: Sort the list of clients by their salary in reverse order. +.. Test case: `sortper name rev` + Expected: Sort the list of clients by their name in sequence order. +.. Test case: `sortper salary rev` + Expected: Sort the list of clients by their salary in reverse order. + +// end::SortPerson[] + + diff --git a/docs/UserGuide.adoc b/docs/UserGuide.adoc index 7e0070e12f49..7690c12dcb53 100644 --- a/docs/UserGuide.adoc +++ b/docs/UserGuide.adoc @@ -1,4 +1,4 @@ -= AddressBook Level 4 - User Guide += Marketing Morph - User Guide :site-section: UserGuide :toc: :toc-title: @@ -14,11 +14,11 @@ ifdef::env-github[] endif::[] :repoURL: https://github.com/se-edu/addressbook-level4 -By: `Team SE-EDU` Since: `Jun 2016` Licence: `MIT` +By: `Sky Walker` Since: `Feb 2019` Licence: `NUS` == Introduction -AddressBook Level 4 (AB4) is for those who *prefer to use a desktop app for managing contacts*. More importantly, AB4 is *optimized for those who prefer to work with a Command Line Interface* (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB4 can get your contact management tasks done faster than traditional GUI apps. Interested? Jump to the <> to get started. Enjoy! +Marketing Morph is for marketers who want to organize consumer data. More importantly, Marketing Morph will allow marketers to use a GUI to keep track of different consumers, maintain client lists, and perform market analysis and research in one simple interface. == Quick Start @@ -34,8 +34,8 @@ e.g. typing *`help`* and pressing kbd:[Enter] will open the help window. . Some example commands you can try: * *`list`* : lists all contacts -* **`add`**`n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : adds a contact named `John Doe` to the Address Book. -* **`delete`**`3` : deletes the 3rd contact shown in the current list +* **`add`**`add n/Jane Doe p/23456789 e/janed@example.com a/113, Clementi Ave 2, #02-24 s/5000 o/teacher r/single` : adds a contact named `Jane Doe` to the Address Book. +* **`delete`**`1` : deletes the 1st contact shown in the current person list * *`exit`* : exits the app . Refer to <> for details of each command. @@ -56,27 +56,56 @@ e.g. typing *`help`* and pressing kbd:[Enter] will open the help window. Format: `help` -=== Adding a person: `add` +// tag::add[] +=== Adding a person : `add` -Adds a person to the address book + -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]...` +Adds a person to the consumer list. + +Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS s/SALARY o/OCCUPATION r/RELATIONSHIP [t/TAG]...` [TIP] A person can have any number of tags (including 0) Examples: -* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` -* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal` +* `add n/Jane Doe p/23456789 e/janed@example.com a/113, Clementi Ave 2, #02-24 s/5000 o/teacher r/single` +* `add n/John Doe p/98765432 e/johnd@example.com a/311, Clementi Ave 2, #02-25 s/100000 o/banker r/single t/friends t/owesMoney` + +// end::add[] + +// tag::addcpny[] +=== Adding a company : `addcpny` + +Adds a company to the client list + +Format: `addcpny` n/NAME p/NUMBER e/EMAIL a/ADDRESS s/REVENUE o/SECTOR r/STRUCTURE [t/TAG]… + +Explanation: +`REVENUE` represents a company's yearly revenue. +`SECTOR` represents the type of company i.e. Food, Retail, Bank. +`STRUCTURE` distinguishes the organization of the company i.e. Conglomerate, Local, International, Private, Public, etc. + +Examples: + +`addcpny` n/Walmart p/98765432 e/walmart@gmail.com a/John street, block 123, #01-01 s/234525223 o/Retail r/Corporation + +`addcpny` n/Betsy Cakes p/1234567 e/cakes@gmail.com a/Bob street, block 232 s/2242023 o/Food r/LocalBusiness t/popular t/local +// end::addcpny[] === Listing all persons : `list` -Shows a list of all persons in the address book. + +Shows a list of all persons in the consumer list. + Format: `list` +// tag::listcpny[] +=== Listing all companies : `listcpny` + +Shows a list of all companies in the clients list. + +Format: `listcpny` +// end::listcpny[] + === Editing a person : `edit` -Edits an existing person in the address book. + +Edits an existing person in the consumer list. + Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]...` **** @@ -94,7 +123,29 @@ Edits the phone number and email address of the 1st person to be `91234567` and * `edit 2 n/Betsy Crower t/` + Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags. -=== Locating persons by name: `find` +// tag::editcpny[] +=== Editing a company : `editcpny` + +Edits an existing company in the client list. + +Format: `editcpny INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]...` + +**** +* Edits the company at the specified `INDEX`. The index refers to the index number shown in the displayed company list. The index *must be a positive integer* 1, 2, 3, ... +* At least one of the optional fields must be provided. +* Existing values will be updated to the input values. +* When editing tags, the existing tags of the company will be removed i.e adding of tags is not cumulative. +* You can remove all the company's tags by typing `t/` without specifying any tags after it. +**** + +Examples: + +* `editcpny 1 p/91234567 e/monay@example.com` + +Edits the phone number and email address of the 1st company to be `91234567` and `monay@example.com` respectively. +* `editcpny 2 n/Small Money t/` + +Edits the name of the 2nd company to be `Small Money` and clears all existing tags. +// end::editcpny[] + +=== Locating persons by name : `find` Finds persons whose names contain any of the given keywords. + Format: `find KEYWORD [MORE_KEYWORDS]` @@ -114,6 +165,61 @@ Returns `john` and `John Doe` * `find Betsy Tim John` + Returns any person having names `Betsy`, `Tim`, or `John` +// tag::findcpny[] +=== Locating companies by name : `findcpny` + +Finds companies whose names contain any of the given keywords. + +Format: `findcpny KEYWORD [MORE_KEYWORDS]` + +**** +* The search is case insensitive. e.g `mcdonalds` will match `McDonalds` +* The order of the keywords does not matter. e.g. `McDonalds Bojangles` will match `Bojangles McDonalds` +* Only the name is searched. +* Only full words will be matched e.g. `McDonald` will not match `McDonalds` +* Companies matching at least one keyword will be returned (i.e. `OR` search). e.g. `McDonalds Bojangles` will return `McDonalds`, `Bojangles` +**** + +Examples: + +* `find Burger` + +Returns `Burger Spot` and `Burger King` +* `find Burger Taco Shoes` + +Returns any person having names `Burger`, `Taco`, or `Shoes` +// end::findcpny[] + + +// tag::findOccupation[] +=== Locating persons by occupation : `findOccupation` + +**** +* The search is case insensitive. e.g `teacher` will match `Teacher` +* Only the occupation is searched. +* Only full words will be matched e.g. `Tea` will not match `Teacher` +* Persons matching the occupation will be returned +**** + +Examples: + +* `findOccupation teacher` + +Returns `Bob` and `Alice` (Their occupation is teacher) +// end::findOccupation[] + +// tag::findSector[] +=== Locating companies by sector : `findSector` + +**** +* The search is case insensitive. e.g `bank` will match `Bank` +* Only the sector is searched. +* Only full words will be matched e.g. `Ban` will not match `Bank` +* Companies matching the sector will be returned +**** + +Examples: + +* `findCompany bank` + +Returns `OCCC` (Its sector is bank) +// end::findSector[] + === Deleting a person : `delete` Deletes the specified person from the address book. + @@ -134,13 +240,35 @@ Deletes the 2nd person in the address book. `delete 1` + Deletes the 1st person in the results of the `find` command. +// tag::deletecpny[] +=== Deleting a company : `deletecpny` + +Deletes the specified Company from the address book. + +Format: `deletecpny INDEX` + +**** +* Deletes the company at the specified `INDEX`. +* The index refers to the index number shown in the displayed company list. +* The index *must be a positive integer* 1, 2, 3, ... +**** + +Examples: + +* `list` + +`delete 2` + +Deletes the 2nd company in the address book. +* `find Burger` + +`delete 1` + +Deletes the 1st company in the results of the `find` command. +// end::deletecpny[] + === Selecting a person : `select` Selects the person identified by the index number used in the displayed person list. + Format: `select INDEX` **** -* Selects the person and loads the Google search page the person at the specified `INDEX`. +* Selects the person and loads a Google Maps address of the person at the specified `INDEX`. * The index refers to the index number shown in the displayed person list. * The index *must be a positive integer* `1, 2, 3, ...` **** @@ -149,7 +277,7 @@ Examples: * `list` + `select 2` + -Selects the 2nd person in the address book. +Selects the 2nd person in the consumer list. * `find Betsy` + `select 1` + Selects the 1st person in the results of the `find` command. @@ -224,6 +352,192 @@ Format: `clear` Exits the program. + Format: `exit` +// tag::sortper[] +=== Sorting persons by name/salary/occupation/residential area/group : `sortper` + +Lists all people that meet the specified filter requirement(in sequence/reverse order). + +Format: sortper name/salary/phone/email/occupation/address/relationship seq/rev + +Examples: + +`sortper` salary seq (list all persons by salary in sequence order) + +`sortper` phone rev (list all persons by their phone number in reverse order) +// end::sortper[] + +// tag::sortcpny[] +=== Sorting companies by name/revenue/occupation/residential area/group : `sortcpny` + +Lists all companies that meet the specified filter requirement(in sequence/reverse order). + +Format: sortper name/revenue/phone/email/occupation/address/relationship seq/rev + +Examples: + +`sortcpny` revenue seq (list all companies by revenue in sequence order) + +`sortcpny` phone rev (list all companies by their phone number in reverse order) +// end::sortcpny[] + +=== Find clients: `findCli` `[coming in v2.0]` + +Lists all persons deemed capable of affording a specific product based on salary + +Format: `findCli` PRICE + +Examples: + +findCli 100 + +findCli 20 + + +=== Find shops: `findShops` `[coming in v2.0]` + + +Lists all stores a person is deemed capable of visiting based on salary and store pricing + + +Format: `findShops` [n/NAME] [s/SALARY] + +Examples: + +`findShops` n/steve smith + +`findShops` s/25000 + +// tag::favorite[] +=== Add to favorite list: `favorite` + +Adds a person to the “favorite” list + +**** +* The full name must be specifid +* If the full name isn't specified it displays a list of potential persons +* The person must exist as a client and can only be favorited once +**** + +Format: `favorite n/NAME` + +Examples: + +* `favorite John Doe` + +* `favorite John` (recommends all John's in the addressbook, adds none) + +image::multipleJohns.png[width="200"] + + +=== Delete person from favorite list: `delFav` + +Removes a person to the “favorite” list + +**** +* The full name must be specifid +* The person must exist as a favorite +**** + +Format: `delFav n/NAME` + +Examples: + +* `delFav John Doe` + +* `favorite John Doe` + + `delFav John Doe` + +=== List the favorite list: `listFav` + +Lists all persons in the “favorite” list under the clients panel in alphabetical order + +Format: `listFav` + +Example: + +* `favorite John Doe` + +`favorite Alex Yeoh` + +`favorite Roy Balakrishnan` + +`favorite David Li` + +`listFav` + +image::listFav.png[width="200"] + +// end::favorite[] + +=== Create group: `group` `[coming in v2.0]` + +Creates a group of persons + +Format: `group` GROUPNAME [[n/NAME] [a/ADDRESS]]... + +Examples: + +`group` g1 n/bob a/123 streets n/steve a/456 main n/smith a/111 abc st + +`group` winners n/jacob a/1 queen st n/adam a/10 north st + +// tag::export[] + +=== Export data as CSV: `export` + +Exports the data to a CSV file + +Format: `export` [FILEPATH] [FILENAME] + +Examples: + +`export` /Users/fandongzhe/Desktop/fg market (type in the whole filepath) +// end::export[] + +// tag::averageSalary[] +=== Show average salary for all persons: `averageSalary` + +Shows average salary for all persons in the address book + +Format: `averageSalary` +// end::averageSalary[] + +// tag::medianSalary[] +=== Show median salary for all persons: `medianSalary` + +Shows median salary for all persons in the address book + +Format: `medianSalary` +// end::medianSalary[] + +// tag::findmax[] +=== Show maximum salary for all clients: `findmax` + +Show maximum salary for all clients in the address book + +Format: `findmax` +// end::findmax[] + +// tag::findmin[] +=== Show minimum salary for all clients: `findmin` + +Show minimum salary for all clients in the address book + +Format: `findmin` +// end::findmin[] + +// tag::averageRevenue[] +=== Show average revenue for all companies: `averageRevenue` + +Shows average revenue for all companies in the address book + +Format: `averageRevenue` +// end::averageRevenue[] + +// tag::medianRevenue[] +=== Show median revenue for all revenue: `medianRevenue` + +Shows median revenue for all companies in the address book + +Format: `medianRevenue` +// end::medianRevenue[] + === Saving the data Address book data are saved in the hard disk automatically after any command that changes the data. + @@ -242,19 +556,44 @@ _{explain how the user can enable/disable data encryption}_ == Command Summary -* *Add* `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]...` + -e.g. `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague` -* *Clear* : `clear` -* *Delete* : `delete INDEX` + +* *Add* `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS s/SALARY o/OCCUPATION r/RELATIONSHIP [t/TAG]...` + +e.g. `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 s/100 o/baker r/single t/friend t/colleague` +* *Add Company* `addcpny n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS s/REVENUE o/SECTOR r/STRUCTURE [t/TAG]...` + +e.g. `addcpny n/McDonalds p/22342422 e/mcds@gmail.com a/23244 Super Tasty Rd s/303030303030 o/Food r/InternationalCorp` +* *Clear Persons* : `clear` +* *Clear Companies* : `clearcpny` +* *Delete Person* : `delete INDEX` + e.g. `delete 3` -* *Edit* : `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]...` + +* *Delete Company* : `deletecpny INDEX` + +e.g. `delete 2` +* *Edit Person* : `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]` + +* *Edit Company* : `editcpny INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]...` + +e.g. `editcpny 1 n/Burger King` +* *Find Person* : `find KEYWORD [MORE_KEYWORDS]` + e.g. `find James Jake` -* *List* : `list` +* *Find Company* `findcpny KEYWORD [MORE_KEYWORDS]` + +e.g. `findcpny McDonalds` +* *Find Occupation* `findOccupation KEYWORD` + +e.g. `findOccupation professor` +* *Find Sector* `findSector KEYWORD` + +e.g. `findSector bank` +* *List Persons* : `list` +* *List Companies* : `listcpny` +* *Sort Persons* : `sortper` +* *Sort Companies* : `sortcpny` * *Help* : `help` * *Select* : `select INDEX` + e.g.`select 2` +* *Add Favorite* : `favorite` +* *Show Average Salary* : `averageSalary` +* *Show Median Salary* : `medianSalary` +* *Show Average Revenue* : `averageRevenue` +* *Show Median Revenue* : `medianRevenue` +* *Delete Favorite* : `delFav` +* *List Favorite* : `listFav` * *History* : `history` * *Undo* : `undo` * *Redo* : `redo` +* *group* : `group` GROUPNAME [[n/NAME] [a/ADDRESS]]... +* *export* : `export` [CLIENTS] [CONSUMERS] [ALL] diff --git a/docs/images/FanDongzhe.png b/docs/images/FanDongzhe.png new file mode 100644 index 000000000000..76bc3d8084d7 Binary files /dev/null and b/docs/images/FanDongzhe.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index 4961edd74e76..010ecab80f72 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 5ec9c527b49c..ffecfb1ff70a 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/UiOld.png b/docs/images/UiOld.png new file mode 100644 index 000000000000..5bdf9941a980 Binary files /dev/null and b/docs/images/UiOld.png differ diff --git a/docs/images/favoriteActivityDiagram.png b/docs/images/favoriteActivityDiagram.png new file mode 100644 index 000000000000..5cb360cde486 Binary files /dev/null and b/docs/images/favoriteActivityDiagram.png differ diff --git a/docs/images/jkhiani.png b/docs/images/jkhiani.png new file mode 100644 index 000000000000..105bcf67ab79 Binary files /dev/null and b/docs/images/jkhiani.png differ diff --git a/docs/images/listFav.png b/docs/images/listFav.png new file mode 100644 index 000000000000..09b1fd332b36 Binary files /dev/null and b/docs/images/listFav.png differ diff --git a/docs/images/multipleJohns.png b/docs/images/multipleJohns.png new file mode 100644 index 000000000000..b75bab4488b4 Binary files /dev/null and b/docs/images/multipleJohns.png differ diff --git a/docs/images/roanurquhart.png b/docs/images/roanurquhart.png new file mode 100644 index 000000000000..b01d1ea75066 Binary files /dev/null and b/docs/images/roanurquhart.png differ diff --git a/docs/images/zhangjiayu0303.png b/docs/images/zhangjiayu0303.png new file mode 100644 index 000000000000..22f5cb6c15c4 Binary files /dev/null and b/docs/images/zhangjiayu0303.png differ diff --git a/docs/team/FanDongzhe.adoc b/docs/team/FanDongzhe.adoc new file mode 100644 index 000000000000..8159f09acd88 --- /dev/null +++ b/docs/team/FanDongzhe.adoc @@ -0,0 +1,92 @@ += Fan Dongzhe - Project Portfolio +:site-section: AboutUs +:imagesDir: ../images +:stylesDir: ../stylesheets + +== PROJECT: Marketing Morph + +--- + +== Overview + +Marketing Morph is a desktop application for marketers who want to organize consumer data. And it will allow marketers to use GUI and command lines to keep track of different consumers. + +== Summary of contributions + +* *Major enhancement*: added *the ability to sort people and export data* +** How I did it: I rewrite the java interface: comparator to implement sorting person/company by any attribute. And I use stream operation to export data. +** What it does: Allow marketers to find their potential clients more convenient, for they can sort clients by their salary and find which person is the best choice. Meanwhile, he/she can export the information of all clients for further analysis, like doing data analysis to find potential clients. +** Justification: This feature improves the product significantly because a user can easily find a person by sorting them. If he/she wants to find a client with the highest salary, he just need to sort all the clients by reverse order. +** Highlights: Sorting person by any parameters in any order and exporting data to any path. + +* *Minor enhancement*: Get the max/min salary of all the clients + +* *Code contributed* + +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/main/java/seedu/address/logic/commands/SortPersonCommand.java[SortPersonCommand.java] + +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/main/java/seedu/address/logic/commands/PersonComparator.java[PersonComparator.java] + +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/main/java/seedu/address/logic/parser/SortPersonCommandParser.java[SortPersonCommandComparator.java] + +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/main/java/seedu/address/logic/commands/SortCompanyCommand.java[SortCompanyComparator.java] + +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/main/java/seedu/address/logic/commands/CompanyComparator.java[CompanyComparator.java] + +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/main/java/seedu/address/logic/parser/SortCompanyCommandParser.java[SortCompanyCommandComparator.java] + +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/main/java/seedu/address/logic/commands/ExportCommand.java[ExportCommand.java] + +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/main/java/seedu/address/logic/commands/FindmaxCommand.java[FindmaxCommand.java] + +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/main/java/seedu/address/logic/commands/FindminCommand.java[FindminCommand.java] + +* *Test contributed* + +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/test/java/seedu/address/logic/commands/SortPersonCommandTest.java[SortPersonCommandTest.java] + +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/test/java/seedu/address/logic/parser/SortPersonCommandParserTest.java[SortPersonCommandParser.java] + + + +* *Other contributions*: + +** Project management: +*** Worked with teammates to diagnose and fix bugs + +** Documentation: +*** Write a rudiment of the User Guide with some basic commands +*** Add `sort command` and `export command` to User Guide + +** Community: +*** Asked several questions in the forum + + +== Contributions to the User Guide + + +|=== +|_Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users._ +|=== + +** Rudiment of the User Guide with some simple commands + +include::../UserGuide.adoc[tag=sortcpny] + +include::../UserGuide.adoc[tag=sortper] + +include::../UserGuide.adoc[tag=export] + +include::../UserGuide.adoc[tag=findmax] + +include::../UserGuide.adoc[tag=findmin] + +== Contributions to the Developer Guide + +|=== +|_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ +|=== + +== Sort Person + +. Sort clients by some attributes + +.. Prerequisites: Must exist a list of clients and each of them has the attribute to be sorted. +.. Test case: `sortper name seq` + Expected: Sort the list of clients by their name in sequence order. +.. Test case: `sortper salary seq` + Expected: Sort the list of clients by their salary in reverse order. +.. Test case: `sortper name rev` + Expected: Sort the list of clients by their name in sequence order. +.. Test case: `sortper salary rev` + Expected: Sort the list of clients by their salary in reverse order. + diff --git a/docs/team/FanDongzhe.html b/docs/team/FanDongzhe.html new file mode 100644 index 000000000000..82fbd32e8151 --- /dev/null +++ b/docs/team/FanDongzhe.html @@ -0,0 +1,693 @@ + + + + + + + +Fan Dongzhe - Project Portfolio + + + + + +
+
+

PROJECT: Marketing Morph

+
+
+
+
+
+

Overview

+
+
+

Marketing Morph is a desktop application for marketers who want to organize consumer data. And it will allow marketers to use GUI and command lines to keep track of different consumers.

+
+
+
+
+

Summary of contributions

+
+
+
    +
  • +

    Major enhancement: added the ability to sort people and export data

    +
    +
      +
    • +

      How I did it: I rewrite the java interface: comparator to implement sorting person/company by any attribute. And I use stream operation to export data.

      +
    • +
    • +

      What it does: Allow marketers to find their potential clients more convenient, for they can sort clients by their salary and find which person is the best choice. Meanwhile, he/she can export the information of all clients for further analysis, like doing data analysis to find potential clients.

      +
    • +
    • +

      Justification: This feature improves the product significantly because a user can easily find a person by sorting them. If he/she wants to find a client with the highest salary, he just need to sort all the clients by reverse order.

      +
    • +
    • +

      Highlights: Sorting person by any parameters in any order and exporting data to any path.

      +
    • +
    +
    +
  • +
  • +

    Minor enhancement: Get the max/min salary of all the clients

    +
  • +
  • +

    Code contributed
    +SortPersonCommand.java
    +PersonComparator.java
    +SortPersonCommandComparator.java
    +SortCompanyComparator.java
    +CompanyComparator.java
    +SortCompanyCommandComparator.java
    +ExportCommand.java
    +FindmaxCommand.java
    +FindminCommand.java

    +
  • +
  • +

    Test contributed
    +SortPersonCommandTest.java
    +SortPersonCommandParser.java

    +
  • +
  • +

    Other contributions:

    +
    +
      +
    • +

      Project management:

      +
      +
        +
      • +

        Worked with teammates to diagnose and fix bugs

        +
      • +
      +
      +
    • +
    • +

      Documentation:

      +
      +
        +
      • +

        Write a rudiment of the User Guide with some basic commands

        +
      • +
      • +

        Add sort command and export command to User Guide

        +
      • +
      +
      +
    • +
    • +

      Community:

      +
      +
        +
      • +

        Asked several questions in the forum

        +
      • +
      +
      +
    • +
    +
    +
  • +
+
+
+
+
+

Contributions to the User Guide

+
+ +++ + + + + + +

Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users.

+
+
    +
  • +

    Rudiment of the User Guide with some simple commands

    +
  • +
+
+
+

Sorting companies by name/revenue/occupation/residential area/group : sortcpny

+
+

Lists all companies that meet the specified filter requirement(in sequence/reverse order).

+
+
+

Format: sortper name/revenue/phone/email/occupation/address/relationship seq/rev

+
+
+

Examples:

+
+
+

sortcpny revenue seq (list all companies by revenue in sequence order)

+
+
+

sortcpny phone rev (list all companies by their phone number in reverse order)

+
+
+
+

Sorting persons by name/salary/occupation/residential area/group : sortper

+
+

Lists all people that meet the specified filter requirement(in sequence/reverse order).

+
+
+

Format: sortper name/salary/phone/email/occupation/address/relationship seq/rev

+
+
+

Examples:

+
+
+

sortper salary seq (list all persons by salary in sequence order)

+
+
+

sortper phone rev (list all persons by their phone number in reverse order)

+
+
+
+

Export data as CSV: export

+
+

Exports the data to a CSV file

+
+
+

Format: export [FILEPATH] [FILENAME]

+
+
+

Examples:

+
+
+

export /Users/fandongzhe/Desktop/fg market (type in the whole filepath)

+
+
+
+

Show maximum salary for all clients: findmax

+
+

Show maximum salary for all clients in the address book

+
+
+

Format: findmax

+
+
+
+

Show minimum salary for all clients: findmin

+
+

Show minimum salary for all clients in the address book

+
+
+

Format: findmin

+
+
+
+
+
+

Contributions to the Developer Guide

+
+ +++ + + + + + +

Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project.

+
+
+
+

Sort Person

+
+
+
    +
  1. +

    Sort clients by some attributes

    +
    +
      +
    1. +

      Prerequisites: Must exist a list of clients and each of them has the attribute to be sorted.

      +
    2. +
    3. +

      Test case: sortper name seq +Expected: Sort the list of clients by their name in sequence order.

      +
    4. +
    5. +

      Test case: sortper salary seq +Expected: Sort the list of clients by their salary in reverse order.

      +
    6. +
    7. +

      Test case: sortper name rev +Expected: Sort the list of clients by their name in sequence order.

      +
    8. +
    9. +

      Test case: sortper salary rev +Expected: Sort the list of clients by their salary in reverse order.

      +
    10. +
    +
    +
  2. +
+
+
+
+
+ + + \ No newline at end of file diff --git a/docs/team/jkhiani.adoc b/docs/team/jkhiani.adoc new file mode 100644 index 000000000000..626e385c854f --- /dev/null +++ b/docs/team/jkhiani.adoc @@ -0,0 +1,73 @@ += Jitesh Khiani - Project Portfolio +:site-section: AboutUs +:imagesDir: ../images +:stylesDir: ../stylesheets + +== PROJECT: Marketing Morph + +== Overview + +Marketing Morph is a desktop address book application used as a tool for marketers to keep track of their databases. It is capable of storing a list of consumers and companies and performing rudimentary analysis on the data. 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. + +== Summary of contributions + +Personal code contributions available at https://nus-cs2103-ay1819s2.github.io/cs2103-dashboard/#=undefined&search=jkhiani[reposense] + +* *Major enhancement*: added *the ability to favorite/unfavorite persons* +** What it does: allows the user to add people to their favorites list, view their favorites list, and remove people from favorites list. +** Justification: This feature improves the product significantly because a user no longer needs to sift through their addressbook (as it can get very large) to organize a few select people they are working with at the moment. +** Highlights: Being able to manipulate the storage for favorites was a challenge, along with figuring out how to provide suggestions if names weren't fully typed out (first + last) + +* *Minor enhancement*: added the ability to click on a person card and view their address on a Google Map. + +* *Minor enhancement*: added the ability to view the total number of clients and companies in the addressbook displayed in the status footer. + +* *Minor enhancement*: set tags to be highlighted different colors. +** Credits: Heavily relied on demo code from pull request https://github.com/se-edu/addressbook-level4/pull/798[#798] + +* *Code contributed* + +https://github.com/CS2103-AY1819S2-W16-3/main/pull/8/[PR #8 adding colors to tags], https://github.com/CS2103-AY1819S2-W16-3/main/pull/71/files[PR #71 add clients to footer], +https://github.com/CS2103-AY1819S2-W16-3/main/pull/124/[PR #124 adding companies to footer], +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/main/java/seedu/address/logic/commands/DelFavoriteCommand.java[DelFavoriteCommand.java], +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/main/java/seedu/address/logic/commands/FavoriteCommand.java[FavoriteCommand.java], +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/main/java/seedu/address/logic/commands/ListFavoritesCommand.java[ListFavoritesCommand.java], +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/main/java/seedu/address/logic/parser/FavoriteCommandParser.java[FavoriteCommandParser.java], +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/main/java/seedu/address/logic/parser/DelFavoriteCommandParser.java[DelFavoriteCommandParser.java] + +And associated code snippets within related files (such as modifying addressbookparser). + +* *Tests contributed* + +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/test/java/seedu/address/logic/parser/FavoriteCommandParserTest.java[FavoriteCommandParserTest.java], +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/test/java/seedu/address/logic/commands/FavoriteCommandTest.java[FavoriteCommandTest.java], +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/test/java/systemtests/DeleteFavoriteSystemTest.java[DeleteFavoriteSystemTest.java], +https://github.com/CS2103-AY1819S2-W16-3/main/blob/master/src/test/java/systemtests/FavoriteCommandSystemTest.java[FavoriteCommandSystemTest.java] + +* *Other contributions*: + +** Project management: +*** Managed releases `v1.1` - `v1.2` on GitHub +*** Managed issue tracker on GitHub +** Documentation: +*** Managed UG and DG for all personal code contributions (see below) +** Community: +*** PRs reviewed (with non-trivial review comments): https://github.com/CS2103-AY1819S2-W16-3/main/pull/82[#82], https://github.com/CS2103-AY1819S2-W16-3/main/pull/74[#74], https://github.com/CS2103-AY1819S2-W16-3/main/pull/41[#41], https://github.com/CS2103-AY1819S2-W16-3/main/pull/39[#39] + +== Contributions to the User Guide + + +|=== +|_Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users._ +|=== + +include::../UserGuide.adoc[tag=favorite] + +== Contributions to the Developer Guide + +|=== +|_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ +|=== + +include::../DeveloperGuide.adoc[tag=favoriteSystem] + +include::../DeveloperGuide.adoc[tag=jiteshUseCase] + +include::../DeveloperGuide.adoc[tag=jiteshAppendix] diff --git a/docs/team/jkhiani.html b/docs/team/jkhiani.html new file mode 100644 index 000000000000..670f59755b5e --- /dev/null +++ b/docs/team/jkhiani.html @@ -0,0 +1,1074 @@ + + + + + + + +Jitesh Khiani - Project Portfolio + + + + + +
+
+

PROJECT: Marketing Morph

+
+ +
+
+
+

Overview

+
+
+

Marketing Morph is a desktop address book application used as a tool for marketers to keep track of their databases. It is capable of storing a list of consumers and companies and performing rudimentary analysis on the data. 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.

+
+
+
+
+

Summary of contributions

+
+
+

Personal code contributions available at reposense

+
+
+
    +
  • +

    Major enhancement: added the ability to favorite/unfavorite persons

    +
    +
      +
    • +

      What it does: allows the user to add people to their favorites list, view their favorites list, and remove people from favorites list.

      +
    • +
    • +

      Justification: This feature improves the product significantly because a user no longer needs to sift through their addressbook (as it can get very large) to organize a few select people they are working with at the moment.

      +
    • +
    • +

      Highlights: Being able to manipulate the storage for favorites was a challenge, along with figuring out how to provide suggestions if names weren’t fully typed out (first + last)

      +
    • +
    +
    +
  • +
  • +

    Minor enhancement: added the ability to click on a person card and view their address on a Google Map.

    +
  • +
  • +

    Minor enhancement: added the ability to view the total number of clients and companies in the addressbook displayed in the status footer.

    +
  • +
  • +

    Minor enhancement: set tags to be highlighted different colors.

    +
    +
      +
    • +

      Credits: Heavily relied on demo code from pull request #798

      +
    • +
    +
    +
  • +
  • +

    Code contributed
    +PR #8 adding colors to tags, PR #71 add clients to footer, +PR #124 adding companies to footer, +DelFavoriteCommand.java, +FavoriteCommand.java, +ListFavoritesCommand.java, +FavoriteCommandParser.java, +DelFavoriteCommandParser.java
    +And associated code snippets within related files (such as modifying addressbookparser).

    +
  • +
  • +

    Tests contributed
    +FavoriteCommandParserTest.java, +FavoriteCommandTest.java, +DeleteFavoriteSystemTest.java, +FavoriteCommandSystemTest.java

    +
  • +
  • +

    Other contributions:

    +
    +
      +
    • +

      Project management:

      +
      +
        +
      • +

        Managed releases v1.1 - v1.2 on GitHub

        +
      • +
      • +

        Managed issue tracker on GitHub

        +
      • +
      +
      +
    • +
    • +

      Documentation:

      +
      +
        +
      • +

        Managed UG and DG for all personal code contributions (see below)

        +
      • +
      +
      +
    • +
    • +

      Community:

      +
      +
        +
      • +

        PRs reviewed (with non-trivial review comments): #82, #74, #41, #39

        +
      • +
      +
      +
    • +
    +
    +
  • +
+
+
+
+
+

Contributions to the User Guide

+
+ +++ + + + + + +

Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users.

+
+

Add to favorite list: favorite

+
+

Adds a person to the “favorite” list

+
+
+
+
+
    +
  • +

    The full name must be specifid

    +
  • +
  • +

    If the full name isn’t specified it displays a list of potential persons

    +
  • +
  • +

    The person must exist as a client and can only be favorited once

    +
  • +
+
+
+
+
+

Format: favorite n/NAME

+
+
+

Examples:

+
+
+
    +
  • +

    favorite John Doe

    +
  • +
  • +

    favorite John (recommends all John’s in the addressbook, adds none)

    +
  • +
+
+
+
+multipleJohns +
+
+
+
+

Delete person from favorite list: delFav

+
+

Removes a person to the “favorite” list

+
+
+
+
+
    +
  • +

    The full name must be specifid

    +
  • +
  • +

    The person must exist as a favorite

    +
  • +
+
+
+
+
+

Format: delFav n/NAME

+
+
+

Examples:

+
+
+
    +
  • +

    delFav John Doe

    +
  • +
  • +

    favorite John Doe
    +delFav John Doe

    +
  • +
+
+
+
+

List the favorite list: listFav

+
+

Lists all persons in the “favorite” list under the clients panel in alphabetical order

+
+
+

Format: listFav

+
+
+

Example:

+
+
+
    +
  • +

    favorite John Doe
    +favorite Alex Yeoh
    +favorite Roy Balakrishnan
    +favorite David Li
    +listFav

    +
  • +
+
+
+
+listFav +
+
+
+
+
+
+

Contributions to the Developer Guide

+
+ +++ + + + + + +

Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project.

+
+

Favorite System

+
+

Current Implementation

+
+

The favorite system is facilitated by AddressBook, +it extends the address book system by allowing the user to save/delete/list favorites, stored internally as a UniquePersonList. +Additionally, it implements the following operations:

+
+
+
    +
  • +

    AddressBook#addFavorites() — Saves the person into the favorites list.

    +
  • +
  • +

    AddressBook#removeFavorite() — Removes the person from the favorites list.

    +
  • +
  • +

    AddressBook#hasFavorite() — Checks if the person exists in the favorites list.

    +
  • +
  • +

    AddressBook#getFavoritesList() — Returns a list of all favorite persons.

    +
  • +
+
+
+

These operations are exposed in the Model interface as Model#addFavorites(), Model#removeFavorite(), Model#hasFavorite(), and Model#getFavoritesList().

+
+
+

Given below is an example usage scenario of how the favorite mechanism behaves at each step.

+
+
+

Step 1. The user launches the application for the first time. An empty UniquePersonList is created in AddressBook.

+
+
+

Step 2. The user executes favorite Alex to save the person Alex into their favorites list. The favorite command creates a new NameContainsKeywordsPredicate updating the filteredPersonList and calls Model#addFavorites() and Model#commitAddressBook thereby updating the favorites list stored in AddressBook and updating the filteredPersonList.

+
+
+

Step 3. The user repeats this a few times, adding a few other persons.

+
+
+

Step 4. The user wants to view all favorites and executes listFav to view all favorites. This creates a new PersonIsFavoritePredicate which is passed in a model and calls Model#getFavoritesList and check if it contains the person. The command class then calls Model#updateFilteredPersonList with the predicate to only display all filtered (favorite) persons.

+
+
+

Step 5. The user notices they accidentally added the wrong person (Alex for example) into the favorites list and executes delFav Alex Yeoh to remove them from the favorites list.

+
+
+

The following is a sequence diagram describing the flow of how the favorite command works behind the scenes.

+
+
+
+favoriteActivityDiagram +
+
+
+

Favorites are stored in addressbook.json as another entry into the existing dictionary of key-value pairs and follows identical architecture to that of adding/removing persons from the personsList.

+
+
+
+

Design Considerations

+
+
Aspect: Data structure to save the favorites
+
+
    +
  • +

    Alternative 1 (current choice): Save all favorites using a simple list.

    +
    +
      +
    • +

      Pros: Easy to implement.

      +
    • +
    • +

      Cons: May have runtime issues in deleting if the list gets large.

      +
    • +
    +
    +
  • +
  • +

    Alternative 2: Linked list to store favorites.

    +
    +
      +
    • +

      Pros: Efficient to delete favorites from anywhere in the list.

      +
    • +
    • +

      Cons: We must ensure that the implementation is proper as there is a lot of complexity.

      +
    • +
    +
    +
  • +
+
+
+
+
Aspect: Method of marking favorites
+
+
    +
  • +

    Alternative 1 (current choice): Create another entry into the dictionary within addressbook.json to store favorites over multiple sessions.

    +
    +
      +
    • +

      Pros: Easy to implement, clean seperate commands/functions related to adding/deleting from favorites (doesn’t affect addressbook contacts in any way).

      +
    • +
    • +

      Cons: A lot of data is repeated when storing favorites.

      +
    • +
    +
    +
  • +
  • +

    Alternative 2: Create an extra boolean attribute within each person to store favorite status.

    +
    +
      +
    • +

      Pros: More space effective.

      +
    • +
    • +

      Cons: We must ensure that the implementation is proper as we can accidentally affect the persons that are regular contacts, harder to visually inspect our json file to see who favorites are/aren’t.

      +
    • +
    +
    +
  • +
+
+

Use case: Favorite person

+
+

MSS

+
+
+
    +
  1. +

    User requests to list persons

    +
  2. +
  3. +

    AddressBook shows a list of persons

    +
  4. +
  5. +

    User requests to add a specific person in the list to their favorites

    +
  6. +
  7. +

    AddressBook saves the person in the favorites list

    +
    +

    Use case ends.

    +
    +
  8. +
+
+
+

Extensions

+
+
+
    +
  • +

    2a. The list is empty.

    +
    +

    Use case ends.

    +
    +
  • +
  • +

    3a. The given name does not exist.

    +
    +
      +
    • +

      3a1. AddressBook shows an error message.

      +
    • +
    +
    +
  • +
  • +

    3b. The given name is not specific enough.

    +
    +
      +
    • +

      3b1. AddressBook shows an error message and displays potential options.

      +
    • +
    +
    +
  • +
  • +

    3c. The given name is already a favorite.

    +
    +
      +
    • +

      3c1. AddressBook shows an error message.

      +
      +

      Use case resumes at step 1.

      +
      +
    • +
    +
    +
  • +
+
+

Use case: Delete favorite person

+
+

MSS

+
+
+
    +
  1. +

    User requests to list favorites

    +
  2. +
  3. +

    AddressBook shows a list of favorites persons

    +
  4. +
  5. +

    User requests to delete a specific person in the favorites list

    +
  6. +
  7. +

    AddressBook deletes the person from the favorites list

    +
    +

    Use case ends.

    +
    +
  8. +
+
+
+

Extensions

+
+
+
    +
  • +

    2a. The list is empty.

    +
    +

    Use case ends.

    +
    +
  • +
  • +

    3a. The given name does not exist.

    +
    +
      +
    • +

      3a1. AddressBook shows an error message.

      +
    • +
    +
    +
  • +
  • +

    3b. The given name is not specific enough.

    +
    +
      +
    • +

      3b1. AddressBook shows an error message and displays potential options.

      +
    • +
    +
    +
  • +
  • +

    3c. The given name isn’t a favorite.

    +
    +
      +
    • +

      3c1. AddressBook shows an error message.

      +
      +

      Use case resumes at step 1.

      +
      +
    • +
    +
    +
  • +
+
+

Use case: View person’s location

+
+

MSS

+
+
+
    +
  1. +

    User requests to list persons

    +
  2. +
  3. +

    AddressBook shows a list of persons

    +
  4. +
  5. +

    User clicks on a specific persons card on the list

    +
  6. +
  7. +

    AddressBook displays a Google Maps window of their address in the Main Window

    +
    +

    Use case ends.

    +
    +
  8. +
+
+
+

Testing Favorite

+
+
+ + + + + +
+
Note
+
+These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing. +
+
+
+
+
+
+

Adding a favorite

+
+
    +
  1. +

    Adding a favorite to the list

    +
    +
      +
    1. +

      Prerequisites: List all persons using the list command. Multiple persons in the list.

      +
    2. +
    3. +

      Test case: favorite Alex
      +Expected: Alex Yeoh not added to the favorites list. Status message requests more specificity, if there are multiple 'Alex' in our client list the cards will update to show them all.

      +
    4. +
    5. +

      Test case: favorite Alex Yeoh
      +Expected: Alex Yeoh added to the favorites list. Status message confirms addition. Timestamp in the status bar is updated.

      +
    6. +
    7. +

      Test case: favorite Alex Yeoh
      +Expected: Favorite is not added (since you just added them). Status message updated to reflect current standing. Status bar remains the same.

      +
    8. +
    9. +

      Incorrect favorite commands to try: favorite blah, favorite both cases the person is not a client and therefore cannot be added
      +Expected: Person does not exist.

      +
    10. +
    +
    +
  2. +
+
+
+
+

List all favorites

+
+
    +
  1. +

    Listing all favorites within the application

    +
    +
      +
    1. +

      Prerequisites: None (will show an empty panel if there are no favorites)

      +
    2. +
    3. +

      Test Case: listFav

      +
    4. +
    +
    +
  2. +
+
+
+
+

Deleting a favorite

+
+
    +
  1. +

    Deleting a favorite while all companies are listed

    +
    +
      +
    1. +

      Prerequisites: List all persons using the listFav command.

      +
    2. +
    3. +

      Test case: delFav Alex
      +Expected: Alex Yeoh not removed from the favorites list. Status message requests more specificity.

      +
    4. +
    5. +

      Test case: delFav Alex Yeoh
      +Expected: Alex Yeoh removed from the favorites list. Status message confirms addition. Timestamp in the status bar is updated.

      +
    6. +
    7. +

      Incorrect favorite commands to try: delFav blah, delFav both cases the person is not a client and therefore cannot be added
      +Expected: Person does not exist.

      +
    8. +
    +
    +
  2. +
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/docs/team/roanurquhart.adoc b/docs/team/roanurquhart.adoc new file mode 100644 index 000000000000..b1d100647219 --- /dev/null +++ b/docs/team/roanurquhart.adoc @@ -0,0 +1,67 @@ += Roan Urquhart - Project Portfolio +:site-section: AboutUs +:imagesDir: ../images +:stylesDir: ../stylesheets + +== PROJECT: Marketing Morph + +--- + +== Overview + +Marketing Morph is a desktop marketing application. It has a GUI but most of the user interactions happen using a CLI (Command Line Interface). It is meant to be utilized by a marketing company in order to effectively organize potential targets and potential clients. + +== Summary of contributions + +* *Major enhancement*: added *the ability to store and manipulate companies in the Addressbook* +** How I did it: I created a parallel architecture similar to the Person class and have connected it to the main application. +** What it does: allows the user to have a list of potential corporate clients, not only potential consumer targets. The user will be able to look at both the companies list and persons list in parallel. The user can analyze the connections between the consumers and the companies with functionality provided by my team member. +** Justification: This feature improves the product significantly because it increases the organization of the application as well as allowing for useful marketing and business analysis. +** Highlights: This enhancement affects existing commands and required the addition of many new classes and objects. Due to the dense OOP structure of Addressbook Level 4, I had to carefully trace the functionality of my additions throughout the infrastructure in order to implement the company class in the most effective way. The implementation wasvery challenging because it involved the navigation and creation of many new classes. I also reworked the infrastructure in order to accomodate for my changes +** Credits: The prexisting code base was very useful for guiding my implementation of the company feature. It is clear and comprehensive so I would like to thank the original contributers to this AB4. No other outside libraries were used. + +* *Minor enhancement*: helped with the implementation of the favorite function that allowed contacts to be saved into a favorites group. + +* *Code contributed*: https://nus-cs2103-ay1819s2.github.io/cs2103-dashboard/#search=roanurquhart + +* *Other contributions*: + +** Project management: +*** Worked with teammates to diagnose and fix bugs +*** Worked to maintain a reasonable timeline for feature completion +** Enhancements to existing features: +*** Enhanced existing UI features to allow for a more user-friendly display of contact data +** Documentation: +*** Extensive additions to the User Guide that document the functionality of the `Company` objects +*** Updates to the application architecture in the Developer Guide with respect to the addition of the Company structure +*** Detailed user stories added in the appendix of the Developer Guide +** Community: +*** Asked several questions in the forum + + +== Contributions to the User Guide + + +|=== +|_Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users._ +|=== + +include::../UserGuide.adoc[tag=addcpny] + +include::../UserGuide.adoc[tag=listcpny] + +include::../UserGuide.adoc[tag=editcpny] + +include::../UserGuide.adoc[tag=findcpny] + +include::../UserGuide.adoc[tag=deletecpny] + +== Contributions to the Developer Guide + +|=== +|_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ +|=== + +include::../DeveloperGuide.adoc[tag=modelc] + +include::../DeveloperGuide.adoc[tag=users] diff --git a/docs/team/roanurquhart.html b/docs/team/roanurquhart.html new file mode 100644 index 000000000000..d7cf2b79eac3 --- /dev/null +++ b/docs/team/roanurquhart.html @@ -0,0 +1,906 @@ + + + + + + + +Roan Urquhart - Project Portfolio + + + + + +
+
+

PROJECT: Marketing Morph

+
+
+
+
+
+

Overview

+
+
+

Marketing Morph is a desktop marketing application. It has a GUI but most of the user interactions happen using a CLI (Command Line Interface). It is meant to be utilized by a marketing company in order to effectively organize potential targets and potential clients.

+
+
+
+
+

Summary of contributions

+
+
+
    +
  • +

    Major enhancement: added the ability to store and manipulate companies in the Addressbook

    +
    +
      +
    • +

      How I did it: I created a parallel architecture similar to the Person class and have connected it to the main application.

      +
    • +
    • +

      What it does: allows the user to have a list of potential corporate clients, not only potential consumer targets. The user will be able to look at both the companies list and persons list in parallel. The user can analyze the connections between the consumers and the companies with functionality provided by my team member.

      +
    • +
    • +

      Justification: This feature improves the product significantly because it increases the organization of the application as well as allowing for useful marketing and business analysis.

      +
    • +
    • +

      Highlights: This enhancement affects existing commands and required the addition of many new classes and objects. Due to the dense OOP structure of Addressbook Level 4, I had to carefully trace the functionality of my additions throughout the infrastructure in order to implement the company class in the most effective way. The implementation wasvery challenging because it involved the navigation and creation of many new classes. I also reworked the infrastructure in order to accomodate for my changes

      +
    • +
    • +

      Credits: The prexisting code base was very useful for guiding my implementation of the company feature. It is clear and comprehensive so I would like to thank the original contributers to this AB4. No other outside libraries were used.

      +
    • +
    +
    +
  • +
  • +

    Minor enhancement: helped with the implementation of the favorite function that allowed contacts to be saved into a favorites group.

    +
  • +
  • +

    Code contributed: https://nus-cs2103-ay1819s2.github.io/cs2103-dashboard/#search=roanurquhart

    +
  • +
  • +

    Other contributions:

    +
    +
      +
    • +

      Project management:

      +
      +
        +
      • +

        Worked with teammates to diagnose and fix bugs

        +
      • +
      • +

        Worked to maintain a reasonable timeline for feature completion

        +
      • +
      +
      +
    • +
    • +

      Enhancements to existing features:

      +
      +
        +
      • +

        Enhanced existing UI features to allow for a more user-friendly display of contact data

        +
      • +
      +
      +
    • +
    • +

      Documentation:

      +
      +
        +
      • +

        Extensive additions to the User Guide that document the functionality of the Company objects

        +
      • +
      • +

        Updates to the application architecture in the Developer Guide with respect to the addition of the Company structure

        +
      • +
      • +

        Detailed user stories added in the appendix of the Developer Guide

        +
      • +
      +
      +
    • +
    • +

      Community:

      +
      +
        +
      • +

        Asked several questions in the forum

        +
      • +
      +
      +
    • +
    +
    +
  • +
+
+
+
+
+

Contributions to the User Guide

+
+ +++ + + + + + +

Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users.

+
+

Adding a company : addcpny

+
+

Adds a company to the client list

+
+
+

Format: addcpny n/NAME p/NUMBER e/EMAIL a/ADDRESS s/REVENUE o/SECTOR r/STRUCTURE [t/TAG]…

+
+
+

Explanation: +REVENUE represents a company’s yearly revenue. +SECTOR represents the type of company i.e. Food, Retail, Bank. +STRUCTURE distinguishes the organization of the company i.e. Conglomerate, Local, International, Private, Public, etc.

+
+
+

Examples:

+
+
+

addcpny n/Walmart p/98765432 e/walmart@gmail.com a/John street, block 123, #01-01 s/234525223 o/Retail r/Corporation

+
+
+

addcpny n/Betsy Cakes p/1234567 e/cakes@gmail.com a/Bob street, block 232 s/2242023 o/Food r/LocalBusiness t/popular t/local

+
+
+
+

Listing all companies : listcpny

+
+

Shows a list of all companies in the clients list.
+Format: listcpny

+
+
+
+

Editing a company : editcpny

+
+

Edits an existing company in the client list.
+Format: editcpny INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​

+
+
+
+
+
    +
  • +

    Edits the company at the specified INDEX. The index refers to the index number shown in the displayed company list. The index must be a positive integer 1, 2, 3, …​

    +
  • +
  • +

    At least one of the optional fields must be provided.

    +
  • +
  • +

    Existing values will be updated to the input values.

    +
  • +
  • +

    When editing tags, the existing tags of the company will be removed i.e adding of tags is not cumulative.

    +
  • +
  • +

    You can remove all the company’s tags by typing t/ without specifying any tags after it.

    +
  • +
+
+
+
+
+

Examples:

+
+
+
    +
  • +

    editcpny 1 p/91234567 e/monay@example.com
    +Edits the phone number and email address of the 1st company to be 91234567 and monay@example.com respectively.

    +
  • +
  • +

    editcpny 2 n/Small Money t/
    +Edits the name of the 2nd company to be Small Money and clears all existing tags.

    +
  • +
+
+
+
+

Locating companies by name : findcpny

+
+

Finds companies whose names contain any of the given keywords.
+Format: findcpny KEYWORD [MORE_KEYWORDS]

+
+
+
+
+
    +
  • +

    The search is case insensitive. e.g mcdonalds will match McDonalds

    +
  • +
  • +

    The order of the keywords does not matter. e.g. McDonalds Bojangles will match Bojangles McDonalds

    +
  • +
  • +

    Only the name is searched.

    +
  • +
  • +

    Only full words will be matched e.g. McDonald will not match McDonalds

    +
  • +
  • +

    Companies matching at least one keyword will be returned (i.e. OR search). e.g. McDonalds Bojangles will return McDonalds, Bojangles

    +
  • +
+
+
+
+
+

Examples:

+
+
+
    +
  • +

    find Burger
    +Returns Burger Spot and Burger King

    +
  • +
  • +

    find Burger Taco Shoes
    +Returns any person having names Burger, Taco, or Shoes

    +
  • +
+
+
+
+

Deleting a company : deletecpny

+
+

Deletes the specified Company from the address book.
+Format: deletecpny INDEX

+
+
+
+
+
    +
  • +

    Deletes the company at the specified INDEX.

    +
  • +
  • +

    The index refers to the index number shown in the displayed company list.

    +
  • +
  • +

    The index must be a positive integer 1, 2, 3, …​

    +
  • +
+
+
+
+
+

Examples:

+
+
+
    +
  • +

    list
    +delete 2
    +Deletes the 2nd company in the address book.

    +
  • +
  • +

    find Burger
    +delete 1
    +Deletes the 1st company in the results of the find command.

    +
  • +
+
+
+
+
+
+

Contributions to the Developer Guide

+
+ +++ + + + + + +

Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project.

+
+

Model component

+
+
+ModelClassDiagram +
+
Figure 1. Structure of the Model Component
+
+
+

API : Model.java

+
+
+

The Model,

+
+
+
    +
  • +

    stores a UserPref object that represents the user’s preferences.

    +
  • +
  • +

    stores the Address Book data.

    +
  • +
  • +

    exposes an unmodifiable ObservableList<Person> that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.

    +
  • +
  • +

    does not depend on any of the other three components.

    +
  • +
+
+
+

The addition of the 'Company' class has altered the 'Model Class Diagram' in several ways

+
+
+
    +
  • +

    In order to store the list of companies required for the application, a 'UniqueCompanyList' class was added

    +
  • +
  • +

    The company class was added in order to distinguish between company and person objects within the UI

    +
  • +
  • +

    Each class has the same attributes but each with a different connotations

    +
  • +
+
+
+

At the current time, with the commands 'addcpny' and 'listcpny' the user can add a company to the address book and list all of the companies added respectively

+
+
+ + + + + +
+
Note
+
+As a more OOP model, we can store a Tag list in Address Book, which Person can reference. This would allow Address Book to only require one Tag object per unique Tag, instead of each Person needing their own Tag object. An example of how such a model may look like is given below.
+
+ModelClassBetterOopDiagram +
+
+
+
+
+
+

Appendix A: User Stories

+
+
+

Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *

+
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PriorityAs a …​I want to …​So that I can…​

* * *

new user

see usage instructions

refer to instructions when I forget how to use the Marketing Morph App

* * *

user

add a new person or company

keep track of potential target demographics and clients

* * *

user

delete a person or company

remove entries that I no longer need

* * *

user

find a person or company by name

locate details of persons without having to go through the entire list

* * *

user

sort persons by name

locate a person easily

* * *

user

create a group of clients

to organize potential clients into a separate group

* * *

user

add contacts to a favorites list

to have quick access to the most used contacts

* * *

user

delete contacts to a favorites list

to have quick access to the most used contacts

* * *

user

update person or company data

to change information that is no longer correct

* *

user

can analyze which people can afford a product

to effectively target potential consumers

* *

user

can determine which people can shop at particular businesses

to sell information to companies

* *

user

view a persons address on a map

visualize where they live

* *

user

see number of entries in the addressbook

to know how many entries I have

*

user

hide private contact details by default

minimize chance of someone else seeing them by accident

+
+
+
+ + + \ No newline at end of file diff --git a/docs/team/zhangjiayu0303.adoc b/docs/team/zhangjiayu0303.adoc new file mode 100644 index 000000000000..85dd0b94b3fd --- /dev/null +++ b/docs/team/zhangjiayu0303.adoc @@ -0,0 +1,70 @@ += Jiayu Zhang - Project Portfolio +:site-section: AboutUs +:imagesDir: ../images +:stylesDir: ../stylesheets + +== PROJECT: Marketing Morph + +--- + +== Overview + +Marketing Morph is a desktop marketing application. It has a GUI but most of the user interactions happen using a CLI (Command Line Interface). It is meant to be utilized by a marketing company in order to effectively organize potential targets and potential clients. + +== Summary of contributions +* *Major enhancement*: added *the ability to store and manipulate salary, occupation and relationship features for persons in the Addressbook* +** How I did it: I extended the architecture of the Person class and have connected them to the main application. +** What it does: allows the user to record salary, occupation, and relationship attributes for persons, the user can analyze the data for the three attributes. +** Justification: This features improves the product significantly because they provide more comprehensive information of persons in address book, and allow the user to do business analysis, such as analyze salary and occupation for markets. +** Highlights: This enhancement affects existing commands and required the addition of many new classes and objects. Due to the dense OOP structure of Addressbook Level 4, I had to carefully trace the functionality of my additions throughout the infrastructure in order to implement the person class in the most effective way. +** Credits: The prexisting code base was very useful for guiding my implementation of the three additional person's attributes. It is very clear and comprehensive so I would like to thank the original contributers to this AB4. There is no other outside library applied. + +* *Minor enhancement*: +** Show the average and median salary for persons through command `averageSalary` and `medianSalary` respectively +** Show the average and median revenue for companies through command `averageRevenue` and `medianRevenue` respectively +** Find persons with exact occupation through `findOccupation` +** Find companies with exact sector through `findSector` + +* *Code contributed*: https://nus-cs2103-ay1819s2.github.io/cs2103-dashboard/#search=zhangjiayu0303 + +* *Other contributions*: + +** Project management: +*** Worked with teammates to suggest ideas and fix bugs +*** Positively discuss with teammates and arrange group meetings +** Documentation: +*** Extensive additions to the User Guide that document the functionality of the `Person` objects +*** Updates to the application architecture in the Developer Guide with respect to the addition of the Person structure +*** Added manual testing instructions in the appendix of the Developer Guide + + +== Contributions to the User Guide + + +|=== +|_Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users._ +|=== + +include::../UserGuide.adoc[tag=add] + +include::../UserGuide.adoc[tag=averageSalary] + +include::../UserGuide.adoc[tag=averageRevenue] + +include::../UserGuide.adoc[tag=medianSalary] + +include::../UserGuide.adoc[tag=medianRevenue] + +include::../UserGuide.adoc[tag=findOccupation] + +include::../UserGuide.adoc[tag=findSector] + +== Contributions to the Developer Guide + +|=== +|_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ +|=== + +include::../DeveloperGuide.adoc[tag=users] + +include::../DeveloperGuide.adoc[tag=manualJiayu] diff --git a/docs/team/zhangjiayu0303.html b/docs/team/zhangjiayu0303.html new file mode 100644 index 000000000000..7b27819a4109 --- /dev/null +++ b/docs/team/zhangjiayu0303.html @@ -0,0 +1,643 @@ + + + + + + + + + Jiayu Zhang - Project Portfolio + + + + + +
+
+

PROJECT: Marketing Morph

+
+
+
+
+
+

Overview

+
+
+

Marketing Morph is a desktop marketing application. It has a GUI but most + of the user interactions happen using a CLI (Command Line Interface). + It is meant to be utilized by a marketing company in order to effectively + organize potential targets and potential clients.

+
+
+
+
+

Summary of contributions

+
+
+
    +
  • +

    Major enhancement: added the ability to store and manipulate salary, occupation and relationship features for persons in the Addressbook

    +
    +
      +
    • +

      How I did it: I extended the architecture of the Person class + and have connected them to the main application.

      +
    • +
    • +

      What it does: allows the user to record salary, occupation, and + relationship attributes for persons, the user can analyze the + data for the three attributes.

      +
    • +
    • +

      Justification: This features improves the product significantly + because they provide more comprehensive information of persons + in address book, and allow the user to do business analysis, + such as analyze salary and occupation for markets.

      +
    • +
    • +

      Highlights: This enhancement affects existing commands and required + the addition of many new classes and objects. Due to the dense + OOP structure of Addressbook Level 4, I had to carefully trace + the functionality of my additions throughout the infrastructure + in order to implement the person class in the most effective + way.

      +
    • +
    • +

      Credits: The prexisting code base was very useful for guiding + my implementation of the three additional person’s attributes. + It is very clear and comprehensive so I would like to thank + the original contributers to this AB4. There is no other outside + library applied.

      +
    • +
    +
    +
  • +
  • +

    Minor enhancement:

    +
    +
      +
    • +

      Show the average and median salary for persons through command + averageSalary and medianSalary respectively

      +
    • +
    • +

      Show the average and median revenue for companies through command + averageRevenue and medianRevenue respectively

      +
    • +
    • +

      Find persons with exact occupation through findOccupation

      +
    • +
    • +

      Find companies with exact sector through findSector

      +
    • +
    +
    +
  • +
  • +

    Code contributed: https://nus-cs2103-ay1819s2.github.io/cs2103-dashboard/#search=zhangjiayu0303

    +
  • +
  • +

    Other contributions:

    +
    +
      +
    • +

      Project management:

      +
      +
        +
      • +

        Worked with teammates to suggest ideas and fix bugs

        +
      • +
      • +

        Positively discuss with teammates and arrange group meetings

        +
      • +
      +
      +
    • +
    • +

      Documentation:

      +
      +
        +
      • +

        Extensive additions to the User Guide that document the + functionality of the Person objects

        +
      • +
      • +

        Updates to the application architecture in the Developer + Guide with respect to the addition of the Person structure

        +
      • +
      • +

        Added manual testing instructions in the appendix of the + Developer Guide

        +
      • +
      +
      +
    • +
    +
    +
  • +
+
+
+
+
+

Contributions to the User Guide

+
+ + + + + + + + + +
+

Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users.

+
+
+

Adding a company : addcpny

+
+

Adds a company to the client list

+
+
+

Format: addcpny n/NAME p/NUMBER e/EMAIL a/ADDRESS s/REVENUE + o/SECTOR r/STRUCTURE [t/TAG]…

+
+
+

Explanation: + REVENUE represents a company’s yearly revenue. + SECTOR represents the type of company i.e. Food, Retail, + Bank. + STRUCTURE distinguishes the organization of the company + i.e. Conglomerate, Local, International, Private, Public, etc.

+
+
+

Examples:

+
+
+

addcpny n/Walmart p/98765432 e/walmart@gmail.com a/John + street, block 123, #01-01 s/234525223 o/Retail r/Corporation

+
+
+

addcpny n/Betsy Cakes p/1234567 e/cakes@gmail.com a/Bob + street, block 232 s/2242023 o/Food r/LocalBusiness t/popular t/local

+
+
+
+

Show average revenue for all companies: averageRevenue

+
+

Shows average revenue for all companies in the address book

+
+
+

Format: averageRevenue

+
+
+
+

Show median revenue for all revenue: medianRevenue

+
+

Shows median revenue for all companies in the address book

+
+
+

Format: medianRevenue

+
+
+
+

Show average salary for all persons: averageSalary

+
+

Shows average salary for all persons in the address book

+
+
+

Format: averageSalary

+
+
+
+

Show median salary for all persons: medianSalary

+
+

Shows median salary for all persons in the address book

+
+
+

Format: medianSalary

+
+
+
+

Locating persons by occupation : findOccupation

+
+
+
+
    +
  • +

    The search is case insensitive. e.g teacher will + match Teacher

    +
  • +
  • +

    Only the occupation is searched.

    +
  • +
  • +

    Only full words will be matched e.g. Tea will not + match Teacher

    +
  • +
  • +

    Persons matching the occupation will be returned

    +
  • +
+
+
+
+
+

Examples:

+
+
+
    +
  • +

    findOccupation teacher
    Returns Bob and Alice (Their occupation is teacher)

    +
  • +
+
+
+
+

Locating companies by sector : findSector

+
+
+
+
    +
  • +

    The search is case insensitive. e.g bank will match + Bank

    +
  • +
  • +

    Only the sector is searched.

    +
  • +
  • +

    Only full words will be matched e.g. Ban will not + match Bank

    +
  • +
  • +

    Companies matching the sector will be returned

    +
  • +
+
+
+
+
+

Examples:

+
+
+
    +
  • +

    findCompany bank
    Returns OCCC (Its + sector is bank)

    +
  • +
+
+
+
+
+
+

Contributions to the Developer Guide

+
+ + + + + + + + + +
+

Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project.

+
+
+
+
+

User Stories

+
+
+

Priorities: High (must have) - * * *, Medium (nice to have) + - * *, Low (unlikely to have) - *

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PriorityAs a …​I want to …​So that I can…​
+

* * *

+
+

new user

+
+

see usage instructions

+
+

refer to instructions when I forget how to use the Marketing Morph + App

+
+

* * *

+
+

user

+
+

add a new person or company

+
+

keep track of potential target demographics and clients

+
+

* * *

+
+

user

+
+

delete a person or company

+
+

remove entries that I no longer need

+
+

* * *

+
+

user

+
+

find a person or company by name

+
+

locate details of persons without having to go through the entire + list

+
+

* * *

+
+

user

+
+

sort persons by name

+
+

locate a person easily

+
+

* * *

+
+

user

+
+

sort persons by occupation

+
+

locate a cohort of persons easily

+
+

* * *

+
+

user

+
+

sort companies by sector

+
+

locate a cohort of companies easily

+
+

* * *

+
+

user

+
+

create a group of clients

+
+

to organize potential clients into a separate group

+
+

* * *

+
+

user

+
+

add contacts to a favorites list

+
+

to have quick access to the most used contacts

+
+

* * *

+
+

user

+
+

delete contacts to a favorites list

+
+

to have quick access to the most used contacts

+
+

* * *

+
+

user

+
+

update person or company data

+
+

to change information that is no longer correct

+
+

* * *

+
+

user

+
+

analyze salary for all persons

+
+

know the average and median salary for all persons

+
+

* * *

+
+

user

+
+

analyze revenue for all companies

+
+

know the average and median revenue for all companies

+
+

* *

+
+

user

+
+

can analyze which people can afford a product

+
+

to effectively target potential consumers

+
+

* *

+
+

user

+
+

can determine which people can shop at particular businesses

+
+

to sell information to companies

+
+

* *

+
+

user

+
+

view a persons address on a map

+
+

visualize where they live

+
+

* *

+
+

user

+
+

see number of entries in the addressbook

+
+

to know how many entries I have

+
+

*

+
+

user

+
+

hide private contact details by default

+
+

minimize chance of someone else seeing them by accident

+
+
+

Finding a person by occupation

+
+
    +
  1. +

    Finding a person by occupation

    +
    +
      +
    1. +

      Prerequisites: The person that is searched for must exist

      +
    2. +
    3. +

      Test Case: findOccupation Professor
      Expected: + May return Alice and Bob if both + exist as persons whose occupation is professor

      +
    4. +
    +
    +
  2. +
+
+
+
+

Finding a company by sector

+
+
    +
  1. +

    Finding a company by sector

    +
    +
      +
    1. +

      Prerequisites: The company that is searched for must exist

      +
    2. +
    3. +

      Test Case: findSector Bank
      Expected: May return + DSS and OCCC if both exist as companies + whose sector is bank

      +
    4. +
    +
    +
  2. +
+
+
+
+
+
+ + + diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java index a92d4d5d71f0..2633cbf3c6de 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/address/MainApp.java @@ -36,7 +36,9 @@ */ public class MainApp extends Application { - public static final Version VERSION = new Version(0, 6, 0, true); + + public static final Version VERSION = new Version(1, 4, 0, true); + private static final Logger logger = LogsCenter.getLogger(MainApp.class); diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java index 1deb3a1e4695..9b7a513b6ad5 100644 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ b/src/main/java/seedu/address/commons/core/Messages.java @@ -9,5 +9,7 @@ public class Messages { 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_COMPANY_DISPLAYED_INDEX = "The company index provided is invalid"; + public static final String MESSAGE_COMPANIES_LISTED_OVERVIEW = "%1$d companies listed!"; } diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java index 60369e2074e4..2eb31053cdbe 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/seedu/address/logic/Logic.java @@ -9,6 +9,7 @@ import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.person.Company; import seedu.address.model.person.Person; /** @@ -34,6 +35,11 @@ public interface Logic { /** Returns an unmodifiable view of the filtered list of persons */ ObservableList getFilteredPersonList(); + /** + * Returns an unmodifiable view of the filtered list of companies + */ + ObservableList getFilteredCompanyList(); + /** * Returns an unmodifiable view of the list of commands entered by the user. * The list is ordered from the least recent command to the most recent command. @@ -63,10 +69,21 @@ public interface Logic { */ ReadOnlyProperty selectedPersonProperty(); + /** + * Selected company in the filtered company list. + * null if no company is selected + */ + ReadOnlyProperty selectedCompanyProperty(); + /** * Sets the selected person in the filtered person list. * * @see seedu.address.model.Model#setSelectedPerson(Person) */ void setSelectedPerson(Person person); + + /** + * Sets the selected company in the filtered company list + */ + void setSelectedCompany(Company company); } diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 5cb24a617beb..e40cb379e1d8 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -15,6 +15,7 @@ import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.Model; import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.person.Company; import seedu.address.model.person.Person; import seedu.address.storage.Storage; @@ -76,6 +77,11 @@ public ObservableList getFilteredPersonList() { return model.getFilteredPersonList(); } + @Override + public ObservableList getFilteredCompanyList() { + return model.getFilteredCompanyList(); + } + @Override public ObservableList getHistory() { return history.getHistory(); @@ -101,8 +107,18 @@ public ReadOnlyProperty selectedPersonProperty() { return model.selectedPersonProperty(); } + @Override + public ReadOnlyProperty selectedCompanyProperty() { + return model.selectedCompanyProperty(); + } + @Override public void setSelectedPerson(Person person) { model.setSelectedPerson(person); } + + @Override + public void setSelectedCompany(Company company) { + + } } diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java index d88e831ff1ce..4027f6f3cf3e 100644 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddCommand.java @@ -4,7 +4,10 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_OCCUPATION; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RELATIONSHIP; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import seedu.address.logic.CommandHistory; @@ -25,12 +28,18 @@ public class AddCommand extends Command { + PREFIX_PHONE + "PHONE " + PREFIX_EMAIL + "EMAIL " + PREFIX_ADDRESS + "ADDRESS " + + PREFIX_SALARY + "SALARY" + + PREFIX_OCCUPATION + "OCCUPATION" + + PREFIX_RELATIONSHIP + "RELATIONSHIP" + "[" + PREFIX_TAG + "TAG]...\n" + "Example: " + COMMAND_WORD + " " + PREFIX_NAME + "John Doe " + PREFIX_PHONE + "98765432 " + PREFIX_EMAIL + "johnd@example.com " + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " + + PREFIX_SALARY + "100000 " + + PREFIX_OCCUPATION + "banker " + + PREFIX_RELATIONSHIP + "single " + PREFIX_TAG + "friends " + PREFIX_TAG + "owesMoney"; diff --git a/src/main/java/seedu/address/logic/commands/AddCpnyCommand.java b/src/main/java/seedu/address/logic/commands/AddCpnyCommand.java new file mode 100644 index 000000000000..7a10cb3c39fd --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddCpnyCommand.java @@ -0,0 +1,77 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_OCCUPATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RELATIONSHIP; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; + +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.person.Company; + +/** + * Adds a company object to the addressbook + */ +public class AddCpnyCommand extends Command { + public static final String COMMAND_WORD = "addcpny"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a company to the address book. " + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_PHONE + "PHONE " + + PREFIX_EMAIL + "EMAIL " + + PREFIX_ADDRESS + "ADDRESS " + + PREFIX_SALARY + "REVENUE " + + PREFIX_OCCUPATION + "SECTOR " + + PREFIX_RELATIONSHIP + "STRUCTURE " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "Money Inc " + + PREFIX_PHONE + "98765432 " + + PREFIX_EMAIL + "money@example.com " + + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " + + PREFIX_SALARY + "100000 " + + PREFIX_OCCUPATION + "bank " + + PREFIX_RELATIONSHIP + "conglomerate " + + PREFIX_TAG + "corrupt " + + PREFIX_TAG + "fraudulent "; + + public static final String MESSAGE_SUCCESS = "New company added: %1$s"; + public static final String MESSAGE_DUPLICATE_COMPANY = "This company already exists in the address book"; + + private final Company toAdd; + + /** + * Creates an AddCommand to add the specified {@code Person} + */ + public AddCpnyCommand(Company company) { + requireNonNull(company); + toAdd = company; + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (model.hasCompany(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_COMPANY); + } + + model.addCompany(toAdd); + model.commitAddressBook(); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddCommand // instanceof handles nulls + && toAdd.equals(((AddCpnyCommand) other).toAdd)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/AverageRevenueCommand.java b/src/main/java/seedu/address/logic/commands/AverageRevenueCommand.java new file mode 100644 index 000000000000..d5742fe20bfb --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AverageRevenueCommand.java @@ -0,0 +1,29 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.logic.CommandHistory; +import seedu.address.model.Model; +/** + * Calculates the average of all avenues + */ +public class AverageRevenueCommand extends Command { + + public static final String COMMAND_WORD = "averageRevenue"; + + public static final String MESSAGE_SUCCESS = "Showed the average revenue of all persons: %.2f"; + + @Override + public CommandResult execute(Model model, CommandHistory history) { + requireNonNull(model); + int sum = 0; + for (int i = 0; i < model.getFilteredCompanyList().size(); i++) { + sum += Integer.parseInt(String.valueOf(model.getFilteredCompanyList().get(i).getSalary())); + } + + return new CommandResult( + String.format(MESSAGE_SUCCESS, (double) sum / model.getFilteredCompanyList().size())); + + + } +} diff --git a/src/main/java/seedu/address/logic/commands/AverageSalaryCommand.java b/src/main/java/seedu/address/logic/commands/AverageSalaryCommand.java new file mode 100644 index 000000000000..249fd993e699 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AverageSalaryCommand.java @@ -0,0 +1,32 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.logic.CommandHistory; +import seedu.address.model.Model; +/** + * Calculates the average of all salaries + */ +public class AverageSalaryCommand extends Command { + + public static final String COMMAND_WORD = "averageSalary"; + + public static final String MESSAGE_SUCCESS = "Showed the average salary of all persons: %.2f"; + + @Override + public CommandResult execute(Model model, CommandHistory history) { + requireNonNull(model); + int sum = 0; + for (int i = 0; i < model.getFilteredPersonList().size(); i++) { + sum += Integer.parseInt(String.valueOf(model.getFilteredPersonList().get(i).getSalary())); + } + + return new CommandResult( + String.format(MESSAGE_SUCCESS, (double) sum / model.getFilteredPersonList().size())); + + + } +} + + + diff --git a/src/main/java/seedu/address/logic/commands/CompanyComparator.java b/src/main/java/seedu/address/logic/commands/CompanyComparator.java new file mode 100644 index 000000000000..bdc7aea670f7 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/CompanyComparator.java @@ -0,0 +1,103 @@ +package seedu.address.logic.commands; + +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Company; + +import java.util.Comparator; + +public class CompanyComparator { + private static Comparator CompanyName = new Comparator () { + @Override + public int compare(Company o1, Company o2) { + + return o1.getName().toString().compareTo(o2.getName().toString()); + } + }; + + private static Comparator CompanySalary = new Comparator () { + @Override + public int compare(Company o1, Company o2) { + Integer sa1 = Integer.parseInt(String.valueOf(o1.getSalary())); + Integer sa2 = Integer.parseInt(String.valueOf(o2.getSalary())); + return sa1.compareTo(sa2); + } + }; + + private static Comparator CompanyPhone = new Comparator() { + @Override + public int compare(Company o1, Company o2) { + Integer sa1 = Integer.parseInt(String.valueOf(o1.getPhone())); + Integer sa2 = Integer.parseInt(String.valueOf(o2.getPhone())); + return sa1.compareTo(sa2); + } + }; + + private static Comparator CompanyAddress = new Comparator() { + @Override + public int compare(Company o1, Company o2) { + return o1.getAddress().toString().compareTo(o2.getAddress().toString()); + } + }; + + private static Comparator CompanyEmail = new Comparator() { + @Override + public int compare(Company o1, Company o2) { + return o1.getEmail().toString().compareTo(o2.getEmail().toString()); + } + }; + + private static Comparator CompanyOccupation = new Comparator() { + @Override + public int compare(Company o1, Company o2) { + return o1.getOccupation().toString().compareTo(o2.getOccupation().toString()); + } + }; + + private static Comparator CompanyRelationship = new Comparator() { + @Override + public int compare(Company o1, Company o2) { + return o1.getRelationship().toString().compareTo(o2.getRelationship().toString()); + } + }; + + + public static Comparator GetFunction(String parameter) throws ParseException { + Comparator compare_function; + + switch (parameter.trim()) + { + case "name": + compare_function = CompanyName; + break; + + case "revenue": + compare_function = CompanySalary; + break; + + case "phone": + compare_function = CompanyPhone; + break; + + case "address": + compare_function = CompanyAddress; + break; + + case "email": + compare_function = CompanyEmail; + break; + + case "occupation": + compare_function = CompanyOccupation; + break; + + case "relationship": + compare_function = CompanyRelationship; + break; + + default: + throw new ParseException("Error"); + } + + return compare_function; + } +} diff --git a/src/main/java/seedu/address/logic/commands/CountCommand.java b/src/main/java/seedu/address/logic/commands/CountCommand.java new file mode 100644 index 000000000000..0cd1382cb1ef --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/CountCommand.java @@ -0,0 +1,24 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.logic.CommandHistory; +import seedu.address.model.Model; + +/** + * Counts how many persons are in the current filtered list + */ +public class CountCommand extends Command { + + public static final String COMMAND_WORD = "count"; + + public static final String MESSAGE_SUCCESS = "Counted the number of persons: %d"; + + + @Override + public CommandResult execute(Model model, CommandHistory history) { + requireNonNull(model); + return new CommandResult( + String.format(MESSAGE_SUCCESS, model.getFilteredPersonList().size())); + } +} diff --git a/src/main/java/seedu/address/logic/commands/DelFavoriteCommand.java b/src/main/java/seedu/address/logic/commands/DelFavoriteCommand.java new file mode 100644 index 000000000000..ebe4a8bf1918 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DelFavoriteCommand.java @@ -0,0 +1,64 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.person.NameMatchesPredicate; + +/** + * Deletes a person from the favorite list + */ +public class DelFavoriteCommand extends Command { + + public static final String COMMAND_WORD = "delFav"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes a person from the favorites list\n" + + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" + + "Example: " + COMMAND_WORD + + " Alex Yeoh"; + + public static final String MESSAGE_SUCCESS = "Person deleted"; + public static final String MESSAGE_NOT_EXIST = "This person doesn't exist"; + public static final String MESSAGE_NOT_SPECIFIC = "Please be more specific"; + public static final String MESSAGE_NOT_FAVORITE = "This person doesn't exist in the favorite list"; + + private final NameMatchesPredicate predicate; + private final NameContainsKeywordsPredicate containsPredicate; + + public DelFavoriteCommand(NameMatchesPredicate predicate, NameContainsKeywordsPredicate containsPredicate) { + this.predicate = predicate; + this.containsPredicate = containsPredicate; + } + + + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + model.updateFilteredPersonList(predicate); + + if (model.getFilteredPersonList().size() == 0) { + throw new CommandException(MESSAGE_NOT_EXIST); + } else if (model.getFilteredPersonList().size() != 1) { + model.updateFilteredPersonList(containsPredicate); + return new CommandResult(String.format(MESSAGE_NOT_SPECIFIC)); + } else if (!model.getFavoritesList().contains(model.getFilteredPersonList().get(0))) { + throw new CommandException(MESSAGE_NOT_FAVORITE); + } else { + model.removeFavorite(model.getFilteredPersonList().get(0)); + model.commitAddressBook(); + return new CommandResult(String.format(MESSAGE_SUCCESS)); + } + } + + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DelFavoriteCommand // instanceof handles nulls + && predicate.equals(((DelFavoriteCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/DeleteCpnyCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCpnyCommand.java new file mode 100644 index 000000000000..80cde6ae5382 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DeleteCpnyCommand.java @@ -0,0 +1,55 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.person.Company; + +/** + * Deletes a company identified using it's displayed index from the address book. + */ +public class DeleteCpnyCommand extends Command { + + public static final String COMMAND_WORD = "deletecpny"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the company identified by the index number used in the displayed company list.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_COMPANY_SUCCESS = "Deleted Company: %1$s"; + + private final Index targetIndexCpny; + + public DeleteCpnyCommand(Index targetIndex) { + this.targetIndexCpny = targetIndex; + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredCompanyList(); + + if (targetIndexCpny.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_COMPANY_DISPLAYED_INDEX); + } + + Company companyToDelete = lastShownList.get(targetIndexCpny.getZeroBased()); + model.deleteCompany(companyToDelete); + model.commitAddressBook(); + return new CommandResult(String.format(MESSAGE_DELETE_COMPANY_SUCCESS, companyToDelete)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteCommand // instanceof handles nulls + && targetIndexCpny.equals(((DeleteCpnyCommand) other).targetIndexCpny)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java index 952a9e7e7f2b..ecd8846070db 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/seedu/address/logic/commands/EditCommand.java @@ -4,8 +4,12 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_OCCUPATION; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RELATIONSHIP; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; + import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; import java.util.Collections; @@ -23,8 +27,11 @@ import seedu.address.model.person.Address; import seedu.address.model.person.Email; import seedu.address.model.person.Name; +import seedu.address.model.person.Occupation; import seedu.address.model.person.Person; import seedu.address.model.person.Phone; +import seedu.address.model.person.Relationship; +import seedu.address.model.person.Salary; import seedu.address.model.tag.Tag; /** @@ -42,6 +49,9 @@ public class EditCommand extends Command { + "[" + PREFIX_PHONE + "PHONE] " + "[" + PREFIX_EMAIL + "EMAIL] " + "[" + PREFIX_ADDRESS + "ADDRESS] " + + "[" + PREFIX_SALARY + "SALARY] " + + "[" + PREFIX_OCCUPATION + "OCCUPATION] " + + "[" + PREFIX_RELATIONSHIP + "RELATIONSHIP] " + "[" + PREFIX_TAG + "TAG]...\n" + "Example: " + COMMAND_WORD + " 1 " + PREFIX_PHONE + "91234567 " @@ -99,9 +109,13 @@ private static Person createEditedPerson(Person personToEdit, EditPersonDescript Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); + Salary updatedSalary = editPersonDescriptor.getSalary().orElse(personToEdit.getSalary()); + Occupation updatedOccupation = editPersonDescriptor.getOccupation().orElse(personToEdit.getOccupation()); + Relationship updateRelationship = editPersonDescriptor.getRelationship().orElse(personToEdit.getRelationship()); Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags()); - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); + return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedSalary, + updatedOccupation, updateRelationship, updatedTags); } @Override @@ -131,6 +145,9 @@ public static class EditPersonDescriptor { private Phone phone; private Email email; private Address address; + private Salary salary; + private Occupation occupation; + private Relationship relationship; private Set tags; public EditPersonDescriptor() {} @@ -144,6 +161,9 @@ public EditPersonDescriptor(EditPersonDescriptor toCopy) { setPhone(toCopy.phone); setEmail(toCopy.email); setAddress(toCopy.address); + setSalary(toCopy.salary); + setOccupation(toCopy.occupation); + setRelationship(toCopy.relationship); setTags(toCopy.tags); } @@ -186,6 +206,30 @@ public Optional
getAddress() { return Optional.ofNullable(address); } + public void setSalary(Salary salary) { + this.salary = salary; + } + + public Optional getSalary() { + return Optional.ofNullable(salary); + } + + public void setOccupation(Occupation occupation) { + this.occupation = occupation; + } + + public Optional getOccupation() { + return Optional.ofNullable(occupation); + } + + public void setRelationship(Relationship relationship) { + this.relationship = relationship; + } + + public Optional getRelationship() { + return Optional.ofNullable(relationship); + } + /** * Sets {@code tags} to this object's {@code tags}. * A defensive copy of {@code tags} is used internally. @@ -222,6 +266,9 @@ public boolean equals(Object other) { && getPhone().equals(e.getPhone()) && getEmail().equals(e.getEmail()) && getAddress().equals(e.getAddress()) + && getSalary().equals(e.getSalary()) + && getOccupation().equals(e.getOccupation()) + && getRelationship().equals(e.getRelationship()) && getTags().equals(e.getTags()); } } diff --git a/src/main/java/seedu/address/logic/commands/EditCpnyCommand.java b/src/main/java/seedu/address/logic/commands/EditCpnyCommand.java new file mode 100644 index 000000000000..5a5548f7922e --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/EditCpnyCommand.java @@ -0,0 +1,276 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_OCCUPATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RELATIONSHIP; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; + +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_COMPANIES; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.commons.util.CollectionUtil; +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.person.Address; +import seedu.address.model.person.Company; +import seedu.address.model.person.Email; +import seedu.address.model.person.Name; +import seedu.address.model.person.Occupation; +import seedu.address.model.person.Phone; +import seedu.address.model.person.Relationship; +import seedu.address.model.person.Salary; +import seedu.address.model.tag.Tag; + +/** + * Edits the details of an existing company in the address book. + */ +public class EditCpnyCommand extends Command { + + public static final String COMMAND_WORD = "editcpny"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the company identified " + + "by the index number used in the displayed company list. " + + "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_SALARY + "REVENUE] " + + "[" + PREFIX_OCCUPATION + "SECTOR] " + + "[" + PREFIX_RELATIONSHIP + "STRUCTURE] " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_PHONE + "91234567 " + + PREFIX_EMAIL + "johndoe@example.com"; + + public static final String MESSAGE_EDIT_COMPANY_SUCCESS = "Edited Company: %1$s"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_DUPLICATE_COMPANY = "This company already exists in the address book."; + + private final Index index; + private final EditCompanyDescriptor editCompanyDescriptor; + + /** + * @param index of the company in the filtered company list to edit + * @param editCompanyDescriptor details to edit the company with + */ + public EditCpnyCommand(Index index, EditCompanyDescriptor editCompanyDescriptor) { + requireNonNull(index); + requireNonNull(editCompanyDescriptor); + + this.index = index; + this.editCompanyDescriptor = new EditCompanyDescriptor(editCompanyDescriptor); + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredCompanyList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_COMPANY_DISPLAYED_INDEX); + } + + Company companyToEdit = lastShownList.get(index.getZeroBased()); + Company editedCompany = createEditedCompany(companyToEdit, editCompanyDescriptor); + + if (!companyToEdit.isSameCompany(editedCompany) && model.hasCompany(editedCompany)) { + throw new CommandException(MESSAGE_DUPLICATE_COMPANY); + } + + model.setCompany(companyToEdit, editedCompany); + model.updateFilteredCompanyList(PREDICATE_SHOW_ALL_COMPANIES); + model.commitAddressBook(); + return new CommandResult(String.format(MESSAGE_EDIT_COMPANY_SUCCESS, editedCompany)); + } + + /** + * Creates and returns a {@code Company} with the details of {@code companyToEdit} + * edited with {@code editCompanyDescriptor}. + */ + private static Company createEditedCompany(Company companyToEdit, EditCompanyDescriptor editCompanyDescriptor) { + assert companyToEdit != null; + + Name updatedName = editCompanyDescriptor.getName().orElse(companyToEdit.getName()); + Phone updatedPhone = editCompanyDescriptor.getPhone().orElse(companyToEdit.getPhone()); + Email updatedEmail = editCompanyDescriptor.getEmail().orElse(companyToEdit.getEmail()); + Address updatedAddress = editCompanyDescriptor.getAddress().orElse(companyToEdit.getAddress()); + Salary updatedSalary = editCompanyDescriptor.getSalary().orElse(companyToEdit.getSalary()); + Occupation updatedOccupation = editCompanyDescriptor.getOccupation().orElse(companyToEdit.getOccupation()); + Relationship updateRelationship = editCompanyDescriptor.getRelationship().orElse(companyToEdit + .getRelationship()); + Set updatedTags = editCompanyDescriptor.getTags().orElse(companyToEdit.getTags()); + + return new Company(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedSalary, + updatedOccupation, updateRelationship, updatedTags); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditCommand)) { + return false; + } + + // state check + EditCpnyCommand e = (EditCpnyCommand) other; + return index.equals(e.index) + && editCompanyDescriptor.equals(e.editCompanyDescriptor); + } + + /** + * Stores the details to edit the person with. Each non-empty field value will replace the + * corresponding field value of the person. + */ + public static class EditCompanyDescriptor { + private Name name; + private Phone phone; + private Email email; + private Address address; + private Salary salary; + private Occupation occupation; + private Relationship relationship; + private Set tags; + + public EditCompanyDescriptor() {} + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public EditCompanyDescriptor(EditCompanyDescriptor toCopy) { + setName(toCopy.name); + setPhone(toCopy.phone); + setEmail(toCopy.email); + setAddress(toCopy.address); + setSalary(toCopy.salary); + setOccupation(toCopy.occupation); + setRelationship(toCopy.relationship); + setTags(toCopy.tags); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); + } + + public void setName(Name name) { + this.name = name; + } + + public Optional getName() { + return Optional.ofNullable(name); + } + + public void setPhone(Phone phone) { + this.phone = phone; + } + + public Optional getPhone() { + return Optional.ofNullable(phone); + } + + public void setEmail(Email email) { + this.email = email; + } + + public Optional getEmail() { + return Optional.ofNullable(email); + } + + public void setAddress(Address address) { + this.address = address; + } + + public Optional
getAddress() { + return Optional.ofNullable(address); + } + + public void setSalary(Salary salary) { + this.salary = salary; + } + + public Optional getSalary() { + return Optional.ofNullable(salary); + } + + public void setOccupation(Occupation occupation) { + this.occupation = occupation; + } + + public Optional getOccupation() { + return Optional.ofNullable(occupation); + } + + public void setRelationship(Relationship relationship) { + this.relationship = relationship; + } + + public Optional getRelationship() { + return Optional.ofNullable(relationship); + } + + /** + * 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; + } + + /** + * 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(); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditCompanyDescriptor)) { + return false; + } + + // state check + EditCompanyDescriptor e = (EditCompanyDescriptor) other; + + return getName().equals(e.getName()) + && getPhone().equals(e.getPhone()) + && getEmail().equals(e.getEmail()) + && getAddress().equals(e.getAddress()) + && getSalary().equals(e.getSalary()) + && getOccupation().equals(e.getOccupation()) + && getRelationship().equals(e.getRelationship()) + && getTags().equals(e.getTags()); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/ExportCommand.java b/src/main/java/seedu/address/logic/commands/ExportCommand.java new file mode 100644 index 000000000000..8abfd19be357 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ExportCommand.java @@ -0,0 +1,111 @@ +package seedu.address.logic.commands; + + +import static java.util.Objects.requireNonNull; + +import javafx.collections.ObservableList; +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; + +import java.io.*; + +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import seedu.address.logic.CommandHistory; +import seedu.address.model.person.Person; + +public class ExportCommand extends Command { + + public final String file_path; + + public final String file_name; + + public static final String COMMAND_WORD = "export"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Export person informaction to csv file"; + + public static final String MESSAGE_SUCCESS = "Successful exported! "; + + public ExportCommand(String path, String name) { + + this.file_path = path; + this.file_name = name; + } + + public static String getRecord(Person P) { + + String record = P.getName() + "," + P.getPhone() + "," + P.getAddress() + "," + P.getEmail() + "," + + P.getSalary() + "," + P.getOccupation() + "," + P.getRelationship(); + return record; + } + + public static boolean exportCSV(File file, ObservableList dataList) { + + FileOutputStream out = null; + OutputStreamWriter osw = null; + BufferedWriter bw = null; + + boolean isSuccess = false; + try { + out = new FileOutputStream(file); + osw = new OutputStreamWriter(out); + bw = new BufferedWriter(new FileWriter(file)); + if (dataList != null && !dataList.isEmpty()) { + for (Person P : dataList) { + String record = getRecord(P); + bw.write(record); + bw.newLine(); + } + } + isSuccess = true; + } catch (Exception e) + { + isSuccess = false; + } finally { + if (bw != null) { + try { + bw.close(); + bw = null; + } catch (IOException e) { + e.printStackTrace(); + } + + } + if(osw != null) { + try { + osw.close(); + osw = null; + } catch (IOException e) { + e.printStackTrace(); + } + } + if(out != null) { + try { + out.close(); + out = null; + } catch (IOException e) { + e.printStackTrace(); + } + } + + } + return isSuccess; + + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + ObservableList P = model.getFilteredPersonList(); + String full_file = this.file_path+"/"+this.file_name+".csv"; + File file = new File(full_file); + boolean success = exportCSV(file,P); + if(success != true) { + throw new CommandException("Failed"); + } + return new CommandResult(MESSAGE_SUCCESS); + + } +} diff --git a/src/main/java/seedu/address/logic/commands/FavoriteCommand.java b/src/main/java/seedu/address/logic/commands/FavoriteCommand.java new file mode 100644 index 000000000000..8d861efe2bc6 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/FavoriteCommand.java @@ -0,0 +1,75 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.person.NameMatchesPredicate; + + +/** + * Finds and adds all persons in address book whose name contains any of the argument keywords to a favorites list. + * Keyword matching is case sensitive. + */ +public class FavoriteCommand extends Command { + + public static final String COMMAND_WORD = "favorite"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain\n" + + "any of the specified \"\n" + + "keywords (case-sensitive) and adds them to a list of favorite contacts.\n" + + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" + + "Example: " + COMMAND_WORD + + " John"; + + public static final String MESSAGE_SUCCESS = "New person added"; + public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the favorite list"; + public static final String MESSAGE_NOT_SPECIFIC = "Please be more specific"; + public static final String MESSAGE_NOT_EXIST = "No person exists with this name"; + + + + private final NameMatchesPredicate predicate; + private final NameContainsKeywordsPredicate containsPredicate; + + public FavoriteCommand(NameMatchesPredicate predicate, NameContainsKeywordsPredicate containsPredicate) { + requireNonNull(predicate); + requireNonNull(containsPredicate); + this.predicate = predicate; + this.containsPredicate = containsPredicate; + } + + + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + model.updateFilteredPersonList(predicate); + + if (model.getFilteredPersonList().size() == 0) { + model.updateFilteredPersonList(containsPredicate); + throw new CommandException(MESSAGE_NOT_EXIST); + } else if (model.getFavoritesList().contains(model.getFilteredPersonList().get(0))) { + throw new CommandException(MESSAGE_DUPLICATE_PERSON); + } else if (model.getFilteredPersonList().size() != 1) { + model.updateFilteredPersonList(containsPredicate); + return new CommandResult(String.format(MESSAGE_NOT_SPECIFIC)); + } else { + model.addFavorites(model.getFilteredPersonList().get(0)); + model.commitAddressBook(); + return new CommandResult(String.format(MESSAGE_SUCCESS)); + } + } + + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FavoriteCommand // instanceof handles nulls + && predicate.equals(((FavoriteCommand) other).predicate)); // state check + } + +} + diff --git a/src/main/java/seedu/address/logic/commands/FindCpnyCommand.java b/src/main/java/seedu/address/logic/commands/FindCpnyCommand.java new file mode 100644 index 000000000000..8a5d8e49de6e --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/FindCpnyCommand.java @@ -0,0 +1,44 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.CommandHistory; +import seedu.address.model.Model; +import seedu.address.model.person.NameContainsCpnyKeywordsPredicate; + +/** + * Finds and lists all companies in address book whose name contains any of the argument keywords. + * Keyword matching is case insensitive. + */ +public class FindCpnyCommand extends Command { + + public static final String COMMAND_WORD = "findcpny"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all companies whose names 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"; + + private final NameContainsCpnyKeywordsPredicate predicate; + + public FindCpnyCommand(NameContainsCpnyKeywordsPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model, CommandHistory history) { + requireNonNull(model); + model.updateFilteredCompanyList(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_COMPANIES_LISTED_OVERVIEW, model.getFilteredCompanyList().size())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FindCpnyCommand // instanceof handles nulls + && predicate.equals(((FindCpnyCommand) other).predicate)); // state check + } +} + diff --git a/src/main/java/seedu/address/logic/commands/FindOccupationCommand.java b/src/main/java/seedu/address/logic/commands/FindOccupationCommand.java new file mode 100644 index 000000000000..2a2e15591d70 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/FindOccupationCommand.java @@ -0,0 +1,41 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.CommandHistory; +import seedu.address.model.Model; +import seedu.address.model.person.OccupationContainsKeywordsPredicate; + +/** + * Finds and lists all persons in address book whose occupation contains any of the argument keywords. + * Keyword matching is case insensitive. + */ +public class FindOccupationCommand extends Command { + + public static final String COMMAND_WORD = "findOccupation"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons with exact occupation. (Case insensitive) " + + "Example: " + COMMAND_WORD + " Teacher"; + + private final OccupationContainsKeywordsPredicate predicate; + + public FindOccupationCommand(OccupationContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model, CommandHistory history) { + 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 FindOccupationCommand // instanceof handles nulls + && predicate.equals(((FindOccupationCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/FindSectorCommand.java b/src/main/java/seedu/address/logic/commands/FindSectorCommand.java new file mode 100644 index 000000000000..5baf036ddfd4 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/FindSectorCommand.java @@ -0,0 +1,41 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.CommandHistory; +import seedu.address.model.Model; +import seedu.address.model.person.SectorContainsKeywordsPredicate; + +/** + * Finds and lists all companies in address book whose sector contains any of the argument keywords. + * Keyword matching is case insensitive. + */ +public class FindSectorCommand extends Command { + + public static final String COMMAND_WORD = "findSector"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all companies with exact sector. (Case insensitive) " + + "Example: " + COMMAND_WORD + " bank"; + + private final SectorContainsKeywordsPredicate predicate; + + public FindSectorCommand(SectorContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model, CommandHistory history) { + requireNonNull(model); + model.updateFilteredCompanyList(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredCompanyList().size())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FindSectorCommand // instanceof handles nulls + && predicate.equals(((FindSectorCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/FindmaxCommand.java b/src/main/java/seedu/address/logic/commands/FindmaxCommand.java new file mode 100644 index 000000000000..e0271df35e85 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/FindmaxCommand.java @@ -0,0 +1,32 @@ +package seedu.address.logic.commands; + +import seedu.address.logic.CommandHistory; +import seedu.address.model.Model; + +import static java.util.Objects.requireNonNull; + +public class FindmaxCommand extends Command { + + public static final String COMMAND_WORD = "findmaxsa"; + + public static final String MESSAGE_SUCCESS = "The max salary of all persons: %.2f"; + + @Override + public CommandResult execute(Model model, CommandHistory history) { + requireNonNull(model); + int max = 0; + for (int i = 0; i < model.getFilteredPersonList().size(); i++) { + if (Integer.parseInt(String.valueOf(model.getFilteredPersonList().get(i).getSalary())) > max) { + max = Integer.parseInt(String.valueOf(model.getFilteredPersonList().get(i).getSalary())); + } + } + + return new CommandResult( + String.format(MESSAGE_SUCCESS, (double) max)); + + + } +} + + + diff --git a/src/main/java/seedu/address/logic/commands/FindminCommand.java b/src/main/java/seedu/address/logic/commands/FindminCommand.java new file mode 100644 index 000000000000..5eba50cb4a62 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/FindminCommand.java @@ -0,0 +1,32 @@ +package seedu.address.logic.commands; + +import seedu.address.logic.CommandHistory; +import seedu.address.model.Model; + +import static java.util.Objects.requireNonNull; + +public class FindminCommand extends Command { + + public static final String COMMAND_WORD = "findminsa"; + + public static final String MESSAGE_SUCCESS = "The min salary of all persons: %.2f"; + + @Override + public CommandResult execute(Model model, CommandHistory history) { + requireNonNull(model); + int min = 999999999; + for(int i = 0; i < model.getFilteredPersonList().size(); i++) { + if (Integer.parseInt(String.valueOf(model.getFilteredPersonList().get(i).getSalary())) < min) { + min = Integer.parseInt(String.valueOf(model.getFilteredPersonList().get(i).getSalary())); + } + } + + return new CommandResult( + String.format(MESSAGE_SUCCESS, (double) min)); + + + } +} + + + diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java index f0ef78dddded..fac24c6a28bd 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java @@ -9,6 +9,7 @@ public class HelpCommand extends Command { public static final String COMMAND_WORD = "help"; + public static final String COMMAND_ALIAS = "h"; public static final String MESSAGE_USAGE = COMMAND_WORD + ": Shows program usage instructions.\n" + "Example: " + COMMAND_WORD; diff --git a/src/main/java/seedu/address/logic/commands/ListCpnyCommand.java b/src/main/java/seedu/address/logic/commands/ListCpnyCommand.java new file mode 100644 index 000000000000..34fce877500c --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ListCpnyCommand.java @@ -0,0 +1,31 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_COMPANIES; + +import javafx.collections.ObservableList; +import seedu.address.logic.CommandHistory; +import seedu.address.model.Model; +import seedu.address.model.person.Company; + +/** + * Lists all persons in the address book to the user. + */ +public class ListCpnyCommand extends Command { + + public static final String COMMAND_WORD = "listcpny"; + + public static final String MESSAGE_SUCCESS = "Listed all companies"; + + + @Override + public CommandResult execute(Model model, CommandHistory history) { + requireNonNull(model); + model.updateFilteredCompanyList(PREDICATE_SHOW_ALL_COMPANIES); + ObservableList c = model.getFilteredCompanyList(); + for (Company comp : c) { + System.out.println(comp.toString()); + } + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ListFavoritesCommand.java b/src/main/java/seedu/address/logic/commands/ListFavoritesCommand.java new file mode 100644 index 000000000000..8688107cd959 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ListFavoritesCommand.java @@ -0,0 +1,27 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.logic.CommandHistory; +import seedu.address.model.Model; +import seedu.address.model.person.PersonIsFavoritePredicate; + +/** + * Filters all people in the person card list displaying only favorites. + */ +public class ListFavoritesCommand extends Command { + + public static final String COMMAND_WORD = "listFav"; + public static final String MESSAGE_SUCCESS = "Listed all favorites"; + + private PersonIsFavoritePredicate predicate = null; + + @Override + public CommandResult execute(Model model, CommandHistory history) { + requireNonNull(model); + predicate = new PersonIsFavoritePredicate(model); + model.updateFilteredPersonList(predicate); + + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/address/logic/commands/MedianRevenueCommand.java b/src/main/java/seedu/address/logic/commands/MedianRevenueCommand.java new file mode 100644 index 000000000000..9e6effa41b22 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/MedianRevenueCommand.java @@ -0,0 +1,29 @@ +package seedu.address.logic.commands; + +import seedu.address.logic.CommandHistory; +import seedu.address.model.Model; + +import static java.util.Objects.requireNonNull; + +public class MedianRevenueCommand extends Command{ + + public static final String COMMAND_WORD = "medianRevenue"; + + public static final String MESSAGE_SUCCESS = "Showed the median revenue of all persons: %.2f"; + + @Override + public CommandResult execute(Model model, CommandHistory history) { + requireNonNull(model); + + + int sum = 0; + for(int i = 0; i < model.getFilteredCompanyList().size(); i++) { + sum += Integer.parseInt(String.valueOf(model.getFilteredCompanyList().get(i).getSalary())); + } + + return new CommandResult( + String.format(MESSAGE_SUCCESS, (double) sum / model.getFilteredCompanyList().size())); + + + } +} diff --git a/src/main/java/seedu/address/logic/commands/MedianSalaryCommand.java b/src/main/java/seedu/address/logic/commands/MedianSalaryCommand.java new file mode 100644 index 000000000000..b71dd22729ef --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/MedianSalaryCommand.java @@ -0,0 +1,30 @@ +package seedu.address.logic.commands; + +import seedu.address.logic.CommandHistory; +import seedu.address.model.Model; + + +import static java.util.Objects.requireNonNull; + +public class MedianSalaryCommand extends Command{ + + public static final String COMMAND_WORD = "medianSalary"; + + public static final String MESSAGE_SUCCESS = "Showed the mean salary of all persons: %.2f"; + + @Override + public CommandResult execute(Model model, CommandHistory history) { + requireNonNull(model); + + + int sum = 0; + for(int i = 0; i < model.getFilteredPersonList().size(); i++) { + sum += Integer.parseInt(String.valueOf(model.getFilteredPersonList().get(i).getSalary())); + } + + return new CommandResult( + String.format(MESSAGE_SUCCESS, (double) sum / model.getFilteredPersonList().size())); + + + } +} diff --git a/src/main/java/seedu/address/logic/commands/PersonComparator.java b/src/main/java/seedu/address/logic/commands/PersonComparator.java new file mode 100644 index 000000000000..4f7bd24a3367 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/PersonComparator.java @@ -0,0 +1,109 @@ +package seedu.address.logic.commands; + +import java.util.Comparator; + +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Person; + +public class PersonComparator { + + private static Comparator PersonName = new Comparator () { + @Override + public int compare(Person o1, Person o2) { + + return o1.getName().toString().compareTo(o2.getName().toString()); + } + }; + + private static Comparator PersonSalary = new Comparator () { + @Override + public int compare(Person o1, Person o2) { + Integer sa1 = Integer.parseInt(String.valueOf(o1.getSalary())); + Integer sa2 = Integer.parseInt(String.valueOf(o2.getSalary())); + return sa1.compareTo(sa2); + } + }; + + private static Comparator PersonPhone = new Comparator() { + @Override + public int compare(Person o1, Person o2) { + Integer sa1 = Integer.parseInt(String.valueOf(o1.getPhone())); + Integer sa2 = Integer.parseInt(String.valueOf(o2.getPhone())); + return sa1.compareTo(sa2); + } + }; + + private static Comparator PersonAddress = new Comparator() { + @Override + public int compare(Person o1, Person o2) { + return o1.getAddress().toString().compareTo(o2.getAddress().toString()); + } + }; + + private static Comparator PersonEmail = new Comparator() { + @Override + public int compare(Person o1, Person o2) { + return o1.getEmail().toString().compareTo(o2.getEmail().toString()); + } + }; + + private static Comparator PersonOccupation = new Comparator() { + @Override + public int compare(Person o1, Person o2) { + return o1.getOccupation().toString().compareTo(o2.getOccupation().toString()); + } + }; + + private static Comparator PersonRelationship = new Comparator() { + @Override + public int compare(Person o1, Person o2) { + return o1.getRelationship().toString().compareTo(o2.getRelationship().toString()); + } + }; + + + public static Comparator GetFunction(String parameter) throws ParseException { + Comparator compare_function; + + switch (parameter.trim()) + { + case "name": + compare_function = PersonName; + break; + + case "salary": + compare_function = PersonSalary; + break; + + case "phone": + compare_function = PersonPhone; + break; + + case "address": + compare_function = PersonAddress; + break; + + case "email": + compare_function = PersonEmail; + break; + + case "occupation": + compare_function = PersonOccupation; + break; + + case "relationship": + compare_function = PersonRelationship; + break; + + default: + throw new ParseException("Error"); + } + + return compare_function; + } + + + + + +} diff --git a/src/main/java/seedu/address/logic/commands/SortCompanyCommand.java b/src/main/java/seedu/address/logic/commands/SortCompanyCommand.java new file mode 100644 index 000000000000..dd3024b47b7b --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/SortCompanyCommand.java @@ -0,0 +1,48 @@ +package seedu.address.logic.commands; + +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.person.Company; + +import java.util.Comparator; + +public class SortCompanyCommand extends Command{ + public final Comparator Compare; + public final String Compare_parameter; + public final String Sequence; + + public static final String COMMAND_WORD = "sortcpny"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Sort Company by specify parameters in sequence or reverse order\n" + + "Example: sortcpny name seq(for sequence order)\n" + + "Example: sortcpny name rev(for reverse order)"; + + public static final String MESSAGE_SUCCESS = "Company have been sorted! "; + + + public SortCompanyCommand(Comparator com_function, String Com_para, String Seq) { + this.Compare = com_function; + this.Compare_parameter = Com_para; + this.Sequence = Seq; + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + model.sortCompany(this.Compare, this.Sequence); + model.updateFilteredCompanyList(Model.PREDICATE_SHOW_ALL_COMPANIES); + if(!this.Sequence.equals("seq") || this.Sequence.equals("rev")) { + throw new CommandException("error"); + } + return new CommandResult(MESSAGE_SUCCESS); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof SortCompanyCommand // instanceof handles nulls + && Compare.equals(((SortCompanyCommand) other).Compare) // state check + && Compare_parameter.equals(((SortCompanyCommand) other).Compare_parameter) + && Sequence.equals(((SortCompanyCommand) other).Sequence)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/SortPersonCommand.java b/src/main/java/seedu/address/logic/commands/SortPersonCommand.java new file mode 100644 index 000000000000..2694ce3d0999 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/SortPersonCommand.java @@ -0,0 +1,55 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import javafx.collections.ObservableList; +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; + +import java.util.Collections; +import java.util.Comparator; +import seedu.address.logic.CommandHistory; +import seedu.address.model.person.Person; + +public class SortPersonCommand extends Command { + + public final Comparator Compare; + public final String Compare_parameter; + public final String Sequence; + + public static final String COMMAND_WORD = "sortper"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Sort People by specify parameters in sequence or reverse order\n" + + "Example: sortper salary seq(for sequence order)\n" + + "Example: sortper salary rev(for reverse order)"; + + public static final String MESSAGE_SUCCESS = "People have been sorted! "; + + + public SortPersonCommand(Comparator com_function, String Com_para, String Seq) { + this.Compare = com_function; + this.Compare_parameter = Com_para; + this.Sequence = Seq; + } + + @Override + public CommandResult execute(Model model,CommandHistory history) throws CommandException { + model.sortPerson(this.Compare, this.Sequence); + model.updateFilteredPersonList(Model.PREDICATE_SHOW_ALL_PERSONS); + if(!this.Sequence.equals("seq") || this.Sequence.equals("rev")) { + throw new CommandException("error"); + } + return new CommandResult(MESSAGE_SUCCESS); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof SortPersonCommand // instanceof handles nulls + && Compare.equals(((SortPersonCommand) other).Compare) // state check + && Compare_parameter.equals(((SortPersonCommand) other).Compare_parameter) + && Sequence.equals(((SortPersonCommand) other).Sequence)); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java index 3b8bfa035e83..def60739ac71 100644 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java @@ -4,7 +4,10 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_OCCUPATION; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RELATIONSHIP; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import java.util.Set; @@ -15,8 +18,11 @@ import seedu.address.model.person.Address; import seedu.address.model.person.Email; import seedu.address.model.person.Name; +import seedu.address.model.person.Occupation; import seedu.address.model.person.Person; import seedu.address.model.person.Phone; +import seedu.address.model.person.Relationship; +import seedu.address.model.person.Salary; import seedu.address.model.tag.Tag; /** @@ -31,9 +37,11 @@ public class AddCommandParser implements Parser { */ public AddCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_SALARY, PREFIX_OCCUPATION, PREFIX_RELATIONSHIP, PREFIX_TAG); - if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, + PREFIX_EMAIL, PREFIX_SALARY, PREFIX_OCCUPATION, PREFIX_RELATIONSHIP) || !argMultimap.getPreamble().isEmpty()) { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); } @@ -42,9 +50,12 @@ 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()); + Salary salary = ParserUtil.parseSalary(argMultimap.getValue(PREFIX_SALARY).get()); + Occupation occupation = ParserUtil.parseOccupation(argMultimap.getValue(PREFIX_OCCUPATION).get()); + Relationship relationship = ParserUtil.parseRelationship(argMultimap.getValue(PREFIX_RELATIONSHIP).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, salary, occupation, relationship, tagList); return new AddCommand(person); } diff --git a/src/main/java/seedu/address/logic/parser/AddCpnyCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCpnyCommandParser.java new file mode 100644 index 000000000000..fd218edf5817 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/AddCpnyCommandParser.java @@ -0,0 +1,71 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_OCCUPATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RELATIONSHIP; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Set; +import java.util.stream.Stream; + +import seedu.address.logic.commands.AddCpnyCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Address; +import seedu.address.model.person.Company; +import seedu.address.model.person.Email; +import seedu.address.model.person.Name; +import seedu.address.model.person.Occupation; +import seedu.address.model.person.Phone; +import seedu.address.model.person.Relationship; +import seedu.address.model.person.Salary; +import seedu.address.model.tag.Tag; + +/** + * Parses input arguments and creates a new AddCommand object + */ +public class AddCpnyCommandParser { + + /** + * Parses the given {@code String} of arguments in the context of the AddCommand + * and returns an AddCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddCpnyCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_SALARY, PREFIX_OCCUPATION, PREFIX_RELATIONSHIP, PREFIX_TAG); + + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, + PREFIX_EMAIL, PREFIX_SALARY, PREFIX_OCCUPATION, PREFIX_RELATIONSHIP) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCpnyCommand.MESSAGE_USAGE)); + } + + Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); + Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()); + Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()); + Salary salary = ParserUtil.parseSalary(argMultimap.getValue(PREFIX_SALARY).get()); + Occupation occupation = ParserUtil.parseOccupation(argMultimap.getValue(PREFIX_OCCUPATION).get()); + Relationship relationship = ParserUtil.parseRelationship(argMultimap.getValue(PREFIX_RELATIONSHIP).get()); + Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); + + Company company = new Company(name, phone, email, address, salary, occupation, relationship, tagList); + + return new AddCpnyCommand(company); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index b7d57f5db86a..11e077813aba 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -7,20 +7,42 @@ import java.util.regex.Pattern; import seedu.address.logic.commands.AddCommand; +import seedu.address.logic.commands.AddCpnyCommand; + +import seedu.address.logic.commands.AverageRevenueCommand; +import seedu.address.logic.commands.AverageSalaryCommand; import seedu.address.logic.commands.ClearCommand; import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CountCommand; +import seedu.address.logic.commands.DelFavoriteCommand; import seedu.address.logic.commands.DeleteCommand; +import seedu.address.logic.commands.DeleteCpnyCommand; import seedu.address.logic.commands.EditCommand; +import seedu.address.logic.commands.EditCpnyCommand; import seedu.address.logic.commands.ExitCommand; +import seedu.address.logic.commands.FavoriteCommand; import seedu.address.logic.commands.FindCommand; +import seedu.address.logic.commands.FindCpnyCommand; +import seedu.address.logic.commands.FindminCommand; +import seedu.address.logic.commands.FindmaxCommand; +import seedu.address.logic.commands.FindOccupationCommand; +import seedu.address.logic.commands.FindSectorCommand; import seedu.address.logic.commands.HelpCommand; import seedu.address.logic.commands.HistoryCommand; +import seedu.address.logic.commands.MedianSalaryCommand; +import seedu.address.logic.commands.MedianRevenueCommand; +import seedu.address.logic.commands.ListCpnyCommand; import seedu.address.logic.commands.ListCommand; +import seedu.address.logic.commands.ListFavoritesCommand; import seedu.address.logic.commands.RedoCommand; import seedu.address.logic.commands.SelectCommand; import seedu.address.logic.commands.UndoCommand; +import seedu.address.logic.commands.SortPersonCommand; +import seedu.address.logic.commands.SortCompanyCommand; +import seedu.address.logic.commands.ExportCommand; import seedu.address.logic.parser.exceptions.ParseException; + /** * Parses user input. */ @@ -54,21 +76,42 @@ public Command parseCommand(String userInput) throws ParseException { case EditCommand.COMMAND_WORD: return new EditCommandParser().parse(arguments); + case EditCpnyCommand.COMMAND_WORD: + return new EditCpnyCommandParser().parse(arguments); + case SelectCommand.COMMAND_WORD: return new SelectCommandParser().parse(arguments); case DeleteCommand.COMMAND_WORD: return new DeleteCommandParser().parse(arguments); + case DeleteCpnyCommand.COMMAND_WORD: + return new DeleteCpnyCommandParser().parse(arguments); + case ClearCommand.COMMAND_WORD: return new ClearCommand(); case FindCommand.COMMAND_WORD: return new FindCommandParser().parse(arguments); + case FindCpnyCommand.COMMAND_WORD: + return new FindCpnyCommandParser().parse(arguments); + + case SortPersonCommand.COMMAND_WORD: + return new SortPersonCommandParser().parse(arguments); + + case SortCompanyCommand.COMMAND_WORD: + return new SortCompanyCommandParser().parse(arguments); + + case ExportCommand.COMMAND_WORD: + return new ExportCommandParser().parse(arguments); + case ListCommand.COMMAND_WORD: return new ListCommand(); + case ListCpnyCommand.COMMAND_WORD: + return new ListCpnyCommand(); + case HistoryCommand.COMMAND_WORD: return new HistoryCommand(); @@ -84,6 +127,48 @@ public Command parseCommand(String userInput) throws ParseException { case RedoCommand.COMMAND_WORD: return new RedoCommand(); + case FavoriteCommand.COMMAND_WORD: + return new FavoriteCommandParser().parse(arguments); + + case DelFavoriteCommand.COMMAND_WORD: + return new DelFavoriteCommandParser().parse(arguments); + + case HelpCommand.COMMAND_ALIAS: + return new HelpCommand(); + + case ListFavoritesCommand.COMMAND_WORD: + return new ListFavoritesCommand(); + + case AddCpnyCommand.COMMAND_WORD: + return new AddCpnyCommandParser().parse(arguments); + + case CountCommand.COMMAND_WORD: + return new CountCommand(); + + case AverageSalaryCommand.COMMAND_WORD: + return new AverageSalaryCommand(); + + case AverageRevenueCommand.COMMAND_WORD: + return new AverageRevenueCommand(); + + case MedianSalaryCommand.COMMAND_WORD: + return new MedianSalaryCommand(); + + case MedianRevenueCommand.COMMAND_WORD: + return new MedianRevenueCommand(); + + case FindminCommand.COMMAND_WORD: + return new FindminCommand(); + + case FindmaxCommand.COMMAND_WORD: + return new FindmaxCommand(); + + case FindOccupationCommand.COMMAND_WORD: + return new FindOccupationCommandParser().parse(arguments); + + case FindSectorCommand.COMMAND_WORD: + return new FindSectorCommandParser().parse(arguments); + default: throw new ParseException(MESSAGE_UNKNOWN_COMMAND); } diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java index 75b1a9bf1190..aae6fb758c5e 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -10,6 +10,9 @@ 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_SALARY = new Prefix("s/"); + public static final Prefix PREFIX_OCCUPATION = new Prefix("o/"); + public static final Prefix PREFIX_RELATIONSHIP = new Prefix("r/"); public static final Prefix PREFIX_TAG = new Prefix("t/"); } diff --git a/src/main/java/seedu/address/logic/parser/DelFavoriteCommandParser.java b/src/main/java/seedu/address/logic/parser/DelFavoriteCommandParser.java new file mode 100644 index 000000000000..f8cb8339480e --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/DelFavoriteCommandParser.java @@ -0,0 +1,35 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import seedu.address.logic.commands.DelFavoriteCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.person.NameMatchesPredicate; + +/** + * Parses user input for deleting a favorite person. + */ +public class DelFavoriteCommandParser { + + /** + * Parses the given {@code String} of arguments in the context of the FindCommand + * and returns an FindCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DelFavoriteCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DelFavoriteCommand.MESSAGE_USAGE)); + } + + String[] nameKeywords = trimmedArgs.split("\\s+"); + + return new DelFavoriteCommand(new NameMatchesPredicate(Arrays.asList(nameKeywords)), + new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/DeleteCpnyCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCpnyCommandParser.java new file mode 100644 index 000000000000..e9afd4669522 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/DeleteCpnyCommandParser.java @@ -0,0 +1,29 @@ +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.DeleteCpnyCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteCommand object + */ +public class DeleteCpnyCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteCommand + * and returns an DeleteCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteCpnyCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteCpnyCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCpnyCommand.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 845644b7dea1..17de5d88f259 100644 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java @@ -5,7 +5,10 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_OCCUPATION; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RELATIONSHIP; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import java.util.Collection; @@ -32,7 +35,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_SALARY, + PREFIX_OCCUPATION, PREFIX_RELATIONSHIP, PREFIX_TAG); Index index; @@ -55,6 +59,17 @@ public EditCommand parse(String args) throws ParseException { if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); } + if (argMultimap.getValue(PREFIX_SALARY).isPresent()) { + editPersonDescriptor.setSalary(ParserUtil.parseSalary(argMultimap.getValue(PREFIX_SALARY).get())); + } + if (argMultimap.getValue(PREFIX_OCCUPATION).isPresent()) { + editPersonDescriptor.setOccupation(ParserUtil.parseOccupation(argMultimap.getValue(PREFIX_OCCUPATION) + .get())); + } + if (argMultimap.getValue(PREFIX_RELATIONSHIP).isPresent()) { + editPersonDescriptor.setRelationship(ParserUtil.parseRelationship(argMultimap.getValue(PREFIX_RELATIONSHIP) + .get())); + } parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags); if (!editPersonDescriptor.isAnyFieldEdited()) { diff --git a/src/main/java/seedu/address/logic/parser/EditCpnyCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCpnyCommandParser.java new file mode 100644 index 000000000000..a868d5b2972f --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/EditCpnyCommandParser.java @@ -0,0 +1,97 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_OCCUPATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RELATIONSHIP; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.EditCpnyCommand; +import seedu.address.logic.commands.EditCpnyCommand.EditCompanyDescriptor; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.tag.Tag; + +/** + * Parses input arguments and creates a new EditCommand object + */ +public class EditCpnyCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditCommand + * and returns an EditCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditCpnyCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_SALARY, + PREFIX_OCCUPATION, PREFIX_RELATIONSHIP, PREFIX_TAG); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCpnyCommand.MESSAGE_USAGE), pe); + } + + EditCompanyDescriptor editCompanyDescriptor = new EditCompanyDescriptor(); + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + editCompanyDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); + } + if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { + editCompanyDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); + } + if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { + editCompanyDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); + } + if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { + editCompanyDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); + } + if (argMultimap.getValue(PREFIX_SALARY).isPresent()) { + editCompanyDescriptor.setSalary(ParserUtil.parseSalary(argMultimap.getValue(PREFIX_SALARY).get())); + } + if (argMultimap.getValue(PREFIX_OCCUPATION).isPresent()) { + editCompanyDescriptor.setOccupation(ParserUtil.parseOccupation(argMultimap.getValue(PREFIX_OCCUPATION) + .get())); + } + if (argMultimap.getValue(PREFIX_RELATIONSHIP).isPresent()) { + editCompanyDescriptor.setRelationship(ParserUtil.parseRelationship(argMultimap.getValue(PREFIX_RELATIONSHIP) + .get())); + } + parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editCompanyDescriptor::setTags); + + if (!editCompanyDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditCpnyCommand.MESSAGE_NOT_EDITED); + } + + return new EditCpnyCommand(index, editCompanyDescriptor); + } + + /** + * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty. + * If {@code tags} contain only one element which is an empty string, it will be parsed into a + * {@code Set} containing zero tags. + */ + private Optional> parseTagsForEdit(Collection tags) throws ParseException { + assert tags != null; + + if (tags.isEmpty()) { + return Optional.empty(); + } + Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; + return Optional.of(ParserUtil.parseTags(tagSet)); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/ExportCommandParser.java b/src/main/java/seedu/address/logic/parser/ExportCommandParser.java new file mode 100644 index 000000000000..db2f22d8c990 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ExportCommandParser.java @@ -0,0 +1,22 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.logic.commands.ExportCommand; +import seedu.address.logic.parser.exceptions.ParseException; + + +public class ExportCommandParser implements Parser{ + + public ExportCommand parse(String args) throws ParseException { + + String trimmedArgs = args.trim(); + String[] keywords = trimmedArgs.split("\\s+"); + + + String path = keywords[0]; + String name = keywords[1]; + return new ExportCommand(path, name); + + } +} diff --git a/src/main/java/seedu/address/logic/parser/FavoriteCommandParser.java b/src/main/java/seedu/address/logic/parser/FavoriteCommandParser.java new file mode 100644 index 000000000000..860f7c81f423 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/FavoriteCommandParser.java @@ -0,0 +1,37 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import seedu.address.logic.commands.AddCommand; +import seedu.address.logic.commands.FavoriteCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.person.NameMatchesPredicate; + +/** + * Parses Favorite Command + */ +public class FavoriteCommandParser implements Parser{ + + /** + * Parses the given {@code String} of arguments in the context of the FindCommand + * and returns an FindCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FavoriteCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FavoriteCommand.MESSAGE_USAGE)); + } + + String[] nameKeywords = trimmedArgs.split("\\s+"); + + return new FavoriteCommand(new NameMatchesPredicate(Arrays.asList(nameKeywords)), + new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); + } + + +} diff --git a/src/main/java/seedu/address/logic/parser/FindCpnyCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCpnyCommandParser.java new file mode 100644 index 000000000000..58b9daddcac2 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/FindCpnyCommandParser.java @@ -0,0 +1,34 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import seedu.address.logic.commands.FindCpnyCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.NameContainsCpnyKeywordsPredicate; + +/** + * Parses input arguments and creates a new FindCommand object + */ +public class FindCpnyCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FindCommand + * and returns an FindCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FindCpnyCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCpnyCommand.MESSAGE_USAGE)); + } + + String[] nameKeywords = trimmedArgs.split("\\s+"); + + return new FindCpnyCommand(new NameContainsCpnyKeywordsPredicate(Arrays.asList(nameKeywords))); + } + +} + diff --git a/src/main/java/seedu/address/logic/parser/FindOccupationCommandParser.java b/src/main/java/seedu/address/logic/parser/FindOccupationCommandParser.java new file mode 100644 index 000000000000..4a4eca5ee2a7 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/FindOccupationCommandParser.java @@ -0,0 +1,33 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import seedu.address.logic.commands.FindOccupationCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.OccupationContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new FindCommand object + */ +public class FindOccupationCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FindCommand + * and returns an FindCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FindOccupationCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindOccupationCommand.MESSAGE_USAGE)); + } + + String[] occupationKeywords = trimmedArgs.split("\\s+"); + + return new FindOccupationCommand(new OccupationContainsKeywordsPredicate(Arrays.asList(occupationKeywords))); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/FindSectorCommandParser.java b/src/main/java/seedu/address/logic/parser/FindSectorCommandParser.java new file mode 100644 index 000000000000..61f742985c39 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/FindSectorCommandParser.java @@ -0,0 +1,33 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import seedu.address.logic.commands.FindSectorCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.SectorContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new FindCommand object + */ +public class FindSectorCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FindCommand + * and returns an FindCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FindSectorCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindSectorCommand.MESSAGE_USAGE)); + } + + String[] sectorKeywords = trimmedArgs.split("\\s+"); + + return new FindSectorCommand(new SectorContainsKeywordsPredicate(Arrays.asList(sectorKeywords))); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java index b117acb9c55b..fbe8b9655fdd 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java @@ -12,7 +12,10 @@ import seedu.address.model.person.Address; import seedu.address.model.person.Email; import seedu.address.model.person.Name; +import seedu.address.model.person.Occupation; import seedu.address.model.person.Phone; +import seedu.address.model.person.Relationship; +import seedu.address.model.person.Salary; import seedu.address.model.tag.Tag; /** @@ -95,6 +98,51 @@ public static Email parseEmail(String email) throws ParseException { return new Email(trimmedEmail); } + /** + * Parses a {@code String salary} into an {@code salary}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code salary} is invalid. + */ + public static Salary parseSalary(String salary) throws ParseException { + requireNonNull(salary); + String trimmedSalary = salary.trim(); + if (!Salary.isValidSalary(trimmedSalary)) { + throw new ParseException(Salary.MESSAGE_CONSTRAINTS); + } + return new Salary(trimmedSalary); + } + + /** + * Parses a {@code String occupation} into an {@code Occupation}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code occupation} is invalid. + */ + public static Occupation parseOccupation(String occupation) throws ParseException { + requireNonNull(occupation); + String trimmedOccupation = occupation.trim(); + if (!Occupation.isValidOccupation(trimmedOccupation)) { + throw new ParseException(Occupation.MESSAGE_CONSTRAINTS); + } + return new Occupation(trimmedOccupation); + } + + /** + * Parses a {@code String relationship} into an {@code Relationship}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code relationship} is invalid. + */ + public static Relationship parseRelationship(String relationship) throws ParseException { + requireNonNull(relationship); + String trimmedRelationship = relationship.trim(); + if (!Relationship.isValidRelationship(trimmedRelationship)) { + throw new ParseException(Relationship.MESSAGE_CONSTRAINTS); + } + return new Relationship(trimmedRelationship); + } + /** * Parses a {@code String tag} into a {@code Tag}. * Leading and trailing whitespaces will be trimmed. diff --git a/src/main/java/seedu/address/logic/parser/SortCompanyCommandParser.java b/src/main/java/seedu/address/logic/parser/SortCompanyCommandParser.java new file mode 100644 index 000000000000..658c8fce5f35 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/SortCompanyCommandParser.java @@ -0,0 +1,26 @@ +package seedu.address.logic.parser; + +import seedu.address.logic.commands.CompanyComparator; +import seedu.address.logic.commands.SortCompanyCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Company; + +import java.util.Comparator; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +public class SortCompanyCommandParser { + + public SortCompanyCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + String[] keywords = trimmedArgs.split("\\s+"); + + + try { + Comparator Compare_function = CompanyComparator.GetFunction(keywords[0]); + return new SortCompanyCommand(Compare_function, keywords[0], keywords[1]); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCompanyCommand.MESSAGE_USAGE),pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/SortPersonCommandParser.java b/src/main/java/seedu/address/logic/parser/SortPersonCommandParser.java new file mode 100644 index 000000000000..9cd804f4136a --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/SortPersonCommandParser.java @@ -0,0 +1,27 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.logic.commands.SortPersonCommand; +import seedu.address.logic.commands.PersonComparator; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Person; + +import java.util.Comparator; + +public class SortPersonCommandParser { + + public SortPersonCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + String[] keywords = trimmedArgs.split("\\s+"); + + + try { + Comparator Compare_function = PersonComparator.GetFunction(keywords[0]); + return new SortPersonCommand(Compare_function, keywords[0], keywords[1]); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortPersonCommand.MESSAGE_USAGE),pe); + } + } + +} diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index 30557cf81ee7..abcefbd497de 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -2,12 +2,15 @@ import static java.util.Objects.requireNonNull; +import java.util.Comparator; import java.util.List; import javafx.beans.InvalidationListener; import javafx.collections.ObservableList; import seedu.address.commons.util.InvalidationListenerManager; +import seedu.address.model.person.Company; import seedu.address.model.person.Person; +import seedu.address.model.person.UniqueCompanyList; import seedu.address.model.person.UniquePersonList; /** @@ -17,6 +20,8 @@ public class AddressBook implements ReadOnlyAddressBook { private final UniquePersonList persons; + private final UniquePersonList favorites; + private final UniqueCompanyList companies; private final InvalidationListenerManager invalidationListenerManager = new InvalidationListenerManager(); /* @@ -28,6 +33,8 @@ public class AddressBook implements ReadOnlyAddressBook { */ { persons = new UniquePersonList(); + favorites = new UniquePersonList(); + companies = new UniqueCompanyList(); } public AddressBook() {} @@ -51,6 +58,24 @@ public void setPersons(List persons) { indicateModified(); } + /** + * Replaces the contents of the person list with {@code persons}. + * {@code persons} must not contain duplicate persons. + */ + public void setFavorites(List persons) { + this.favorites.setPersons(persons); + indicateModified(); + } + + /** + * Replaces the contents of the person list with {@code persons}. + * {@code persons} must not contain duplicate persons. + */ + public void setCompanies(List companies) { + this.companies.setCompanies(companies); + indicateModified(); + } + /** * Resets the existing data of this {@code AddressBook} with {@code newData}. */ @@ -58,6 +83,8 @@ public void resetData(ReadOnlyAddressBook newData) { requireNonNull(newData); setPersons(newData.getPersonList()); + setFavorites(newData.getFavoritesList()); + setCompanies(newData.getCompanyList()); } //// person-level operations @@ -79,6 +106,55 @@ public void addPerson(Person p) { indicateModified(); } + /** + * Returns true if a company with the same identity as {@code company} exists in the address book. + */ + public boolean hasCompany(Company company) { + requireNonNull(company); + return companies.contains(company); + } + + /** + * Adds a company to the address book. + * The company must not already exist in the address book. + */ + public void addCompany(Company c) { + companies.add(c); + indicateModified(); + } + + /** + * Returns true if a person with the same identity as {@code person} exists in the favorite list. + */ + public boolean hasFavorite(Person person) { + requireNonNull(person); + return favorites.contains(person); + } + + /** + * Adds a person to the favorites list. + * The person must not already exist in the favorites list. + * @param p person + */ + public void addFavorites(Person p) { + favorites.add(p); + indicateModified(); + } + + /** + * Removes a person from the favorites list. + * The person must already exist in the favorites list. + * @param p person + */ + public void removeFavorite(Person p) { + favorites.remove(p); + indicateModified(); + } + + public ObservableList getFavoritesList() { + return favorites.asUnmodifiableObservableList(); + } + /** * Replaces the given person {@code target} in the list with {@code editedPerson}. * {@code target} must exist in the address book. @@ -91,6 +167,13 @@ public void setPerson(Person target, Person editedPerson) { indicateModified(); } + public void setCompany(Company target, Company editedCompany) { + requireNonNull(editedCompany); + + companies.setCompany(target, editedCompany); + indicateModified(); + } + /** * Removes {@code key} from this {@code AddressBook}. * {@code key} must exist in the address book. @@ -100,6 +183,15 @@ public void removePerson(Person key) { indicateModified(); } + /** + * Removes {@code key} from this {@code AddressBook}. + * {@code key} must exist in the address book. + */ + public void removeCompany(Company key) { + companies.remove(key); + indicateModified(); + } + @Override public void addListener(InvalidationListener listener) { invalidationListenerManager.addListener(listener); @@ -130,6 +222,11 @@ public ObservableList getPersonList() { return persons.asUnmodifiableObservableList(); } + @Override + public ObservableList getCompanyList() { + return companies.asUnmodifiableObservableList(); + } + @Override public boolean equals(Object other) { return other == this // short circuit if same object @@ -141,4 +238,15 @@ public boolean equals(Object other) { public int hashCode() { return persons.hashCode(); } + + public void sortPersons(Comparator comPer , String sequence) { + persons.SortList(comPer, sequence); + } + + public void sortCompanies(Comparator comCom , String sequence) { + companies.SortList(comCom, sequence); + } + + + } diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index e857533821b6..147dfa40c627 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -1,11 +1,13 @@ package seedu.address.model; import java.nio.file.Path; +import java.util.Comparator; import java.util.function.Predicate; import javafx.beans.property.ReadOnlyProperty; import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; +import seedu.address.model.person.Company; import seedu.address.model.person.Person; /** @@ -14,6 +16,7 @@ public interface Model { /** {@code Predicate} that always evaluate to true */ Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; + Predicate PREDICATE_SHOW_ALL_COMPANIES = unused -> true; /** * Replaces user prefs data with the data in {@code userPrefs}. @@ -58,6 +61,11 @@ public interface Model { */ boolean hasPerson(Person person); + /** + * Returns true if a company with the same identity as {@code person} exists in the address book. + */ + boolean hasCompany(Company company); + /** * Deletes the given person. * The person must exist in the address book. @@ -70,6 +78,32 @@ public interface Model { */ void addPerson(Person person); + /** + * Deletes a given company + * The person must exist in the address book. + */ + void deleteCompany(Company target); + + /** + * Adds the given company. + * {@code company} must not already exist in the address book. + */ + void addCompany(Company company); + + /** + * Adds the given person to the favorites list. + * @param person + */ + void addFavorites(Person person); + + /** + * Removes the given person from the favorites list. + * @param person + */ + void removeFavorite(Person person); + + ObservableList getFavoritesList(); + /** * Replaces the given person {@code target} with {@code editedPerson}. * {@code target} must exist in the address book. @@ -77,6 +111,13 @@ public interface Model { */ void setPerson(Person target, Person editedPerson); + /** + * Replaces a given company {@code target} with {@code editedCompany} + * The company identity of {@code editedCompany} must not be the same as another existing company + * in the address book. + */ + void setCompany(Company target, Company editedCompany); + /** Returns an unmodifiable view of the filtered person list */ ObservableList getFilteredPersonList(); @@ -86,6 +127,17 @@ public interface Model { */ void updateFilteredPersonList(Predicate predicate); + /** + * Returns an unmodifiable view of the filtered person list + */ + ObservableList getFilteredCompanyList(); + + /** + * Updates the filter of the filtered company list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredCompanyList(Predicate predicate); + /** * Returns true if the model has previous address book states to restore. */ @@ -117,14 +169,41 @@ public interface Model { */ ReadOnlyProperty selectedPersonProperty(); + /** + * Selected company in the filtered company list. + * null if no company is selected. + */ + ReadOnlyProperty selectedCompanyProperty(); + /** * Returns the selected person in the filtered person list. * null if no person is selected. */ Person getSelectedPerson(); + /** + * Returns the selected company in the filtered company list. + * null if no company is selected. + */ + Company getSelectedCompany(); + /** * Sets the selected person in the filtered person list. */ void setSelectedPerson(Person person); + + /** + * Sets the selected company in the filtered company list. + */ + void setSelectedCompany(Company company); + + /** + * Sort person by specific parameter. + */ + void sortPerson(Comparator comPerson, String sequence); + + /** + * Sort company by specific parameter. + */ + void sortCompany(Comparator comCompany, String sequence); } diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index b56806232814..15929f4d4d42 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -4,6 +4,7 @@ import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; import java.nio.file.Path; +import java.util.Comparator; import java.util.Objects; import java.util.function.Predicate; import java.util.logging.Logger; @@ -15,6 +16,7 @@ import javafx.collections.transformation.FilteredList; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; +import seedu.address.model.person.Company; import seedu.address.model.person.Person; import seedu.address.model.person.exceptions.PersonNotFoundException; @@ -27,7 +29,10 @@ public class ModelManager implements Model { private final VersionedAddressBook versionedAddressBook; private final UserPrefs userPrefs; private final FilteredList filteredPersons; + private final FilteredList filteredCompanies; + private final FilteredList filteredFavorites; private final SimpleObjectProperty selectedPerson = new SimpleObjectProperty<>(); + private final SimpleObjectProperty selectedCompany = new SimpleObjectProperty<>(); /** * Initializes a ModelManager with the given addressBook and userPrefs. @@ -42,6 +47,9 @@ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs this.userPrefs = new UserPrefs(userPrefs); filteredPersons = new FilteredList<>(versionedAddressBook.getPersonList()); filteredPersons.addListener(this::ensureSelectedPersonIsValid); + filteredCompanies = new FilteredList<>(versionedAddressBook.getCompanyList()); + filteredFavorites = new FilteredList<>(versionedAddressBook.getFavoritesList()); + filteredFavorites.addListener(this::ensureSelectedPersonIsValid); } public ModelManager() { @@ -112,6 +120,89 @@ public void addPerson(Person person) { updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); } + @Override + public boolean hasCompany(Company company) { + requireNonNull(company); + return versionedAddressBook.hasCompany(company); + } + + @Override + public void addCompany(Company company) { + versionedAddressBook.addCompany(company); + updateFilteredCompanyList(PREDICATE_SHOW_ALL_COMPANIES); + } + + @Override + public ReadOnlyProperty selectedCompanyProperty() { + return selectedCompany; + } + + @Override + public Company getSelectedCompany() { + return selectedCompany.getValue(); + } + + @Override + public void setSelectedCompany(Company company) { + if (company != null && !filteredCompanies.contains(company)) { + throw new PersonNotFoundException(); + } + selectedCompany.setValue(company); + } + + /** + * Ensures {@code selectedCompany} is a valid company in {@code filteredCompanies}. + */ + private void ensureSelectedCompanyIsValid(ListChangeListener.Change change) { + while (change.next()) { + if (selectedCompany.getValue() == null) { + // null is always a valid selected company, so we do not need to check that it is valid anymore. + return; + } + + boolean wasSelectedCompanyReplaced = change.wasReplaced() && change.getAddedSize() == change + .getRemovedSize() + && change.getRemoved().contains(selectedCompany.getValue()); + if (wasSelectedCompanyReplaced) { + // Update selectedCompany to its new value. + int index = change.getRemoved().indexOf(selectedCompany.getValue()); + selectedCompany.setValue(change.getAddedSubList().get(index)); + continue; + } + + boolean wasSelectedCompanyRemoved = change.getRemoved().stream() + .anyMatch(removedCompany -> selectedCompany.getValue().isSameCompany(removedCompany)); + if (wasSelectedCompanyRemoved) { + // Select the company that came before it in the list, + // or clear the selection if there is no such company. + selectedCompany.setValue(change.getFrom() > 0 ? change.getList().get(change.getFrom() - 1) : null); + } + } + } + + @Override + public void deleteCompany(Company target) { + versionedAddressBook.removeCompany(target); + } + + @Override + public void addFavorites(Person person) { + versionedAddressBook.addFavorites(person); + updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + } + + @Override + public void removeFavorite(Person person) { + versionedAddressBook.removeFavorite(person); + updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + } + + @Override + public ObservableList getFavoritesList() { + return versionedAddressBook.getFavoritesList(); + } + + @Override public void setPerson(Person target, Person editedPerson) { requireAllNonNull(target, editedPerson); @@ -119,6 +210,13 @@ public void setPerson(Person target, Person editedPerson) { versionedAddressBook.setPerson(target, editedPerson); } + @Override + public void setCompany(Company target, Company editedCompany) { + requireAllNonNull(target, editedCompany); + + versionedAddressBook.setCompany(target, editedCompany); + } + //=========== Filtered Person List Accessors ============================================================= /** @@ -130,12 +228,24 @@ public ObservableList getFilteredPersonList() { return filteredPersons; } + @Override public void updateFilteredPersonList(Predicate predicate) { requireNonNull(predicate); filteredPersons.setPredicate(predicate); } + @Override + public ObservableList getFilteredCompanyList() { + return filteredCompanies; + } + + @Override + public void updateFilteredCompanyList(Predicate predicate) { + requireNonNull(predicate); + filteredCompanies.setPredicate(predicate); + } + //=========== Undo/Redo ================================================================================= @Override @@ -232,4 +342,20 @@ public boolean equals(Object obj) { && Objects.equals(selectedPerson.get(), other.selectedPerson.get()); } + @Override + + public void sortPerson(Comparator comPerson , String sequence) { + requireNonNull(comPerson); + versionedAddressBook.sortPersons(comPerson, sequence); + } + + @Override + + public void sortCompany(Comparator comCompany, String sequence) { + requireNonNull(comCompany); + versionedAddressBook.sortCompanies(comCompany, sequence); + + } + + } diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java index 6a301434b33b..69fdf6221501 100644 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java @@ -2,6 +2,7 @@ import javafx.beans.Observable; import javafx.collections.ObservableList; +import seedu.address.model.person.Company; import seedu.address.model.person.Person; /** @@ -14,5 +15,7 @@ public interface ReadOnlyAddressBook extends Observable { * This list will not contain any duplicate persons. */ ObservableList getPersonList(); + ObservableList getCompanyList(); + ObservableList getFavoritesList(); } diff --git a/src/main/java/seedu/address/model/person/Company.java b/src/main/java/seedu/address/model/person/Company.java new file mode 100644 index 000000000000..8553669d46fe --- /dev/null +++ b/src/main/java/seedu/address/model/person/Company.java @@ -0,0 +1,166 @@ +package seedu.address.model.person; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import seedu.address.model.tag.Tag; + +/** + *Represents a Company in the address book. + *Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Company { + + // Identity fields + private final Name name; + private final Phone phone; + private final Email email; + private final Salary salary; + private final Occupation occupation; + private final Relationship relationship; + + // Data fields + private final Address address; + private final Set tags = new HashSet<>(); + + /** + * Every field must be present and not null. + */ + public Company(Name name, Phone phone, Email email, Address address, + Salary salary, Occupation occupation, Relationship relationship, Set tags) { + requireAllNonNull(name, phone, email, address, salary, occupation, relationship, tags); + this.name = name; + this.phone = phone; + this.email = email; + this.salary = salary; + this.occupation = occupation; + this.relationship = relationship; + this.address = address; + this.tags.addAll(tags); + } + + public Company(Name name, Phone phone, Email email, Address address, Set tags) { + requireAllNonNull(name, phone, email, address, tags); + this.name = name; + this.phone = phone; + this.email = email; + this.address = address; + this.tags.addAll(tags); + this.salary = new Salary("1"); + this.occupation = new Occupation(""); + this.relationship = new Relationship(""); + } + + public Name getName() { + return name; + } + + public Phone getPhone() { + return phone; + } + + public Email getEmail() { + return email; + } + + public Address getAddress() { + return address; + } + + public Salary getSalary() { + return salary; + } + + public Occupation getOccupation() { + return occupation; + } + + public Relationship getRelationship() { + return relationship; + } + + /** + * Returns an immutable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public Set getTags() { + return Collections.unmodifiableSet(tags); + } + + /** + * Returns true if both companies of the same name have at least one other identity field that is the same. + * This defines a weaker notion of equality between two companies. + */ + public boolean isSameCompany(Company otherCompany) { + + if (otherCompany == this) { + return true; + } + + return otherCompany != null + && otherCompany.getName().equals(getName()) + && (otherCompany.getPhone().equals(getPhone()) + || otherCompany.getEmail().equals(getEmail()) + || otherCompany.getSalary().equals(getSalary()) + || otherCompany.getOccupation().equals(getOccupation()) + || otherCompany.getRelationship().equals(getRelationship())); + } + + /** + * Returns true if both companies have the same identity and data fields. + * This defines a stronger notion of equality between two companiess. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Company)) { + return false; + } + + Company otherCompany = (Company) other; + return otherCompany.getName().equals(getName()) + && otherCompany.getPhone().equals(getPhone()) + && otherCompany.getEmail().equals(getEmail()) + && otherCompany.getAddress().equals(getAddress()) + && otherCompany.getSalary().equals(getSalary()) + && otherCompany.getOccupation().equals(getOccupation()) + && otherCompany.getRelationship().equals(getRelationship()) + && otherCompany.getTags().equals(getTags()); + + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(name, phone, email, address, salary, occupation, relationship, tags); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getName()) + .append(" Phone: ") + .append(getPhone()) + .append(" Email: ") + .append(getEmail()) + .append(" Address: ") + .append(getAddress()) + .append(" Salary: ") + .append(getSalary()) + .append(" Occupation: ") + .append(getOccupation()) + .append(" Relationship: ") + .append(getRelationship()) + .append(" Tags: "); + getTags().forEach(builder::append); + return builder.toString(); + } + +} diff --git a/src/main/java/seedu/address/model/person/NameContainsCpnyKeywordsPredicate.java b/src/main/java/seedu/address/model/person/NameContainsCpnyKeywordsPredicate.java new file mode 100644 index 000000000000..5d0c1e46732a --- /dev/null +++ b/src/main/java/seedu/address/model/person/NameContainsCpnyKeywordsPredicate.java @@ -0,0 +1,33 @@ +package seedu.address.model.person; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; + +/** + * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. + */ +public class NameContainsCpnyKeywordsPredicate implements Predicate { + private final List keywords; + + public NameContainsCpnyKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Company company) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(company.getName().fullName, keyword)); + } + + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof NameContainsCpnyKeywordsPredicate // instanceof handles nulls + && keywords.equals(((NameContainsCpnyKeywordsPredicate) other).keywords)); // state check + } + +} + diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java index c9b5868427ca..2660ec117e57 100644 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ b/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java @@ -21,6 +21,7 @@ public boolean test(Person person) { .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword)); } + @Override public boolean equals(Object other) { return other == this // short circuit if same object diff --git a/src/main/java/seedu/address/model/person/NameMatchesPredicate.java b/src/main/java/seedu/address/model/person/NameMatchesPredicate.java new file mode 100644 index 000000000000..48bdb0f8f34a --- /dev/null +++ b/src/main/java/seedu/address/model/person/NameMatchesPredicate.java @@ -0,0 +1,29 @@ +package seedu.address.model.person; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Tests that a {@code Person}'s {@code Name} matches the exact name passed in. + */ + +public class NameMatchesPredicate implements Predicate { + + private final String name; + + public NameMatchesPredicate(List keywords) { + if (keywords.size() == 0) { + name = ""; + } else if (keywords.size() == 1) { + name = keywords.get(0); + } else { + name = keywords.get(0) + " " + keywords.get(1); + } + } + + @Override + public boolean test(Person person) { + return name.equals(person.getName().fullName); + } + +} diff --git a/src/main/java/seedu/address/model/person/Occupation.java b/src/main/java/seedu/address/model/person/Occupation.java new file mode 100644 index 000000000000..8a9fe0f21900 --- /dev/null +++ b/src/main/java/seedu/address/model/person/Occupation.java @@ -0,0 +1,59 @@ +package seedu.address.model.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a Person's occupation in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidOccupation(String)} + */ +public class Occupation { + + public static final String MESSAGE_CONSTRAINTS = + "Occupations should only contain alphanumeric characters, and it should not be blank"; + + /* + * The first character of the occupation must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "[^\\s].*"; + + public final String value; + + /** + * Constructs a {@code Occupation}. + * + * @param occupation A valid occupation. + */ + public Occupation(String occupation) { + requireNonNull(occupation); + checkArgument(isValidOccupation(occupation), MESSAGE_CONSTRAINTS); + value = occupation; + } + + /** + * Returns true if a given string is a valid occupation. + */ + public static boolean isValidOccupation(String test) { + return test.matches(VALIDATION_REGEX); + } + + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Occupation // instanceof handles nulls + && value.equals(((Occupation) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/person/OccupationContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/OccupationContainsKeywordsPredicate.java new file mode 100644 index 000000000000..bdc0e7e65bb3 --- /dev/null +++ b/src/main/java/seedu/address/model/person/OccupationContainsKeywordsPredicate.java @@ -0,0 +1,32 @@ +package seedu.address.model.person; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; + +/** + * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. + */ +public class OccupationContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public OccupationContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Person person) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getOccupation().value, keyword)); + } + + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof OccupationContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((OccupationContainsKeywordsPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java index 557a7a60cd51..ba2231b46999 100644 --- a/src/main/java/seedu/address/model/person/Person.java +++ b/src/main/java/seedu/address/model/person/Person.java @@ -19,6 +19,9 @@ public class Person { private final Name name; private final Phone phone; private final Email email; + private final Salary salary; + private final Occupation occupation; + private final Relationship relationship; // Data fields private final Address address; @@ -27,6 +30,19 @@ public class Person { /** * Every field must be present and not null. */ + public Person(Name name, Phone phone, Email email, Address address, + Salary salary, Occupation occupation, Relationship relationship, Set tags) { + requireAllNonNull(name, phone, email, address, salary, occupation, relationship, tags); + this.name = name; + this.phone = phone; + this.email = email; + this.salary = salary; + this.occupation = occupation; + this.relationship = relationship; + this.address = address; + this.tags.addAll(tags); + } + public Person(Name name, Phone phone, Email email, Address address, Set tags) { requireAllNonNull(name, phone, email, address, tags); this.name = name; @@ -34,6 +50,9 @@ public Person(Name name, Phone phone, Email email, Address address, Set tag this.email = email; this.address = address; this.tags.addAll(tags); + this.salary = new Salary("1"); + this.occupation = new Occupation(""); + this.relationship = new Relationship(""); } public Name getName() { @@ -52,6 +71,18 @@ public Address getAddress() { return address; } + public Salary getSalary() { + return salary; + } + + public Occupation getOccupation() { + return occupation; + } + + public Relationship getRelationship() { + return relationship; + } + /** * Returns an immutable tag set, which throws {@code UnsupportedOperationException} * if modification is attempted. @@ -71,7 +102,11 @@ public boolean isSamePerson(Person otherPerson) { return otherPerson != null && otherPerson.getName().equals(getName()) - && (otherPerson.getPhone().equals(getPhone()) || otherPerson.getEmail().equals(getEmail())); + && (otherPerson.getPhone().equals(getPhone()) + && otherPerson.getEmail().equals(getEmail()) + && otherPerson.getSalary().equals(getSalary()) + && otherPerson.getOccupation().equals(getOccupation()) + && otherPerson.getRelationship().equals(getRelationship())); } /** @@ -93,13 +128,17 @@ public boolean equals(Object other) { && otherPerson.getPhone().equals(getPhone()) && otherPerson.getEmail().equals(getEmail()) && otherPerson.getAddress().equals(getAddress()) + && otherPerson.getSalary().equals(getSalary()) + && otherPerson.getOccupation().equals(getOccupation()) + && otherPerson.getRelationship().equals(getRelationship()) && otherPerson.getTags().equals(getTags()); + } @Override public int hashCode() { // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); + return Objects.hash(name, phone, email, address, salary, occupation, relationship, tags); } @Override @@ -112,6 +151,12 @@ public String toString() { .append(getEmail()) .append(" Address: ") .append(getAddress()) + .append(" Salary: ") + .append(getSalary()) + .append(" Occupation: ") + .append(getOccupation()) + .append(" Relationship: ") + .append(getRelationship()) .append(" Tags: "); getTags().forEach(builder::append); return builder.toString(); diff --git a/src/main/java/seedu/address/model/person/PersonIsFavoritePredicate.java b/src/main/java/seedu/address/model/person/PersonIsFavoritePredicate.java new file mode 100644 index 000000000000..d979f5bf12f6 --- /dev/null +++ b/src/main/java/seedu/address/model/person/PersonIsFavoritePredicate.java @@ -0,0 +1,22 @@ +package seedu.address.model.person; + +import java.util.function.Predicate; + +import seedu.address.model.Model; + +/** + * Tests that a {@code Person}'s {@code Name} exists in the favorites list. + */ +public class PersonIsFavoritePredicate implements Predicate { + + private Model model; + + public PersonIsFavoritePredicate(Model model) { + this.model = model; + } + + @Override + public boolean test(Person person) { + return model.getFavoritesList().contains(person); + } +} diff --git a/src/main/java/seedu/address/model/person/Relationship.java b/src/main/java/seedu/address/model/person/Relationship.java new file mode 100644 index 000000000000..cc940dc7dac1 --- /dev/null +++ b/src/main/java/seedu/address/model/person/Relationship.java @@ -0,0 +1,59 @@ +package seedu.address.model.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a Person's relationship status in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidRelationship(String)} + */ +public class Relationship { + + public static final String MESSAGE_CONSTRAINTS = + "Relationships should only contain alphanumeric characters, and it should not be blank"; + + /* + * The first character of the relationship must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "\\p{Alnum}+"; + + public final String value; + + /** + * Constructs a {@code Relationship}. + * + * @param relationship A valid relationship. + */ + public Relationship(String relationship) { + requireNonNull(relationship); + checkArgument(isValidRelationship(relationship), MESSAGE_CONSTRAINTS); + value = relationship; + } + + /** + * Returns true if a given string is a valid relationship. + */ + public static boolean isValidRelationship(String test) { + return test.matches(VALIDATION_REGEX); + } + + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Relationship // instanceof handles nulls + && value.equals(((Relationship) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/person/Salary.java b/src/main/java/seedu/address/model/person/Salary.java new file mode 100644 index 000000000000..f5b1b134ef7b --- /dev/null +++ b/src/main/java/seedu/address/model/person/Salary.java @@ -0,0 +1,53 @@ +package seedu.address.model.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a Person's salary in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidSalary(String)} + */ +public class Salary { + + + public static final String MESSAGE_CONSTRAINTS = + "Salary should only contain numbers, and it should be at least 1 digits long"; + public static final String VALIDATION_REGEX = "\\d{1,}"; + public final String value; + + /** + * Constructs a {@code Salary}. + * + * @param salary A valid salary number. + */ + public Salary(String salary) { + requireNonNull(salary); + checkArgument(isValidSalary(salary), MESSAGE_CONSTRAINTS); + value = salary; + } + + /** + * Returns true if a given string is a valid salary number. + */ + public static boolean isValidSalary(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Salary // instanceof handles nulls + && value.equals(((Salary) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/person/SectorContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/SectorContainsKeywordsPredicate.java new file mode 100644 index 000000000000..91ff3982df3d --- /dev/null +++ b/src/main/java/seedu/address/model/person/SectorContainsKeywordsPredicate.java @@ -0,0 +1,32 @@ +package seedu.address.model.person; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; + +/** + * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. + */ +public class SectorContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public SectorContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Company company) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(company.getOccupation().value, keyword)); + } + + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof SectorContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((SectorContainsKeywordsPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/seedu/address/model/person/UniqueCompanyList.java b/src/main/java/seedu/address/model/person/UniqueCompanyList.java new file mode 100644 index 000000000000..dd7894895925 --- /dev/null +++ b/src/main/java/seedu/address/model/person/UniqueCompanyList.java @@ -0,0 +1,152 @@ +package seedu.address.model.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.address.model.person.exceptions.DuplicatePersonException; +import seedu.address.model.person.exceptions.PersonNotFoundException; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * A list of persons that enforces uniqueness between its elements and does not allow nulls. + * A person is considered unique by comparing using {@code Person#isSameCompany(Company)}. + * As such, adding and updating of + * persons uses Company#isSameCompany(Company) for equality so as to ensure that + * the person being added or updated is + * unique in terms of identity in the UniqueCompanyList. However, the removal of a + * person uses Company#equals(Object) so + * as to ensure that the person with exactly the same fields will be removed. + * + * Supports a minimal set of list operations. + * + * @see Company#isSameCompany(Company) + */ +public class UniqueCompanyList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent person as the given argument. + */ + public boolean contains(Company toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameCompany); + } + + /** + * Adds a person to the list. + * The person must not already exist in the list. + */ + public void add(Company toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicatePersonException(); + } + internalList.add(toAdd); + } + + /** + * Replaces the person {@code target} in the list with {@code editedPerson}. + * {@code target} must exist in the list. + * The person identity of {@code editedPerson} must not be the same as another existing person in the list. + */ + public void setCompany(Company target, Company editedCompany) { + requireAllNonNull(target, editedCompany); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new PersonNotFoundException(); + } + + if (!target.isSameCompany(editedCompany) && contains(editedCompany)) { + throw new DuplicatePersonException(); + } + + internalList.set(index, editedCompany); + } + + /** + * Removes the equivalent person from the list. + * The person must exist in the list. + */ + public void remove(Company toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new PersonNotFoundException(); + } + } + + public void setCompanies(UniqueCompanyList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code persons}. + * {@code persons} must not contain duplicate persons. + */ + public void setCompanies(List companies) { + requireAllNonNull(companies); + if (!companiesAreUnique(companies)) { + throw new DuplicatePersonException(); + } + + internalList.setAll(companies); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return internalUnmodifiableList; + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueCompanyList // instanceof handles nulls + && internalList.equals(((UniqueCompanyList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code persons} contains only unique persons. + */ + private boolean companiesAreUnique(List companies) { + for (int i = 0; i < companies.size() - 1; i++) { + for (int j = i + 1; j < companies.size(); j++) { + if (companies.get(i).isSameCompany(companies.get(j))) { + return false; + } + } + } + return true; + } + + public void SortList(Comparator Comcompany , String sequence) { + if(sequence.equals("seq")) { + Collections.sort(this.internalList, Comcompany); + } + else if(sequence.equals("rev")) { + Collections.sort(this.internalList, Collections.reverseOrder(Comcompany)); + } + } +} diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java index 0fee4fe57e6b..72458894a4c7 100644 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ b/src/main/java/seedu/address/model/person/UniquePersonList.java @@ -3,13 +3,14 @@ import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; -import java.util.Iterator; -import java.util.List; +import java.util.*; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.person.exceptions.DuplicatePersonException; import seedu.address.model.person.exceptions.PersonNotFoundException; +import java.util.Comparator; /** * A list of persons that enforces uniqueness between its elements and does not allow nulls. @@ -33,6 +34,7 @@ public class UniquePersonList implements Iterable { */ public boolean contains(Person toCheck) { requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSamePerson); } @@ -134,4 +136,14 @@ private boolean personsAreUnique(List persons) { } return true; } + + public void SortList(Comparator Comperson , String sequence) { + if(sequence.equals("seq")) { + Collections.sort(this.internalList, Comperson); + } + else if(sequence.equals("rev")) { + Collections.sort(this.internalList, Collections.reverseOrder(Comperson)); + } + } + } diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java index 1806da4facfa..7b36d8722ed9 100644 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java @@ -9,8 +9,11 @@ import seedu.address.model.person.Address; import seedu.address.model.person.Email; import seedu.address.model.person.Name; +import seedu.address.model.person.Occupation; import seedu.address.model.person.Person; import seedu.address.model.person.Phone; +import seedu.address.model.person.Relationship; +import seedu.address.model.person.Salary; import seedu.address.model.tag.Tag; /** @@ -20,23 +23,24 @@ public class SampleDataUtil { public static Person[] getSamplePersons() { return new Person[] { new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), - new Address("Blk 30 Geylang Street 29, #06-40"), - getTagSet("friends")), + new Address("Blk 30 Geylang Street 29, #06-40"), new Salary("15000"), new Occupation("Professor"), + new Relationship("single"), getTagSet("friends")), new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), - new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), - getTagSet("colleagues", "friends")), + new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), new Salary("9000"), + new Occupation("Programmer"), new Relationship("single"), + getTagSet("colleagues", "friends")), new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), - new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), - getTagSet("neighbours")), + new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), new Salary("3000"), new Occupation("HR"), + new Relationship("married"), getTagSet("neighbours")), new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), - new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), - getTagSet("family")), + new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), new Salary("50000"), + new Occupation("Excutive Director"), new Relationship("single"), getTagSet("family")), new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), - new Address("Blk 47 Tampines Street 20, #17-35"), - getTagSet("classmates")), + new Address("Blk 47 Tampines Street 20, #17-35"), new Salary("20000"), new Occupation("Vice President"), + new Relationship("married"), getTagSet("classmates")), new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), - new Address("Blk 45 Aljunied Street 85, #11-31"), - getTagSet("colleagues")) + new Address("Blk 45 Aljunied Street 85, #11-31"), new Salary("5000"), new Occupation("Project manager"), + new Relationship("divorced"), getTagSet("colleagues")) }; } diff --git a/src/main/java/seedu/address/storage/JsonAdaptedCompany.java b/src/main/java/seedu/address/storage/JsonAdaptedCompany.java new file mode 100644 index 000000000000..58424db37409 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedCompany.java @@ -0,0 +1,151 @@ +package seedu.address.storage; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.person.Address; +import seedu.address.model.person.Company; +import seedu.address.model.person.Email; +import seedu.address.model.person.Name; +import seedu.address.model.person.Occupation; +import seedu.address.model.person.Phone; +import seedu.address.model.person.Relationship; +import seedu.address.model.person.Salary; +import seedu.address.model.tag.Tag; + +/** + * Jackson-friendly version of {@link Company}. + */ +class JsonAdaptedCompany { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Company's %s field is missing!"; + + private final String name; + private final String phone; + private final String email; + private final String address; + private final String salary; + private final String occupation; + private final String relationship; + + private final List tagged = new ArrayList<>(); + + /** + * Constructs a {@code JsonAdaptedCompany} with the given company details. + */ + @JsonCreator + public JsonAdaptedCompany(@JsonProperty("name") String name, @JsonProperty("phone") String phone, + @JsonProperty("email") String email, @JsonProperty("address") String address, + @JsonProperty("salary") String salary, @JsonProperty("occupation") String occupation, + @JsonProperty("relationship") String relationship, + @JsonProperty("tagged") List tagged) { + this.name = name; + this.phone = phone; + this.email = email; + this.address = address; + this.salary = salary; + this.occupation = occupation; + this.relationship = relationship; + if (tagged != null) { + this.tagged.addAll(tagged); + } + } + + /** + * Converts a given {@code Company} into this class for Jackson use. + */ + public JsonAdaptedCompany(Company source) { + name = source.getName().fullName; + phone = source.getPhone().value; + email = source.getEmail().value; + address = source.getAddress().value; + salary = source.getSalary().value; + occupation = source.getOccupation().value; + relationship = source.getRelationship().value; + tagged.addAll(source.getTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList())); + } + + /** + * Converts this Jackson-friendly adapted company object into the model's {@code Company} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted company. + */ + public Company toModelType() throws IllegalValueException { + final List companyTags = new ArrayList<>(); + for (JsonAdaptedTag tag : tagged) { + companyTags.add(tag.toModelType()); + } + + if (name == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); + } + if (!Name.isValidName(name)) { + throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); + } + final Name modelName = new Name(name); + + if (phone == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); + } + if (!Phone.isValidPhone(phone)) { + throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); + } + final Phone modelPhone = new Phone(phone); + + if (email == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName())); + } + if (!Email.isValidEmail(email)) { + throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS); + } + final Email modelEmail = new Email(email); + + if (address == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); + } + if (!Address.isValidAddress(address)) { + throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); + } + final Address modelAddress = new Address(address); + + if (salary == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Salary.class.getSimpleName())); + } + if (!Salary.isValidSalary(salary)) { + throw new IllegalValueException(Salary.MESSAGE_CONSTRAINTS); + } + final Salary modelSalary = new Salary(salary); + + if (occupation == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Occupation.class.getSimpleName())); + } + if (!Occupation.isValidOccupation(occupation)) { + throw new IllegalValueException(Occupation.MESSAGE_CONSTRAINTS); + } + final Occupation modelOccupation = new Occupation(occupation); + + if (relationship == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Relationship.class.getSimpleName())); + } + if (!Relationship.isValidRelationship(relationship)) { + throw new IllegalValueException(Relationship.MESSAGE_CONSTRAINTS); + } + final Relationship modelRelationship = new Relationship(relationship); + + final Set modelTags = new HashSet<>(companyTags); + return new Company(modelName, modelPhone, modelEmail, modelAddress, + modelSalary, modelOccupation, modelRelationship, modelTags); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java index a6321cec2eac..1749ebb235ab 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java @@ -13,8 +13,11 @@ import seedu.address.model.person.Address; import seedu.address.model.person.Email; import seedu.address.model.person.Name; +import seedu.address.model.person.Occupation; import seedu.address.model.person.Person; import seedu.address.model.person.Phone; +import seedu.address.model.person.Relationship; +import seedu.address.model.person.Salary; import seedu.address.model.tag.Tag; /** @@ -28,6 +31,10 @@ class JsonAdaptedPerson { private final String phone; private final String email; private final String address; + private final String salary; + private final String occupation; + private final String relationship; + private final List tagged = new ArrayList<>(); /** @@ -36,11 +43,15 @@ class JsonAdaptedPerson { @JsonCreator public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone, @JsonProperty("email") String email, @JsonProperty("address") String address, - @JsonProperty("tagged") List tagged) { + @JsonProperty("salary") String salary, @JsonProperty("occupation") String occupation, + @JsonProperty("relationship") String relationship, @JsonProperty("tagged") List tagged) { this.name = name; this.phone = phone; this.email = email; this.address = address; + this.salary = salary; + this.occupation = occupation; + this.relationship = relationship; if (tagged != null) { this.tagged.addAll(tagged); } @@ -54,6 +65,9 @@ public JsonAdaptedPerson(Person source) { phone = source.getPhone().value; email = source.getEmail().value; address = source.getAddress().value; + salary = source.getSalary().value; + occupation = source.getOccupation().value; + relationship = source.getRelationship().value; tagged.addAll(source.getTags().stream() .map(JsonAdaptedTag::new) .collect(Collectors.toList())); @@ -102,8 +116,35 @@ public Person toModelType() throws IllegalValueException { } final Address modelAddress = new Address(address); + if (salary == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Salary.class.getSimpleName())); + } + if (!Salary.isValidSalary(salary)) { + throw new IllegalValueException(Salary.MESSAGE_CONSTRAINTS); + } + final Salary modelSalary = new Salary(salary); + + if (occupation == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Occupation.class.getSimpleName())); + } + if (!Occupation.isValidOccupation(occupation)) { + throw new IllegalValueException(Occupation.MESSAGE_CONSTRAINTS); + } + final Occupation modelOccupation = new Occupation(occupation); + + if (relationship == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Relationship.class.getSimpleName())); + } + if (!Relationship.isValidRelationship(relationship)) { + throw new IllegalValueException(Relationship.MESSAGE_CONSTRAINTS); + } + final Relationship modelRelationship = new Relationship(relationship); + final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); + return new Person(modelName, modelPhone, modelEmail, modelAddress, + modelSalary, modelOccupation, modelRelationship, modelTags); } } diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java index 5efd834091d4..f479c32a1d8d 100644 --- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java +++ b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java @@ -11,6 +11,7 @@ import seedu.address.commons.exceptions.IllegalValueException; import seedu.address.model.AddressBook; import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.person.Company; import seedu.address.model.person.Person; /** @@ -20,15 +21,22 @@ class JsonSerializableAddressBook { public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s)."; + public static final String MESSAGE_DUPLICATE_COMPANY = "Companies list contains duplicate person(s)."; private final List persons = new ArrayList<>(); + private final List companies = new ArrayList<>(); + private final List favorites = new ArrayList<>(); /** * Constructs a {@code JsonSerializableAddressBook} with the given persons. */ @JsonCreator - public JsonSerializableAddressBook(@JsonProperty("persons") List persons) { + public JsonSerializableAddressBook(@JsonProperty("persons") List persons, + @JsonProperty("companies") List companies, + @JsonProperty("favorites") List favorites) { this.persons.addAll(persons); + this.companies.addAll(companies); + this.favorites.addAll(favorites); } /** @@ -38,6 +46,8 @@ public JsonSerializableAddressBook(@JsonProperty("persons") List { public static final URL DEFAULT_PAGE = requireNonNull(MainApp.class.getResource(FXML_FILE_FOLDER + "default.html")); - public static final String SEARCH_PAGE_URL = "https://se-education.org/dummy-search-page/?name="; + + public static final String SEARCH_PAGE_URL = "https://www.google.com.sg/maps/search/"; private static final String FXML = "BrowserPanel.fxml"; @@ -50,7 +51,7 @@ public BrowserPanel(ObservableValue selectedPerson) { } private void loadPersonPage(Person person) { - loadPage(SEARCH_PAGE_URL + person.getName().fullName); + loadPage(SEARCH_PAGE_URL + person.getAddress()); } public void loadPage(String url) { diff --git a/src/main/java/seedu/address/ui/CompanyCard.java b/src/main/java/seedu/address/ui/CompanyCard.java new file mode 100644 index 000000000000..22b1ba48932c --- /dev/null +++ b/src/main/java/seedu/address/ui/CompanyCard.java @@ -0,0 +1,85 @@ +package seedu.address.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.address.model.person.Company; + +/** + * An UI component that displays information of a {@code Company}. + */ +public class CompanyCard extends UiPart { + + private static final String FXML = "CompanyListCard.fxml"; + private static final String[] TAG_COLORS = {"red", "yellow", "blue", "orange", "brown", "green"}; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + + public final Company company; + + @FXML + private HBox cardPane; + @FXML + private Label name; + @FXML + private Label id; + @FXML + private Label phone; + @FXML + private Label address; + @FXML + private Label email; + @FXML + private Label salary; + @FXML + private Label occupation; + @FXML + private Label relationship; + @FXML + private FlowPane tags; + + public CompanyCard(Company company, int displayedIndex) { + super(FXML); + this.company = company; + id.setText(displayedIndex + ". "); + name.setText(company.getName().fullName); + phone.setText("Phone: "+company.getPhone().value); + address.setText("Address: "+company.getAddress().value); + email.setText("Email: "+company.getEmail().value); + salary.setText("Revenue: "+company.getSalary().value); + occupation.setText("Sector: "+company.getOccupation().value); + relationship.setText("Structure: "+company.getRelationship().value); + + company.getTags().forEach(tag -> { + Label tagLabel = new Label(tag.tagName); + tagLabel.getStyleClass().add(TAG_COLORS[Math.abs(tag.tagName.hashCode()) % TAG_COLORS.length]); + tags.getChildren().add(tagLabel); + }); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof CompanyCard)) { + return false; + } + + // state check + CompanyCard card = (CompanyCard) other; + return id.getText().equals(card.id.getText()) + && company.equals(card.company); + } +} diff --git a/src/main/java/seedu/address/ui/CompanyListPanel.java b/src/main/java/seedu/address/ui/CompanyListPanel.java new file mode 100644 index 000000000000..1a07f7f3824c --- /dev/null +++ b/src/main/java/seedu/address/ui/CompanyListPanel.java @@ -0,0 +1,71 @@ +package seedu.address.ui; + +import java.util.Objects; +import java.util.function.Consumer; +import java.util.logging.Logger; + +import javafx.beans.value.ObservableValue; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Region; +import seedu.address.commons.core.LogsCenter; +import seedu.address.model.person.Company; + +/** + * Panel containing the list of companies. + */ +public class CompanyListPanel extends UiPart { + private static final String FXML = "CompanyListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(CompanyListPanel.class); + + @FXML + private ListView companyListView; + + public CompanyListPanel(ObservableList companyList, ObservableValue selectedCompany, + Consumer onSelectedCompanyChange) { + super(FXML); + companyListView.setItems(companyList); + companyListView.setCellFactory(listView -> new CompanyListViewCell()); + companyListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { + logger.fine("Selection in company list panel changed to : '" + newValue + "'"); + onSelectedCompanyChange.accept(newValue); + }); + selectedCompany.addListener((observable, oldValue, newValue) -> { + logger.fine("Selected company changed to: " + newValue); + + // Don't modify selection if we are already selecting the selected company, + // otherwise we would have an infinite loop. + if (Objects.equals(companyListView.getSelectionModel().getSelectedItem(), newValue)) { + return; + } + + if (newValue == null) { + companyListView.getSelectionModel().clearSelection(); + } else { + int index = companyListView.getItems().indexOf(newValue); + companyListView.scrollTo(index); + companyListView.getSelectionModel().clearAndSelect(index); + } + }); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Company} using a {@code CompanyCard}. + */ + class CompanyListViewCell extends ListCell { + @Override + protected void updateItem(Company company, boolean empty) { + super.updateItem(company, empty); + + if (empty || company == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new CompanyCard(company, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index ac165736001d..3f36a0e57367 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -33,6 +33,7 @@ public class MainWindow extends UiPart { // Independent Ui parts residing in this Ui container private BrowserPanel browserPanel; private PersonListPanel personListPanel; + private CompanyListPanel companyListPanel; private ResultDisplay resultDisplay; private HelpWindow helpWindow; @@ -48,6 +49,9 @@ public class MainWindow extends UiPart { @FXML private StackPane personListPanelPlaceholder; + @FXML + private StackPane companyListPanelPlaceholder; + @FXML private StackPane resultDisplayPlaceholder; @@ -118,10 +122,19 @@ void fillInnerParts() { logic::setSelectedPerson); personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); + + companyListPanel = new CompanyListPanel(logic.getFilteredCompanyList(), logic.selectedCompanyProperty(), + logic::setSelectedCompany); + companyListPanelPlaceholder.getChildren().add(companyListPanel.getRoot()); + + resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); - StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath(), logic.getAddressBook()); + StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath(), + logic.getAddressBook(), + logic.getFilteredPersonList().size(), + logic.getFilteredCompanyList().size()); statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); CommandBox commandBox = new CommandBox(this::executeCommand, logic.getHistory()); @@ -172,6 +185,10 @@ public PersonListPanel getPersonListPanel() { return personListPanel; } + public CompanyListPanel getCompanyListPanel() { + return companyListPanel; + } + /** * Executes the command and returns the result. * diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java index f6727ea83abd..d64c2a2cf91d 100644 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ b/src/main/java/seedu/address/ui/PersonCard.java @@ -13,6 +13,7 @@ public class PersonCard extends UiPart { private static final String FXML = "PersonListCard.fxml"; + private static final String[] TAG_COLORS = {"red", "yellow", "blue", "orange", "brown", "green"}; /** * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. @@ -37,6 +38,12 @@ public class PersonCard extends UiPart { @FXML private Label email; @FXML + private Label salary; + @FXML + private Label occupation; + @FXML + private Label relationship; + @FXML private FlowPane tags; public PersonCard(Person person, int displayedIndex) { @@ -44,10 +51,18 @@ public PersonCard(Person person, int displayedIndex) { this.person = person; id.setText(displayedIndex + ". "); name.setText(person.getName().fullName); - phone.setText(person.getPhone().value); - address.setText(person.getAddress().value); - email.setText(person.getEmail().value); - person.getTags().forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + phone.setText("Phone: "+person.getPhone().value); + address.setText("Address: "+person.getAddress().value); + email.setText("Email: "+person.getEmail().value); + salary.setText("Salary: "+person.getSalary().value); + occupation.setText("Occupation: "+person.getOccupation().value); + relationship.setText("Relationship: "+person.getRelationship().value); + + person.getTags().forEach(tag -> { + Label tagLabel = new Label(tag.tagName); + tagLabel.getStyleClass().add(TAG_COLORS[Math.abs(tag.tagName.hashCode()) % TAG_COLORS.length]); + tags.getChildren().add(tagLabel); + }); } @Override diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/seedu/address/ui/StatusBarFooter.java index b22e1f525256..db1490a8a4a2 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/seedu/address/ui/StatusBarFooter.java @@ -17,6 +17,8 @@ public class StatusBarFooter extends UiPart { public static final String SYNC_STATUS_INITIAL = "Not updated yet in this session"; public static final String SYNC_STATUS_UPDATED = "Last Updated: %s"; + public static final String SYNC_STATUS_NUM_CONTACTS = "Clients: %d"; + public static final String SYNC_STATUS_NUM_COMPANIES = "Companies: %d"; /** * Used to generate time stamps. @@ -33,13 +35,20 @@ public class StatusBarFooter extends UiPart { @FXML private Label syncStatus; @FXML + private Label numCompaniesStatus; + @FXML + private Label numContactsStatus; + @FXML private Label saveLocationStatus; - public StatusBarFooter(Path saveLocation, ReadOnlyAddressBook addressBook) { + public StatusBarFooter(Path saveLocation, ReadOnlyAddressBook addressBook, int numContacts, int numCompanies) { super(FXML); - addressBook.addListener(observable -> updateSyncStatus()); + addressBook.addListener(observable -> updateSyncStatus(addressBook.getPersonList().size(), + addressBook.getCompanyList().size())); syncStatus.setText(SYNC_STATUS_INITIAL); + numContactsStatus.setText(String.format(SYNC_STATUS_NUM_CONTACTS, numContacts)); + numCompaniesStatus.setText(String.format(SYNC_STATUS_NUM_COMPANIES, numCompanies)); saveLocationStatus.setText(Paths.get(".").resolve(saveLocation).toString()); } @@ -60,10 +69,12 @@ public static Clock getClock() { /** * Updates "last updated" status to the current time. */ - private void updateSyncStatus() { + private void updateSyncStatus(int numContacts, int numCompanies) { long now = clock.millis(); String lastUpdated = new Date(now).toString(); syncStatus.setText(String.format(SYNC_STATUS_UPDATED, lastUpdated)); + numContactsStatus.setText(String.format(SYNC_STATUS_NUM_CONTACTS, numContacts)); + numCompaniesStatus.setText(String.format(SYNC_STATUS_NUM_COMPANIES, numCompanies)); } } diff --git a/src/main/resources/view/AnalyzePanel.fxml b/src/main/resources/view/AnalyzePanel.fxml new file mode 100644 index 000000000000..30acdd035c11 --- /dev/null +++ b/src/main/resources/view/AnalyzePanel.fxml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/src/main/resources/view/CompanyListCard.fxml b/src/main/resources/view/CompanyListCard.fxml new file mode 100644 index 000000000000..0bea01e9dd2f --- /dev/null +++ b/src/main/resources/view/CompanyListCard.fxml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/CompanyListPanel.fxml b/src/main/resources/view/CompanyListPanel.fxml new file mode 100644 index 000000000000..b7117327227c --- /dev/null +++ b/src/main/resources/view/CompanyListPanel.fxml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 36e6b001cd8d..713c23d4999f 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -3,6 +3,20 @@ background-color: #383838; /* Used in the default.html file */ } +#clients { + -fx-font-size: 14pt; + -fx-font-family: "Segoe UI Semibold"; + -fx-text-fill: #e2edff; + -fx-opacity: 0.9; +} + +#companies { + -fx-font-size: 14pt; + -fx-font-family: "Segoe UI Semibold"; + -fx-text-fill: #e2edff; + -fx-opacity: 0.9; +} + .label { -fx-font-size: 11pt; -fx-font-family: "Segoe UI Semibold"; @@ -350,3 +364,33 @@ -fx-background-radius: 2; -fx-font-size: 11; } + +#tags .red { + -fx-text-fill: black; + -fx-background-color: red; +} + +#tags .yellow { + -fx-background-color: yellow; + -fx-text-fill: black; +} + +#tags .blue { + -fx-text-fill: white; + -fx-background-color: blue; +} + +#tags .orange { + -fx-text-fill: black; + -fx-background-color: orange; +} + +#tags .brown { + -fx-text-fill: white; + -fx-background-color: brown; +} + +#tags .green { + -fx-text-fill: black; + -fx-background-color: green; +} diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index 601ddabf2b5e..3154023db7f4 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -11,6 +11,7 @@ + @@ -51,9 +52,18 @@ + + + + + + + + + diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml index f08ea32ad558..6fbb53c16ce4 100644 --- a/src/main/resources/view/PersonListCard.fxml +++ b/src/main/resources/view/PersonListCard.fxml @@ -31,6 +31,9 @@