Skip to content

Commit

Permalink
Merge pull request nus-cs2103-AY2324S1#114 from FerdiHS/filter-property
Browse files Browse the repository at this point in the history
Add filter properties command
  • Loading branch information
FerdiHS authored Oct 31, 2023
2 parents 8fe5eeb + a5e0fd2 commit 9195ba0
Show file tree
Hide file tree
Showing 11 changed files with 689 additions and 4 deletions.
153 changes: 153 additions & 0 deletions src/main/java/seedu/address/logic/commands/FilterPropertyCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package seedu.address.logic.commands;

import static java.util.Objects.requireNonNull;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PRICE;
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.property.Price;
import seedu.address.model.property.PriceAndTagsInRangePredicate;
import seedu.address.model.tag.Tag;

/**
* Filter all properties in the address book to the user based on specific tags and/or price.
*/
public class FilterPropertyCommand extends Command {
public static final String COMMAND_WORD = "filterprop";

public static final String MESSAGE_USAGE = COMMAND_WORD + ": Filters properties from the address book. "
+ "Parameters: "
+ "[" + PREFIX_PRICE + "PRICE] "
+ "[" + PREFIX_TAG + "TAG]...\n"
+ "Example: " + COMMAND_WORD + " "
+ PREFIX_PRICE + "100000000 "
+ PREFIX_TAG + "bright "
+ PREFIX_TAG + "sunny";

private PriceAndTagsInRangePredicate predicate;

/**
* Creates an FilteredPropertyCommand to get all the specified {@code Property}
*/
public FilterPropertyCommand(PriceAndTagsInRangePredicate predicate) {
this.predicate = predicate;
}

@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
model.updateFilteredPropertyList(predicate);
return new CommandResult(
String.format(Messages.MESSAGE_PROPERTIES_LISTED_OVERVIEW, model.getFilteredPropertyList().size()));
}

@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}

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

FilterPropertyCommand otherFilterPropertyCommand = (FilterPropertyCommand) other;
return predicate.equals(otherFilterPropertyCommand.predicate);
}

@Override
public String toString() {
return new ToStringBuilder(this)
.add("predicate", predicate)
.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;

public FilterPropertyDescriptor() {}

/**
* Copy constructor.
* A defensive copy of {@code tags} is used internally.
*/
public FilterPropertyDescriptor(FilterPropertyDescriptor toCopy) {
setPrice(toCopy.price);
setTags(toCopy.tags);
}

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

public void setPrice(Price price) {
this.price = price;
}

public Optional<Price> getPrice() {
return Optional.ofNullable(price);
}

/**
* 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 PriceAndTagsInRangePredicate getPredicate() {
return new PriceAndTagsInRangePredicate(price, tags);
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}

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

FilterPropertyDescriptor otherFilterPropertyDescriptor = (FilterPropertyDescriptor) other;
return Objects.equals(price, otherFilterPropertyDescriptor.price)
&& Objects.equals(tags, otherFilterPropertyDescriptor.tags);
}

@Override
public String toString() {
return new ToStringBuilder(this)
.add("price", price)
.add("tags", tags)
.toString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import seedu.address.logic.commands.EditPropertyCommand;
import seedu.address.logic.commands.ExitCommand;
import seedu.address.logic.commands.FilterCustomerCommand;
import seedu.address.logic.commands.FilterPropertyCommand;
import seedu.address.logic.commands.FindCustomerCommand;
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCustomerCommand;
Expand Down Expand Up @@ -87,6 +88,9 @@ public Command parseCommand(String userInput) throws ParseException {
case FilterCustomerCommand.COMMAND_WORD:
return new FilterCustomerCommandParser().parse(arguments);

case FilterPropertyCommand.COMMAND_WORD:
return new FilterPropertyCommandParser().parse(arguments);

case ListCustomerCommand.COMMAND_WORD:
return new ListCustomerCommand();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
public class FilterCustomerCommandParser implements Parser<FilterCustomerCommand> {

/**
* Parses the given {@code String} of arguments in the context of the FilterCommand
* and returns a FilterCommand object for execution.
* Parses the given {@code String} of arguments in the context of the FilterCustomerCommand
* and returns a FilterPropertyCommand object for execution.
* @throws ParseException if the user input does not conform the expected format
*/
public FilterCustomerCommand parse(String args) throws ParseException {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package seedu.address.logic.parser;

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

import seedu.address.logic.commands.FilterPropertyCommand;
import seedu.address.logic.commands.FilterPropertyCommand.FilterPropertyDescriptor;
import seedu.address.logic.parser.exceptions.ParseException;

/**
* Filters and lists all properties in address book whose price and/or tags are selected.
*/
public class FilterPropertyCommandParser implements Parser<FilterPropertyCommand> {
/**
* Parses the given {@code String} of arguments in the context of the FilterPropertyCommand
* and returns a FilterPropertyCommand object for execution.
* @throws ParseException if the user input does not conform the expected format
*/
public FilterPropertyCommand parse(String args) throws ParseException {
requireNonNull(args);
FilterPropertyDescriptor descriptor = new FilterPropertyDescriptor();
ArgumentMultimap argMultimap =
ArgumentTokenizer.tokenize(args, PREFIX_PRICE, PREFIX_TAG);

argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_PRICE);
if (argMultimap.getValue(PREFIX_PRICE).isPresent()) {
descriptor.setPrice(ParserUtil.parsePrice(argMultimap.getValue(PREFIX_PRICE).get()));
}

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

if (!descriptor.isAnyFieldFiltered()) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
FilterPropertyCommand.MESSAGE_USAGE));
}

