Skip to content

Commit

Permalink
Merge pull request nus-cs2103-AY2324S1#117 from FerdiHS/add-filter-cu…
Browse files Browse the repository at this point in the history
…stomer-descriptor

Add FilterCustomerDescriptor
  • Loading branch information
FerdiHS committed Oct 31, 2023
2 parents 160b0e2 + 5c94294 commit 37c8238
Show file tree
Hide file tree
Showing 13 changed files with 278 additions and 32 deletions.
12 changes: 6 additions & 6 deletions docs/DeveloperGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ This section describes some noteworthy details on how certain features are imple

#### Motivation
The property agent may want to edit the details of a customer or property after adding it to the application. For example, the property agent may want to change the budget range of a customer after adding it to PropertyMatch.
Or, the property agent may want to change the price of a property after adding it to PropertyMatch.
Or, the property agent may want to change the budget of a property after adding it to PropertyMatch.

#### Implementation
The `EditCustomerCommand` and `EditPropertyCommand` classes extends the `Command` class. They are used to edit the details of a customer or property, respectively.
Expand Down Expand Up @@ -301,17 +301,17 @@ The following sequence diagram shows how the `FilterCustomerCommand` is executed
[Back to top](#table-of-contents)

#### Motivation
The property agent may want to see a list of properties based on their budget. For example, the property agent may want to filter properties with price less than $1000000.
The property agent may want to see a list of properties based on their budget. For example, the property agent may want to filter properties with budget less than $1000000.
Or, the property agent may want to see a list of properties based on the characteristics. For example, the property agent may want to filter pink properties.
Or, the property agent may want to see a list of properties based on both price and characteristics to enhance productivity.
Or, the property agent may want to see a list of properties based on both budget and characteristics to enhance productivity.

#### Implementation
The `FilterPropertyCommand` class extends the `Command` class. They are used to filter properties.
The command allows the user to filter properties based on their price and/or characteristics. The commands expect at least one flag, either price or characteristics, to be used as a filter.
The command allows the user to filter properties based on their budget and/or characteristics. The commands expect at least one flag, either budget or characteristics, to be used as a filter.
When the filter command is inputted, the `FilterPropertyCommandParser` class is used to parse the user input and create the respective `FilterPropertyCommand` objects.
When these created command objects are executed by the `LogicManager`, the `FilterPropertyCommand#execute(Model model)` methods are called. These methods will update the filtered property list in the `model` which will eventually update the properties shown in the UI, and return a `CommandResult` object.

During this execution process, a new `PriceAndTagsInRangePredicate` object which is used as a predicate to check whether a property's price is lower and if the property has all the characteristics.
During this execution process, a new `PriceAndTagsInRangePredicate` object which is used as a predicate to check whether a property's budget is lower and if the property has all the characteristics.
All properties will be tested using this `PriceAndTagsInRangePredicate`. Properties which satisfy this condition will be included into the `FilteredPropertyList` in the model.

#### Design Considerations
Expand Down Expand Up @@ -576,7 +576,7 @@ System: PropertyMatch address book

Actor: Property Agent

1. Property agent fills in name, address, characteristics, number, price of property
1. Property agent fills in name, address, characteristics, number, budget of property
2. Property agent adds property to address book

**Use Case: UC02 - Add customer**
Expand Down
10 changes: 5 additions & 5 deletions docs/UserGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,14 @@ PropertyMatch is a desktop application for property agents who want to organise

Adds a property to the application.

Format: `addprop n/NAME a/ADDRESS [c/CHARACTERISTIC] ph/number pr/price`
Format: `addprop n/NAME a/ADDRESS [c/CHARACTERISTIC] ph/number pr/budget`

Parameter:
* `n/NAME` : The propName of the property (String)
* `a/ADDRESS` : The propAddress of the property (String)
* `c/CHARACTERISTIC` (Optional) : The characteristics of the property (String)
* `ph/NUMBER` : The contact number (Integer)
* `pr/PRICE` : The price of the property in psf (Number)
* `pr/PRICE` : The budget of the property in psf (Number)


Examples:
Expand All @@ -89,7 +89,7 @@ When command fails:
* `Missing propName parameter for add properties command` for missing propName parameter
* `Missing propAddress parameter for add properties command` for missing propAddress parameter
* `Missing number parameter for add properties command` for missing propName parameter
* `Missing price parameter for add properties command` for missing price parameter
* `Missing budget parameter for add properties command` for missing budget parameter
* `Invalid Command` for mispelling of command

### Adding a customer: `addcust`
Expand Down Expand Up @@ -278,7 +278,7 @@ When command fails:
Format: `filtercust [pr/PRICE] [c/CHARACTERISTIC]…​`

Parameter:
* `pr/PRICE` (optional) : The price of the property (Integer)
* `pr/PRICE` (optional) : The budget of the property (Integer)
* `c/CHARACTERISTIC` (optional) : The characteristics of the property (String)

Notes:
Expand Down Expand Up @@ -340,7 +340,7 @@ When command fails: Invalid command for misspelling of command

| Action | Format, Examples |
|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Addprop** | `addprop n/NAME a/ADDRESS [c/CHARACTERISTIC] ph/number pr/price` <br> e.g., `addprop n/Property a/randomAddress c/bright;sunny;big;square ph/91135235 pr/5` |
| **Addprop** | `addprop n/NAME a/ADDRESS [c/CHARACTERISTIC] ph/number pr/budget` <br> e.g., `addprop n/Property a/randomAddress c/bright;sunny;big;square ph/91135235 pr/5` |
| **Addcust** | `addcust n/NAME p/PHONE e/EMAIL [b/BUDGET] [c/CHARACTERISTIC]` <br> e.g., `addcust n/Fredy p/12345678 e/[email protected] b/100000` |
| **Delprop** | `delprop INDEX`<br> e.g., `delprop 3` |
| **Delcust** | `delcust INDEX`<br> e.g., `delcust 3` |
Expand Down
26 changes: 24 additions & 2 deletions docs/diagrams/FilterCustomerSequenceDiagram.puml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
participant ":FilterCustomerCommandParser" as FilterCustomerCommandParser LOGIC_COLOR
participant ":Budget" as Budget LOGIC_COLOR
participant ":Tag" as Tag LOGIC_COLOR
participant "descriptor:FilterCustomerDescriptor" as FilterCustomerDescriptor LOGIC_COLOR
participant ":BudgetAndTagsInRangePredicate" as BudgetAndTagsInRangePredicate LOGIC_COLOR
participant "command:FilterCustomerCommand" as FilterCustomerCommand LOGIC_COLOR
participant ":CommandResult" as CommandResult LOGIC_COLOR
Expand All @@ -32,27 +33,48 @@ deactivate FilterCustomerCommandParser
AddressBookParser -> FilterCustomerCommandParser : parse("b/100000 c/pink")
activate FilterCustomerCommandParser

create FilterCustomerDescriptor
FilterCustomerCommandParser -> FilterCustomerDescriptor: new FilterCustomerDescriptor
activate FilterCustomerDescriptor

FilterCustomerDescriptor --> FilterCustomerCommandParser: descriptor
deactivate FilterCustomerDescriptor

create Budget
FilterCustomerCommandParser -> Budget: new Budget(100000)
activate Budget

Budget --> FilterCustomerCommandParser: budget
deactivate Budget

FilterCustomerCommandParser -> FilterCustomerDescriptor: setBudget(budget)
activate FilterCustomerDescriptor
deactivate FilterCustomerDescriptor

create Tag
FilterCustomerCommandParser -> Tag: new Tag("pink")
activate Tag

Tag --> FilterCustomerCommandParser: tag
deactivate Tag

FilterCustomerCommandParser -> FilterCustomerDescriptor: setTags(tags)
activate FilterCustomerDescriptor
deactivate FilterCustomerDescriptor

FilterCustomerCommandParser -> FilterCustomerDescriptor: getPredicate()
activate FilterCustomerDescriptor

create BudgetAndTagsInRangePredicate
FilterCustomerCommandParser -> BudgetAndTagsInRangePredicate: new BudgetAndTagsInRangePredicate(budget, tags)
FilterCustomerDescriptor -> BudgetAndTagsInRangePredicate: new BudgetAndTagsInRangePredicate(budget, tags)
activate BudgetAndTagsInRangePredicate

BudgetAndTagsInRangePredicate --> FilterCustomerCommandParser: budgetAndTagsInRangePredicate
BudgetAndTagsInRangePredicate --> FilterCustomerDescriptor: budgetAndTagsInRangePredicate
deactivate BudgetAndTagsInRangePredicate

FilterCustomerDescriptor --> FilterCustomerCommandParser: budgetAndTagsInRangePredicate
deactivate FilterCustomerDescriptor

create FilterCustomerCommand
FilterCustomerCommandParser -> FilterCustomerCommand : new FilterCustomerCommand(budgetAndTagsInRangePredicate)
activate FilterCustomerCommand
Expand Down
Binary file modified docs/images/FilterCustomerSequenceDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,20 @@
import static seedu.address.logic.parser.CliSyntax.PREFIX_BUDGET;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;

import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import seedu.address.commons.util.CollectionUtil;
import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.Messages;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
import seedu.address.model.customer.Budget;
import seedu.address.model.customer.BudgetAndTagsInRangePredicate;
import seedu.address.model.tag.Tag;

/**
* Filter all customers in the address book to the user based on specific tags and/or budget.
Expand Down Expand Up @@ -63,4 +72,82 @@ public String toString() {
.add("predicate", predicate)
.toString();
}

/**
* Stores the details to filter the customer with budget and tags.
*/
public static class FilterCustomerDescriptor {
private Budget budget;
private Set<Tag> tags;

public FilterCustomerDescriptor() {}

/**
* Copy constructor.
* A defensive copy of {@code tags} is used internally.
*/
public FilterCustomerDescriptor(FilterCustomerDescriptor toCopy) {
setBudget(toCopy.budget);
setTags(toCopy.tags);
}

/**
* Returns true if at least one field is edited.
*/
public boolean isAnyFieldFiltered() {
return CollectionUtil.isAnyNonNull(budget, tags);
}

public void setBudget(Budget budget) {
this.budget = budget;
}

public Optional<Budget> getBudget() {
return Optional.ofNullable(budget);
}

/**
* Sets {@code tags} to this object's {@code tags}.
* A defensive copy of {@code tags} is used internally.
*/
public void setTags(Set<Tag> 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<Set<Tag>> getTags() {
return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty();
}

public BudgetAndTagsInRangePredicate getPredicate() {
return new BudgetAndTagsInRangePredicate(budget, tags);
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}

// instanceof handles nulls
if (!(other instanceof FilterCustomerDescriptor)) {
return false;
}

FilterCustomerDescriptor otherFilterPropertyDescriptor = (FilterCustomerDescriptor) other;
return Objects.equals(budget, otherFilterPropertyDescriptor.budget)
&& Objects.equals(tags, otherFilterPropertyDescriptor.tags);
}

@Override
public String toString() {
return new ToStringBuilder(this)
.add("budget", budget)
.add("tags", tags)
.toString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ public String toString() {
* Stores the details to filter the property with price and tags.
*/
public static class FilterPropertyDescriptor {
private Price price = null;
private Set<Tag> tags = null;
private Price price;
private Set<Tag> tags;

public FilterPropertyDescriptor() {}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
package seedu.address.logic.parser;

import static java.util.Objects.isNull;
import static java.util.Objects.requireNonNull;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_BUDGET;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;

import java.util.Set;

import seedu.address.logic.commands.FilterCustomerCommand;
import seedu.address.logic.commands.FilterCustomerCommand.FilterCustomerDescriptor;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.customer.Budget;
import seedu.address.model.customer.BudgetAndTagsInRangePredicate;
import seedu.address.model.tag.Tag;

/**
* Filters and lists all customers in address book whose budget and/or tags are selected.
Expand All @@ -26,24 +21,25 @@ public class FilterCustomerCommandParser implements Parser<FilterCustomerCommand
*/
public FilterCustomerCommand parse(String args) throws ParseException {
requireNonNull(args);
Budget budget = null;
Set<Tag> tags;
FilterCustomerDescriptor descriptor = new FilterCustomerDescriptor();
ArgumentMultimap argMultimap =
ArgumentTokenizer.tokenize(args, PREFIX_BUDGET, PREFIX_TAG);

argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_BUDGET);
if (argMultimap.getValue(PREFIX_BUDGET).isPresent()) {
budget = ParserUtil.parseBudget(argMultimap.getValue(PREFIX_BUDGET).get());
descriptor.setBudget(ParserUtil.parseBudget(argMultimap.getValue(PREFIX_BUDGET).get()));
}

tags = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
if (argMultimap.getValue(PREFIX_TAG).isPresent()) {
descriptor.setTags(ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)));
}

if (isNull(budget) && tags.isEmpty()) {
if (!descriptor.isAnyFieldFiltered()) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
FilterCustomerCommand.MESSAGE_USAGE));
}

return new FilterCustomerCommand(new BudgetAndTagsInRangePredicate(budget, tags));
return new FilterCustomerCommand(descriptor.getPredicate());
}

}
2 changes: 1 addition & 1 deletion src/main/resources/view/PropertyListCard.fxml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
<FlowPane fx:id="tags" />
<Label fx:id="phone" styleClass="cell_small_label" text="\$propPhone" />
<Label fx:id="address" styleClass="cell_small_label" text="\$propAddress" />
<Label fx:id="price" styleClass="cell_small_label" text="\$price" />
<Label fx:id="budget" styleClass="cell_small_label" text="\$budget" />
</VBox>
</GridPane>
</HBox>
13 changes: 11 additions & 2 deletions src/test/java/seedu/address/logic/commands/CommandTestUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@
import java.util.List;

