forked from nus-cs2103-AY2324S2/tp
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #67 from iynixil/49-filter-contacts
Add filter functionality for tags
- Loading branch information
Showing
8 changed files
with
490 additions
and
0 deletions.
There are no files selected for viewing
70 changes: 70 additions & 0 deletions
70
src/main/java/staffconnect/logic/commands/FilterCommand.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package staffconnect.logic.commands; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
// import static staffconnect.logic.parser.CliSyntax.PREFIX_FACULTY; // TODO: add filtering for faculty and module | ||
// import static staffconnect.logic.parser.CliSyntax.PREFIX_MODULE; | ||
import static staffconnect.logic.parser.CliSyntax.PREFIX_TAG; | ||
|
||
import staffconnect.commons.util.ToStringBuilder; | ||
import staffconnect.logic.Messages; | ||
import staffconnect.model.Model; | ||
import staffconnect.model.person.PersonHasTagsPredicate; // TagContainsKeywordsPredicate | ||
|
||
/** | ||
* Filters all persons in staff book whose module code or faculty shorthand or | ||
* tags include the given filtering criteria. | ||
* Criteria matching is case-insensitive. | ||
*/ | ||
public class FilterCommand extends Command { | ||
|
||
public static final String COMMAND_WORD = "filter"; | ||
|
||
public static final String MESSAGE_USAGE = COMMAND_WORD + ": Filters all persons in staff book whose tags" | ||
+ " include the given tag name (case-insensitive) and displays them as a list with index numbers.\n" | ||
+ "Parameters: " | ||
// + "[" + PREFIX_FACULTY + "FACULTY]\n" | ||
// + "[" + PREFIX_MODULE + "MOODULE]\n" | ||
+ "[" + PREFIX_TAG + "TAG]\n" | ||
+ "Example: " + COMMAND_WORD + " " | ||
+ PREFIX_TAG + "BestProf"; | ||
|
||
private final PersonHasTagsPredicate tagPredicate; | ||
|
||
/** | ||
* Creates a FilterTagCommand to filter for the specified {@code Tag} | ||
*/ | ||
public FilterCommand(PersonHasTagsPredicate tagPredicate) { | ||
this.tagPredicate = tagPredicate; | ||
} | ||
|
||
@Override | ||
public CommandResult execute(Model model) { | ||
requireNonNull(model); | ||
model.updateFilteredPersonList(tagPredicate); | ||
return new CommandResult( | ||
String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object other) { | ||
if (other == this) { | ||
return true; | ||
} | ||
|
||
// instanceof handles nulls | ||
if (!(other instanceof FilterCommand)) { | ||
return false; | ||
} | ||
|
||
FilterCommand otherFilterCommand = (FilterCommand) other; | ||
return tagPredicate.equals(otherFilterCommand.tagPredicate); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return new ToStringBuilder(this) | ||
.add("tagPredicate", tagPredicate) | ||
.toString(); | ||
} | ||
|
||
} |
48 changes: 48 additions & 0 deletions
48
src/main/java/staffconnect/logic/parser/FilterCommandParser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package staffconnect.logic.parser; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
import static staffconnect.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; | ||
// import static staffconnect.logic.parser.CliSyntax.PREFIX_FACULTY; // TODO: add parsing for faculty and module | ||
// import static staffconnect.logic.parser.CliSyntax.PREFIX_MODULE; | ||
import static staffconnect.logic.parser.CliSyntax.PREFIX_TAG; | ||
|
||
import java.util.Set; | ||
|
||
import staffconnect.commons.exceptions.IllegalValueException; | ||
import staffconnect.logic.commands.FilterCommand; | ||
import staffconnect.logic.parser.exceptions.ParseException; | ||
import staffconnect.model.person.PersonHasTagsPredicate; | ||
import staffconnect.model.tag.Tag; | ||
|
||
/** | ||
* Parses input arguments and creates a new FilterCommand object | ||
*/ | ||
public class FilterCommandParser implements Parser<FilterCommand> { | ||
|
||
/** | ||
* Parses the given {@code String} of arguments in the context of the | ||
* FilterCommand | ||
* and returns a FilterCommand object for execution. | ||
* @throws ParseException if the user input does not conform the expected format | ||
*/ | ||
public FilterCommand parse(String args) throws ParseException { | ||
requireNonNull(args); | ||
ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, | ||
PREFIX_TAG); | ||
|
||
if (argMultimap.getAllValues(PREFIX_TAG).size() == 0) { // TODO: update for filter faculty/module | ||
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_USAGE)); | ||
} | ||
|
||
Set<Tag> tags; | ||
try { | ||
tags = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); | ||
} catch (IllegalValueException ive) { | ||
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, | ||
FilterCommand.MESSAGE_USAGE), ive); | ||
} | ||
|
||
return new FilterCommand(new PersonHasTagsPredicate(tags)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
src/main/java/staffconnect/model/person/PersonHasTagsPredicate.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package staffconnect.model.person; | ||
|
||
import java.util.List; | ||
import java.util.Set; | ||
import java.util.function.Predicate; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
|
||
import staffconnect.commons.util.ToStringBuilder; | ||
import staffconnect.model.tag.Tag; | ||
|
||
/** | ||
* Tests that a {@code Person}'s {@code Tag} matches any of the tag names given. | ||
*/ | ||
public class PersonHasTagsPredicate implements Predicate<Person> { | ||
private final Set<Tag> tags; // TODO: change to multiple ss in later iterations | ||
|
||
public PersonHasTagsPredicate(Set<Tag> tags) { | ||
this.tags = tags; | ||
} | ||
|
||
@Override | ||
public boolean test(Person person) { | ||
// get list of person tags | ||
List<String> personTags = person.getTags().stream().map(t -> t.tagName.toLowerCase()) | ||
.collect(Collectors.toList()); | ||
// get stream of tags to filter from | ||
Stream<String> tagsToFilter = tags.stream().map(t -> t.tagName.toLowerCase()) | ||
.collect(Collectors.toList()).stream(); | ||
// check if the person DOES NOT contain any of the tags to filter from, if true | ||
// then predicate is not satisfied | ||
return !tagsToFilter.anyMatch(tag -> !personTags.contains(tag)); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object other) { | ||
if (other == this) { | ||
return true; | ||
} | ||
|
||
// instanceof handles nulls | ||
if (!(other instanceof PersonHasTagsPredicate)) { | ||
return false; | ||
} | ||
|
||
PersonHasTagsPredicate otherPersonHasTagPredicate = (PersonHasTagsPredicate) other; | ||
return tags.equals(otherPersonHasTagPredicate.tags); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return new ToStringBuilder(this).add("tag name", tags).toString(); | ||
} | ||
} |
97 changes: 97 additions & 0 deletions
97
src/test/java/staffconnect/logic/commands/FilterCommandTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package staffconnect.logic.commands; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
import static staffconnect.logic.Messages.MESSAGE_PERSONS_LISTED_OVERVIEW; | ||
import static staffconnect.logic.commands.CommandTestUtil.assertCommandSuccess; | ||
import static staffconnect.testutil.TypicalPersons.ALICE; | ||
import static staffconnect.testutil.TypicalPersons.BENSON; | ||
import static staffconnect.testutil.TypicalPersons.DANIEL; | ||
import static staffconnect.testutil.TypicalPersons.getTypicalStaffBook; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
import staffconnect.model.Model; | ||
import staffconnect.model.ModelManager; | ||
import staffconnect.model.UserPrefs; | ||
import staffconnect.model.person.PersonHasTagsPredicate; | ||
import staffconnect.model.tag.Tag; | ||
|
||
public class FilterCommandTest { | ||
|
||
private Model model = new ModelManager(getTypicalStaffBook(), new UserPrefs()); | ||
private Model expectedModel = new ModelManager(getTypicalStaffBook(), new UserPrefs()); | ||
|
||
@Test | ||
public void execute_personHasTag_noPersonFound() { | ||
String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 0); | ||
PersonHasTagsPredicate tagPredicate = prepareTagPredicate("hello"); | ||
FilterCommand command = new FilterCommand(tagPredicate); | ||
expectedModel.updateFilteredPersonList(tagPredicate); | ||
assertCommandSuccess(command, model, expectedMessage, model); | ||
assertEquals(Collections.emptyList(), model.getFilteredPersonList()); | ||
} | ||
|
||
@Test | ||
public void execute_personHasTag_multiplePersonsFound() { | ||
String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 3); | ||
PersonHasTagsPredicate tagPredicate = prepareTagPredicate("friends"); | ||
FilterCommand command = new FilterCommand(tagPredicate); | ||
expectedModel.updateFilteredPersonList(tagPredicate); | ||
assertCommandSuccess(command, model, expectedMessage, model); | ||
assertEquals(Arrays.asList(ALICE, BENSON, DANIEL), model.getFilteredPersonList()); | ||
} | ||
|
||
@Test | ||
public void equals() { | ||
PersonHasTagsPredicate firstTagPredicate = prepareTagPredicate("friend"); | ||
PersonHasTagsPredicate secondTagPredicate = prepareTagPredicate("colleagues"); | ||
|
||
FilterCommand filterTagFirstCommand = new FilterCommand(firstTagPredicate); | ||
FilterCommand filterTagSecondCommand = new FilterCommand(secondTagPredicate); | ||
|
||
// same object -> returns true | ||
assertTrue(filterTagFirstCommand.equals(filterTagFirstCommand)); | ||
|
||
// same values -> returns true | ||
FilterCommand filterTagFirstCommandCopy = new FilterCommand(firstTagPredicate); | ||
assertTrue(filterTagFirstCommand.equals(filterTagFirstCommandCopy)); | ||
|
||
// different types -> returns false | ||
assertFalse(filterTagFirstCommand.equals(1)); | ||
|
||
// null -> returns false | ||
assertFalse(filterTagFirstCommand.equals(null)); | ||
|
||
// different tag -> returns false | ||
assertFalse(filterTagFirstCommand.equals(filterTagSecondCommand)); | ||
} | ||
|
||
@Test | ||
public void toStringMethod() { | ||
PersonHasTagsPredicate tagPredicate = prepareTagPredicate("hello"); | ||
FilterCommand filterCommand = new FilterCommand(tagPredicate); | ||
String expected = FilterCommand.class.getCanonicalName() + "{tagPredicate=" + tagPredicate + "}"; | ||
assertEquals(expected, filterCommand.toString()); | ||
} | ||
|
||
/** | ||
* Parses {@code userInput} into a {@code PersonHasTagPredicate}. | ||
*/ | ||
private PersonHasTagsPredicate prepareTagPredicate(String userInput) { | ||
List<Tag> tagList = Stream.of(userInput.split(" ")).map(str -> new Tag(str)).collect(Collectors.toList()); | ||
for (String separatedTag : userInput.split(" ")) { | ||
tagList.add(new Tag(separatedTag)); | ||
} | ||
return new PersonHasTagsPredicate(new HashSet<Tag>(tagList)); | ||
} | ||
|
||
} |
85 changes: 85 additions & 0 deletions
85
src/test/java/staffconnect/logic/parser/FilterCommandParserTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package staffconnect.logic.parser; | ||
|
||
import static staffconnect.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; | ||
import static staffconnect.logic.commands.CommandTestUtil.INVALID_TAG_DESC; | ||
import static staffconnect.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; | ||
import static staffconnect.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; | ||
import static staffconnect.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; | ||
import static staffconnect.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; | ||
import static staffconnect.logic.parser.CliSyntax.PREFIX_TAG; | ||
import static staffconnect.logic.parser.CommandParserTestUtil.assertParseFailure; | ||
import static staffconnect.logic.parser.CommandParserTestUtil.assertParseSuccess; | ||
|
||
import java.util.Arrays; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
import staffconnect.logic.commands.FilterCommand; | ||
import staffconnect.model.person.PersonHasTagsPredicate; | ||
import staffconnect.model.tag.Tag; | ||
|
||
public class FilterCommandParserTest { | ||
|
||
private static final String TAG_EMPTY = " " + PREFIX_TAG; // should have a space before tag prefix | ||
|
||
private FilterCommandParser parser = new FilterCommandParser(); | ||
|
||
@Test | ||
public void parse_emptyArg_throwsParseException() { | ||
assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_USAGE)); | ||
} | ||
|
||
@Test | ||
public void parse_invalidTagName_throwsParseException() { | ||
// tagname is non-alphanumeric (contains '*') | ||
assertParseFailure(parser, INVALID_TAG_DESC, | ||
String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_USAGE)); | ||
|
||
} | ||
|
||
@Test | ||
public void parse_validTagName_success() { | ||
Set<Tag> singleTag = new HashSet<Tag>(Arrays.asList(new Tag(VALID_TAG_FRIEND))); | ||
|
||
// single tag | ||
// 1 leading and no trailing whitespaces (TAG_DESC_xxx always has 1 leading) | ||
FilterCommand expectedFilterCommand = new FilterCommand(new PersonHasTagsPredicate(singleTag)); | ||
assertParseSuccess(parser, TAG_DESC_FRIEND, expectedFilterCommand); | ||
|
||
// 1 leading and multiple trailing whitespaces | ||
// 1 leading, 3 trailing | ||
assertParseSuccess(parser, TAG_DESC_FRIEND + " ", expectedFilterCommand); | ||
|
||
// multiple leading and trailing whitespaces | ||
// 2 leading, 1 trailing | ||
assertParseSuccess(parser, " " + TAG_DESC_FRIEND + " ", expectedFilterCommand); | ||
// 5 leading, 3 trailing | ||
assertParseSuccess(parser, " " + TAG_DESC_FRIEND + " ", expectedFilterCommand); | ||
|
||
// multiple tags | ||
Set<Tag> multipleTags = new HashSet<Tag>(Arrays.asList(new Tag(VALID_TAG_FRIEND), new Tag(VALID_TAG_HUSBAND))); | ||
expectedFilterCommand = new FilterCommand(new PersonHasTagsPredicate(multipleTags)); | ||
|
||
// 1 leading and no trailing whitespaces | ||
assertParseSuccess(parser, TAG_DESC_FRIEND + TAG_DESC_HUSBAND, expectedFilterCommand); | ||
|
||
// 1 leading and multiple trailing whitespaces | ||
// 1 leading, 3 trailing | ||
assertParseSuccess(parser, TAG_DESC_FRIEND + TAG_DESC_HUSBAND + " ", expectedFilterCommand); | ||
|
||
// multiple leading and trailing whitespaces | ||
// 2 leading, 1 trailing | ||
assertParseSuccess(parser, " " + TAG_DESC_FRIEND + TAG_DESC_HUSBAND + " ", expectedFilterCommand); | ||
// 5 leading, 3 trailing | ||
assertParseSuccess(parser, " " + TAG_DESC_FRIEND + TAG_DESC_HUSBAND + " ", expectedFilterCommand); | ||
|
||
// whitespaces in middle | ||
// 1 leading, 1 middle, 3 trailing | ||
assertParseSuccess(parser, TAG_DESC_FRIEND + " " + TAG_DESC_HUSBAND + " ", expectedFilterCommand); | ||
// 1 leading, 3 middle, 1 trailing | ||
assertParseSuccess(parser, TAG_DESC_FRIEND + " " + TAG_DESC_HUSBAND + " ", expectedFilterCommand); | ||
} | ||
|
||
} |
Oops, something went wrong.