return new FilterPropertyCommand(descriptor.getPredicate());
}
}
38 changes: 38 additions & 0 deletions src/test/java/seedu/address/logic/commands/CommandTestUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
import static seedu.address.logic.parser.CliSyntax.PREFIX_BUDGET;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PRICE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import static seedu.address.testutil.Assert.assertThrows;

Expand All @@ -14,18 +16,21 @@
import java.util.List;

import seedu.address.commons.core.index.Index;
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.FilterPropertyDescriptorBuilder;

/**
* Contains helper methods for testing commands.
*/
public class CommandTestUtil {

// Customers
public static final String VALID_NAME_AMY = "Amy Bee";
public static final String VALID_NAME_BOB = "Bob Choo";
public static final String VALID_PHONE_AMY = "11111111";
Expand Down Expand Up @@ -69,6 +74,39 @@ public class CommandTestUtil {
.withTags(VALID_TAG_BIG, VALID_TAG_SQUARE).build();
}

// Properties
public static final String VALID_NAME_LIGHT = "Light House";
public static final String VALID_NAME_HELIX = "Helix House";
public static final String VALID_PHONE_LIGHT = "333333333";
public static final String VALID_PHONE_HELIX = "44444444";
public static final String VALID_ADDRESS_LIGHT = "25 Prince George's Park, Singapore 118424";
public static final String VALID_ADDRESS_HELIX = "37 Prince George's Park, Singapore 118430";
public static final String VALID_PRICE_LIGHT = "1000000";
public static final String VALID_PRICE_HELIX = "5000000";

public static final String NAME_DESC_LIGHT = " " + PREFIX_NAME + VALID_NAME_LIGHT;
public static final String NAME_DESC_HELIX = " " + PREFIX_NAME + VALID_NAME_HELIX;
public static final String PHONE_DESC_LIGHT = " " + PREFIX_PHONE + VALID_PHONE_LIGHT;
public static final String PHONE_DESC_HELIX = " " + PREFIX_PHONE + VALID_PHONE_HELIX;
public static final String ADDRESS_DESC_LIGHT = " " + PREFIX_EMAIL + VALID_ADDRESS_LIGHT;
public static final String ADDRESS_DESC_HELIX = " " + PREFIX_EMAIL + VALID_ADDRESS_HELIX;
public static final String PRICE_DESC_LIGHT = " " + PREFIX_BUDGET + VALID_PRICE_LIGHT;
public static final String PRICE_DESC_HELIX = " " + PREFIX_BUDGET + VALID_PRICE_HELIX;

public static final String INVALID_PRICE_DESC = " " + PREFIX_PRICE; // empty string not allowed for Price
public static final String INVALID_ADDRESS_DESC = " " + PREFIX_ADDRESS; // empty string not allowed for Address

public static final FilterPropertyDescriptor FILTER_PROPERTY_DESCRIPTOR_LIGHT;
public static final FilterPropertyDescriptor FILTER_PROPERTY_DESCRIPTOR_HELIX;

static {
FILTER_PROPERTY_DESCRIPTOR_LIGHT = new FilterPropertyDescriptorBuilder().withPrice(VALID_PRICE_LIGHT)
.withTags(VALID_TAG_SQUARE).build();
FILTER_PROPERTY_DESCRIPTOR_HELIX = new FilterPropertyDescriptorBuilder().withPrice(VALID_PRICE_HELIX)
.withTags(VALID_TAG_BIG, VALID_TAG_SQUARE).build();
}


/**
* Executes the given {@code command}, confirms that <br>
* - the returned {@link CommandResult} matches {@code expectedCommandResult} <br>
Expand Down
Loading

0 comments on commit 9195ba0

Please sign in to comment.