import seedu.address.commons.core.index.Index;
import seedu.address.logic.commands.EditCustomerCommand.EditCustomerDescriptor;
import seedu.address.logic.commands.FilterCustomerCommand.FilterCustomerDescriptor;
import seedu.address.logic.commands.FilterPropertyCommand.FilterPropertyDescriptor;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.AddressBook;
import seedu.address.model.Model;
import seedu.address.model.customer.Customer;
import seedu.address.model.customer.NameContainsKeywordsPredicate;
import seedu.address.testutil.EditCustomerDescriptorBuilder;
import seedu.address.testutil.FilterCustomerDescriptorBuilder;
import seedu.address.testutil.FilterPropertyDescriptorBuilder;

/**
Expand Down Expand Up @@ -62,8 +65,10 @@ public class CommandTestUtil {
public static final String PREAMBLE_WHITESPACE = "\t \r \n";
public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble";

public static final EditCustomerCommand.EditCustomerDescriptor DESC_AMY;
public static final EditCustomerCommand.EditCustomerDescriptor DESC_BOB;
public static final EditCustomerDescriptor DESC_AMY;
public static final EditCustomerDescriptor DESC_BOB;
public static final FilterCustomerDescriptor FILTER_CUSTOMER_DESCRIPTOR_AMY;
public static final FilterCustomerDescriptor FILTER_CUSTOMER_DESCRIPTOR_BOB;

static {
DESC_AMY = new EditCustomerDescriptorBuilder().withName(VALID_NAME_AMY)
Expand All @@ -72,6 +77,10 @@ public class CommandTestUtil {
DESC_BOB = new EditCustomerDescriptorBuilder().withName(VALID_NAME_BOB)
.withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB).withBudget(VALID_BUDGET_BOB)
.withTags(VALID_TAG_BIG, VALID_TAG_SQUARE).build();
FILTER_CUSTOMER_DESCRIPTOR_AMY = new FilterCustomerDescriptorBuilder().withBudget(VALID_BUDGET_AMY)
.withTags(VALID_TAG_SQUARE).build();
FILTER_CUSTOMER_DESCRIPTOR_BOB = new FilterCustomerDescriptorBuilder().withBudget(VALID_BUDGET_BOB)
.withTags(VALID_TAG_BIG, VALID_TAG_SQUARE).build();
}

// Properties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,27 @@ public void execute_withBudgetSomeTags_customerFound() {
assertEquals(Arrays.asList(BENSON), model.getFilteredCustomerList());
}

@Test
public void toStringMethod() {
String firstTagString = "square";
String secondTagString = "garden";

Tag firstTag = new Tag(firstTagString);
Tag secondTag = new Tag(secondTagString);

Set<Tag> tags = new HashSet<>();
tags.add(firstTag);
tags.add(secondTag);

String budgetString = "100000000";
Budget budget = new Budget(budgetString);

BudgetAndTagsInRangePredicate predicate = new BudgetAndTagsInRangePredicate(budget, tags);
FilterCustomerCommand filterCustomerCommand = new FilterCustomerCommand(predicate);
String expected = FilterCustomerCommand.class.getCanonicalName() + "{predicate=" + predicate + "}";
assertEquals(expected, filterCustomerCommand.toString());
}

private FilterCustomerCommand preparePredicate(String message) {
try {
return (new FilterCustomerCommandParser()).parse(message);
Expand Down
Loading

0 comments on commit 37c8238

Please sign in to comment.