Skip to content

Commit

Permalink
Update filter by tag to handle multiple tags
Browse files Browse the repository at this point in the history
  • Loading branch information
iynixil committed Mar 13, 2024
1 parent 68f35d1 commit 0d981b7
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 82 deletions.
6 changes: 3 additions & 3 deletions src/main/java/staffconnect/logic/commands/FilterCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import staffconnect.commons.util.ToStringBuilder;
import staffconnect.logic.Messages;
import staffconnect.model.Model;
import staffconnect.model.person.PersonHasTagPredicate; // TagContainsKeywordsPredicate
import staffconnect.model.person.PersonHasTagsPredicate; // TagContainsKeywordsPredicate

/**
* Filters all persons in staff book whose module code or faculty shorthand or
Expand All @@ -28,12 +28,12 @@ public class FilterCommand extends Command {
+ "Example: " + COMMAND_WORD + " "
+ PREFIX_TAG + "BestProf";

private final PersonHasTagPredicate tagPredicate;
private final PersonHasTagsPredicate tagPredicate;

/**
* Creates a FilterTagCommand to filter for the specified {@code Tag}
*/
public FilterCommand(PersonHasTagPredicate tagPredicate) {
public FilterCommand(PersonHasTagsPredicate tagPredicate) {
this.tagPredicate = tagPredicate;
}

Expand Down
19 changes: 13 additions & 6 deletions src/main/java/staffconnect/logic/parser/FilterCommandParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
// 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.PersonHasTagPredicate;
import staffconnect.model.person.PersonHasTagsPredicate;
import staffconnect.model.tag.Tag;

/**
Expand All @@ -18,24 +20,29 @@
public class FilterCommandParser implements Parser<FilterCommand> {

/**
* Parses the given {@code String} of arguments in the context of the 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 argumentMultimap = ArgumentTokenizer.tokenize(args,
ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args,
PREFIX_TAG);

Tag tagName;
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 {
tagName = ParserUtil.parseTag(argumentMultimap.getValue(PREFIX_TAG).orElse(""));
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 PersonHasTagPredicate(tagName));
return new FilterCommand(new PersonHasTagsPredicate(tags));
}

}
43 changes: 0 additions & 43 deletions src/main/java/staffconnect/model/person/PersonHasTagPredicate.java

This file was deleted.

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();
}
}
24 changes: 16 additions & 8 deletions src/test/java/staffconnect/logic/commands/FilterCommandTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@

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.PersonHasTagPredicate;
import staffconnect.model.person.PersonHasTagsPredicate;
import staffconnect.model.tag.Tag;

public class FilterCommandTest {
Expand All @@ -29,7 +33,7 @@ public class FilterCommandTest {
@Test
public void execute_personHasTag_noPersonFound() {
String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 0);
PersonHasTagPredicate tagPredicate = prepareTagPredicate("hello");
PersonHasTagsPredicate tagPredicate = prepareTagPredicate("hello");
FilterCommand command = new FilterCommand(tagPredicate);
expectedModel.updateFilteredPersonList(tagPredicate);
assertCommandSuccess(command, model, expectedMessage, model);
Expand All @@ -39,7 +43,7 @@ public void execute_personHasTag_noPersonFound() {
@Test
public void execute_personHasTag_multiplePersonsFound() {
String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 3);
PersonHasTagPredicate tagPredicate = prepareTagPredicate("friends");
PersonHasTagsPredicate tagPredicate = prepareTagPredicate("friends");
FilterCommand command = new FilterCommand(tagPredicate);
expectedModel.updateFilteredPersonList(tagPredicate);
assertCommandSuccess(command, model, expectedMessage, model);
Expand All @@ -48,8 +52,8 @@ public void execute_personHasTag_multiplePersonsFound() {

@Test
public void equals() {
PersonHasTagPredicate firstTagPredicate = new PersonHasTagPredicate(new Tag("friend"));
PersonHasTagPredicate secondTagPredicate = new PersonHasTagPredicate(new Tag("colleagues"));
PersonHasTagsPredicate firstTagPredicate = prepareTagPredicate("friend");
PersonHasTagsPredicate secondTagPredicate = prepareTagPredicate("colleagues");

FilterCommand filterTagFirstCommand = new FilterCommand(firstTagPredicate);
FilterCommand filterTagSecondCommand = new FilterCommand(secondTagPredicate);
Expand All @@ -73,7 +77,7 @@ public void equals() {

@Test
public void toStringMethod() {
PersonHasTagPredicate tagPredicate = new PersonHasTagPredicate(new Tag("hello"));
PersonHasTagsPredicate tagPredicate = prepareTagPredicate("hello");
FilterCommand filterCommand = new FilterCommand(tagPredicate);
String expected = FilterCommand.class.getCanonicalName() + "{tagPredicate=" + tagPredicate + "}";
assertEquals(expected, filterCommand.toString());
Expand All @@ -82,8 +86,12 @@ public void toStringMethod() {
/**
* Parses {@code userInput} into a {@code PersonHasTagPredicate}.
*/
private PersonHasTagPredicate prepareTagPredicate(String userInput) {
return new PersonHasTagPredicate(new Tag(userInput));
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));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@
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.PersonHasTagPredicate;
import staffconnect.model.person.PersonHasTagsPredicate;
import staffconnect.model.tag.Tag;

public class FilterCommandParserTest {
Expand All @@ -27,9 +33,6 @@ public void parse_emptyArg_throwsParseException() {

@Test
public void parse_invalidTagName_throwsParseException() {
// no whitespace before PREFIX_TAG
assertParseFailure(parser, PREFIX_TAG + VALID_TAG_FRIEND,
String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_USAGE));
// tagname is non-alphanumeric (contains '*')
assertParseFailure(parser, INVALID_TAG_DESC,
String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_USAGE));
Expand All @@ -38,16 +41,45 @@ public void parse_invalidTagName_throwsParseException() {

@Test
public void parse_validTagName_success() {
// 1 leading and no trailing whitespaces
FilterCommand expectedFilterCommand = new FilterCommand(new PersonHasTagPredicate(new Tag(VALID_TAG_FRIEND)));
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
assertParseSuccess(parser, TAG_DESC_FRIEND + " ", expectedFilterCommand); // 1 leading, 3 trailing
// 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
assertParseSuccess(parser, " " + TAG_DESC_FRIEND + " ", expectedFilterCommand); // 2 leading, 1 trailing
assertParseSuccess(parser, " " + TAG_DESC_FRIEND + " ", expectedFilterCommand); // 5 leading, 3 trailing
// 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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
import static staffconnect.testutil.TypicalIndexes.INDEX_FIRST_PERSON;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.junit.jupiter.api.Test;
Expand All @@ -26,7 +28,7 @@
import staffconnect.logic.parser.exceptions.ParseException;
import staffconnect.model.person.NameContainsKeywordsPredicate;
import staffconnect.model.person.Person;
import staffconnect.model.person.PersonHasTagPredicate;
import staffconnect.model.person.PersonHasTagsPredicate;
import staffconnect.model.tag.Tag;
import staffconnect.testutil.EditPersonDescriptorBuilder;
import staffconnect.testutil.PersonBuilder;
Expand Down Expand Up @@ -73,10 +75,19 @@ public void parseCommand_exit() throws Exception {

@Test
public void parseCommand_filter() throws Exception {
// single tag
String tag = "hello";
FilterCommand command = (FilterCommand) parser.parseCommand(FilterCommand.COMMAND_WORD
Set<Tag> singleTag = new HashSet<Tag>(Arrays.asList(new Tag(tag)));
FilterCommand singleTagCommand = (FilterCommand) parser.parseCommand(FilterCommand.COMMAND_WORD
+ " t/" + tag);
assertEquals(new FilterCommand(new PersonHasTagPredicate(new Tag(tag))), command);
assertEquals(new FilterCommand(new PersonHasTagsPredicate(singleTag)), singleTagCommand);

// multiple tags
String tag2 = "hello2";
Set<Tag> multipleTags = new HashSet<Tag>(Arrays.asList(new Tag(tag), new Tag(tag2)));
FilterCommand multipleTagsCommand = (FilterCommand) parser.parseCommand(FilterCommand.COMMAND_WORD
+ " t/" + tag + " t/" + tag2);
assertEquals(new FilterCommand(new PersonHasTagsPredicate(multipleTags)), multipleTagsCommand);
}

@Test
Expand Down
Loading

0 comments on commit 0d981b7

Please sign in to comment.