From 0a7278454bb74027039bce6602538a3b18a8caf8 Mon Sep 17 00:00:00 2001 From: jiakai-17 <111171022+jiakai-17@users.noreply.github.com> Date: Wed, 25 Oct 2023 18:40:09 +0800 Subject: [PATCH 01/17] feat: Make Tags comparable --- src/main/java/seedu/address/model/tag/Tag.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java index f1a0d4e233b..523df58dd48 100644 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ b/src/main/java/seedu/address/model/tag/Tag.java @@ -7,7 +7,7 @@ * Represents a Tag in the address book. * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)} */ -public class Tag { +public class Tag implements Comparable { public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric"; public static final String VALIDATION_REGEX = "\\p{Alnum}+"; @@ -53,10 +53,15 @@ public int hashCode() { } /** - * Format state as text for viewing. + * Formats tag as text for viewing. */ + @Override public String toString() { return '[' + tagName + ']'; } + @Override + public int compareTo(Tag other) { + return this.tagName.compareTo(other.tagName); + } } From 242cf7b817a3358f8b6910d801e5cffafce31a43 Mon Sep 17 00:00:00 2001 From: jiakai-17 <111171022+jiakai-17@users.noreply.github.com> Date: Wed, 25 Oct 2023 18:50:12 +0800 Subject: [PATCH 02/17] test: Add more tests to Tag --- .../java/seedu/address/model/tag/TagTest.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/test/java/seedu/address/model/tag/TagTest.java b/src/test/java/seedu/address/model/tag/TagTest.java index 64d07d79ee2..07073d1a9f6 100644 --- a/src/test/java/seedu/address/model/tag/TagTest.java +++ b/src/test/java/seedu/address/model/tag/TagTest.java @@ -1,11 +1,17 @@ package seedu.address.model.tag; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static seedu.address.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; public class TagTest { + public static final Tag VALID_TAG = new Tag("tag"); + public static final Tag VALID_TAG_COPY = new Tag("tag"); + @Test public void constructor_null_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> new Tag(null)); @@ -23,4 +29,42 @@ public void isValidTagName() { assertThrows(NullPointerException.class, () -> Tag.isValidTagName(null)); } + @Test + public void equals() { + // same object -> returns true + assertEquals(VALID_TAG, VALID_TAG); + + // same values -> returns true + assertEquals(VALID_TAG, VALID_TAG_COPY); + + // null -> returns false + assertNotEquals(VALID_TAG, null); + + // different types -> returns false + assertNotEquals(VALID_TAG, 1); + + // different values -> returns false + Tag differentTag = new Tag("differentTag"); + assertNotEquals(VALID_TAG, differentTag); + } + + @Test + public void hashCodeTest() { + assertEquals(VALID_TAG.hashCode(), VALID_TAG_COPY.hashCode()); + } + + @Test + public void toStringTest() { + assertEquals("[tag]", VALID_TAG.toString()); + } + + @Test + public void compareTo() { + Tag smallerTag = new Tag("a"); + Tag biggerTag = new Tag("z"); + + assertEquals(0, VALID_TAG.compareTo(VALID_TAG_COPY)); + assertTrue(VALID_TAG.compareTo(biggerTag) < 0); + assertTrue(VALID_TAG.compareTo(smallerTag) > 0); + } } From f83d2f9df146bcd99cf26e1cd1b3b4df745a18d4 Mon Sep 17 00:00:00 2001 From: jiakai-17 <111171022+jiakai-17@users.noreply.github.com> Date: Wed, 25 Oct 2023 20:01:45 +0800 Subject: [PATCH 03/17] feat: Create TagFrequencyTable class * Add tests for TagFrequencyTable --- .../address/model/tag/TagFrequencyTable.java | 143 ++++++++++++++++++ .../model/tag/TagFrequencyTableTest.java | 130 ++++++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 src/main/java/seedu/address/model/tag/TagFrequencyTable.java create mode 100644 src/test/java/seedu/address/model/tag/TagFrequencyTableTest.java diff --git a/src/main/java/seedu/address/model/tag/TagFrequencyTable.java b/src/main/java/seedu/address/model/tag/TagFrequencyTable.java new file mode 100644 index 00000000000..259e58c80dd --- /dev/null +++ b/src/main/java/seedu/address/model/tag/TagFrequencyTable.java @@ -0,0 +1,143 @@ +package seedu.address.model.tag; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.model.person.Person; +import seedu.address.model.task.Task; + +/** + * Represents a table of tag frequencies for both persons and tasks in the address book. + */ +public class TagFrequencyTable { + + private final Map personTagFrequency; + private final Map taskTagFrequency; + private final Map combinedTagFrequency; + + /** + * Constructs a new {@code TagFrequencyTable} from a list of {@code Person}s and {@code Task}s. + * + * @param personList List of {@code Person}s. + * @param taskList List of {@code Task}s. + */ + public TagFrequencyTable(List personList, List taskList) { + this.personTagFrequency = new HashMap<>(); + this.taskTagFrequency = new HashMap<>(); + this.combinedTagFrequency = new HashMap<>(); + + personList.forEach(this::processPerson); + taskList.forEach(this::processTask); + } + + /** + * Processes the tags of a person and increments the frequency of each tag. + * + * @param p The Person whose tags are to be processed. + */ + private void processPerson(Person p) { + p.getTags().forEach(tag -> { + personTagFrequency.put(tag, personTagFrequency.getOrDefault(tag, 0) + 1); + combinedTagFrequency.put(tag, combinedTagFrequency.getOrDefault(tag, 0) + 1); + }); + } + + /** + * Processes the tags of a task and increments the frequency of each tag. + * + * @param t The Task whose tags are to be processed. + */ + private void processTask(Task t) { + t.getTags().forEach(tag -> { + taskTagFrequency.put(tag, taskTagFrequency.getOrDefault(tag, 0) + 1); + combinedTagFrequency.put(tag, combinedTagFrequency.getOrDefault(tag, 0) + 1); + }); + } + + public Map getPersonTagFrequency() { + return new HashMap<>(personTagFrequency); + } + + public Map getTaskTagFrequency() { + return new HashMap<>(taskTagFrequency); + } + + public Map getCombinedTagFrequency() { + return new HashMap<>(combinedTagFrequency); + } + + /** + * Returns a formatted string of the tag frequency table. + * The output is formatted in descending order of tag frequency, and then in alphabetical order. + * + * @return Formatted String representation of the tag frequency table. + */ + public String format() { + ArrayList formattedTagList = new ArrayList<>(); + + // Compares two entries in the table, and sorts them in descending order of frequency. + // If two entries have the same frequency, they are sorted in alphabetical order. + // Solution inspired by https://stackoverflow.com/a/33552682 + Comparator> comparator = (e1, e2) -> + e1.getValue().equals(e2.getValue()) + ? e1.getKey().compareTo(e2.getKey()) + : e2.getValue().compareTo(e1.getValue()); + + combinedTagFrequency.entrySet() + .stream() + .sorted(comparator) + .map(entry -> String.format("%s: %d items (%d Persons, %d Tasks)", + entry.getKey(), + entry.getValue(), + personTagFrequency.getOrDefault(entry.getKey(), 0), + taskTagFrequency.getOrDefault(entry.getKey(), 0))) + .forEach(formattedTagList::add); + + return String.join("\n", formattedTagList); + } + + /** + * Checks if the tag frequency table is empty. + * + * @return True if the tag frequency table is empty. + */ + public boolean isEmpty() { + return combinedTagFrequency.isEmpty(); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("personTagFrequency", personTagFrequency) + .add("taskTagFrequency", taskTagFrequency) + .add("combinedTagFrequency", combinedTagFrequency) + .toString(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof TagFrequencyTable)) { + return false; + } + + TagFrequencyTable otherTable = (TagFrequencyTable) other; + return personTagFrequency.equals(otherTable.personTagFrequency) + && taskTagFrequency.equals(otherTable.taskTagFrequency) + && combinedTagFrequency.equals(otherTable.combinedTagFrequency); + } + + @Override + public int hashCode() { + return Objects.hash(personTagFrequency, taskTagFrequency, combinedTagFrequency); + } +} diff --git a/src/test/java/seedu/address/model/tag/TagFrequencyTableTest.java b/src/test/java/seedu/address/model/tag/TagFrequencyTableTest.java new file mode 100644 index 00000000000..a82c8dff1db --- /dev/null +++ b/src/test/java/seedu/address/model/tag/TagFrequencyTableTest.java @@ -0,0 +1,130 @@ +package seedu.address.model.tag; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Map; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import seedu.address.model.AddressBook; +import seedu.address.testutil.TypicalPersons; +import seedu.address.testutil.TypicalTasks; + +class TagFrequencyTableTest { + + private static TagFrequencyTable tagFrequencyTable; + private static TagFrequencyTable tagFrequencyTableCopy; + private static TagFrequencyTable personOnlyTagFrequencyTable; + private static TagFrequencyTable taskOnlyTagFrequencyTable; + private static TagFrequencyTable emptyTagFrequencyTable; + + @BeforeAll + static void beforeAll() { + AddressBook addressBook = new AddressBook(); + addressBook.setPersons(TypicalPersons.getTypicalPersons()); + addressBook.setTasks(TypicalTasks.getTypicalTasks()); + tagFrequencyTable = + new TagFrequencyTable(addressBook.getPersonList(), addressBook.getTaskList()); + tagFrequencyTableCopy = + new TagFrequencyTable(addressBook.getPersonList(), addressBook.getTaskList()); + personOnlyTagFrequencyTable = + new TagFrequencyTable(addressBook.getPersonList(), new ArrayList<>()); + taskOnlyTagFrequencyTable = + new TagFrequencyTable(new ArrayList<>(), addressBook.getTaskList()); + emptyTagFrequencyTable = + new TagFrequencyTable(new ArrayList<>(), new ArrayList<>()); + } + + @Test + public void getPersonTagFrequency_addEntry_notModified() { + Map modifiedMap = tagFrequencyTable.getPersonTagFrequency(); + modifiedMap.put(new Tag("modifiedEntry"), 1); + assertNotEquals(modifiedMap, tagFrequencyTable.getPersonTagFrequency()); + } + + @Test + public void getTaskTagFrequency_addEntry_notModified() { + Map modifiedMap = tagFrequencyTable.getTaskTagFrequency(); + modifiedMap.put(new Tag("modifiedEntry"), 1); + assertNotEquals(modifiedMap, tagFrequencyTable.getTaskTagFrequency()); + } + + @Test + public void getCombinedTagFrequency_addEntry_notModified() { + Map modifiedMap = tagFrequencyTable.getCombinedTagFrequency(); + modifiedMap.put(new Tag("modifiedEntry"), 1); + assertNotEquals(modifiedMap, tagFrequencyTable.getCombinedTagFrequency()); + } + + @Test + public void format() { + String expected = "[class]: 8 items (0 Persons, 8 Tasks)\n" + + "[admin]: 3 items (0 Persons, 3 Tasks)\n" + + "[finance]: 3 items (0 Persons, 3 Tasks)\n" + + "[friends]: 3 items (3 Persons, 0 Tasks)\n" + + "[caterer]: 1 items (0 Persons, 1 Tasks)\n" + + "[location]: 1 items (0 Persons, 1 Tasks)\n" + + "[owesMoney]: 1 items (1 Persons, 0 Tasks)"; + assertEquals(expected, tagFrequencyTable.format()); + } + + @Test + public void isEmpty() { + assertFalse(tagFrequencyTable.isEmpty()); + assertTrue(emptyTagFrequencyTable.isEmpty()); + } + + @Test + public void toStringMethod() { + // different contents -> different string + assertNotEquals(tagFrequencyTable.toString(), personOnlyTagFrequencyTable.toString()); + + // same contents -> same string + assertEquals(tagFrequencyTable.toString(), tagFrequencyTableCopy.toString()); + + // check format + String expected = TagFrequencyTable.class.getCanonicalName() + + "{personTagFrequency=" + tagFrequencyTable.getPersonTagFrequency() + + ", taskTagFrequency=" + tagFrequencyTable.getTaskTagFrequency() + + ", combinedTagFrequency=" + tagFrequencyTable.getCombinedTagFrequency() + + "}"; + assertEquals(expected, tagFrequencyTable.toString()); + } + + @Test + public void equals() { + // same object -> returns true + assertEquals(tagFrequencyTable, tagFrequencyTable); + + // null -> returns false + assertNotEquals(tagFrequencyTable, null); + + // different types -> returns false + assertNotEquals(tagFrequencyTable, 5); + + // different contents -> returns false + assertNotEquals(tagFrequencyTable, personOnlyTagFrequencyTable); + assertNotEquals(tagFrequencyTable, taskOnlyTagFrequencyTable); + assertNotEquals(tagFrequencyTable, emptyTagFrequencyTable); + + // same contents -> returns true + assertEquals(tagFrequencyTable, tagFrequencyTableCopy); + } + + @Test + public void hashCodeMethod() { + // same object -> same hashcode + assertEquals(tagFrequencyTable.hashCode(), tagFrequencyTable.hashCode()); + + // different contents -> different hashcode + assertNotEquals(tagFrequencyTable.hashCode(), personOnlyTagFrequencyTable.hashCode()); + + // same contents -> same hashcode + assertEquals(tagFrequencyTable.hashCode(), tagFrequencyTableCopy.hashCode()); + } +} From 5852e4ce12bf42a16abbc9bd62f4474c9d12024f Mon Sep 17 00:00:00 2001 From: jiakai-17 <111171022+jiakai-17@users.noreply.github.com> Date: Wed, 25 Oct 2023 20:46:07 +0800 Subject: [PATCH 04/17] feat: Implement getting of tag frequency table --- .../java/seedu/address/model/AddressBook.java | 10 ++++++++++ src/main/java/seedu/address/model/Model.java | 7 +++++++ .../java/seedu/address/model/ModelManager.java | 8 ++++++++ .../seedu/address/model/ModelManagerTest.java | 16 ++++++++++++++++ src/test/java/seedu/address/model/ModelStub.java | 6 ++++++ 5 files changed, 47 insertions(+) diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index a04818a64ba..c96270d7ac6 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -8,6 +8,7 @@ import seedu.address.commons.util.ToStringBuilder; import seedu.address.model.person.Person; import seedu.address.model.person.UniquePersonList; +import seedu.address.model.tag.TagFrequencyTable; import seedu.address.model.task.Task; import seedu.address.model.task.UniqueTaskList; @@ -159,6 +160,15 @@ public void deleteAllTask() { tasks.deleteAll(); } + // =========== Tag Level Operations ====================================================================== + + /** + * Returns a frequency table of all tags in the address book. + */ + public TagFrequencyTable getTagFrequencyTable() { + return new TagFrequencyTable(persons.asUnmodifiableObservableList(), tasks.asUnmodifiableObservableList()); + } + //// util methods @Override diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index aa56aa5189d..75102206f35 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -6,6 +6,7 @@ import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; import seedu.address.model.person.Person; +import seedu.address.model.tag.TagFrequencyTable; import seedu.address.model.task.Task; /** @@ -160,4 +161,10 @@ public interface Model { */ Task unmarkTask(Task task); + // =========== Tag Level Operations ====================================================================== + + /** + * Returns a frequency table of all tags in the address book. + */ + TagFrequencyTable getTagFrequencyTable(); } diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 6c884dc84c4..cad44638dd8 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -12,6 +12,7 @@ import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; import seedu.address.model.person.Person; +import seedu.address.model.tag.TagFrequencyTable; import seedu.address.model.task.Task; /** @@ -181,6 +182,13 @@ public Task unmarkTask(Task task) { return task.unmarkDone(); } + // =========== Tag Level Operations ====================================================================== + + @Override + public TagFrequencyTable getTagFrequencyTable() { + return addressBook.getTagFrequencyTable(); + } + // =========== Filtered Task List Accessors ============================================================== /** diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/seedu/address/model/ModelManagerTest.java index cb6e3e336a8..ab730e8cde3 100644 --- a/src/test/java/seedu/address/model/ModelManagerTest.java +++ b/src/test/java/seedu/address/model/ModelManagerTest.java @@ -7,7 +7,9 @@ import static seedu.address.testutil.Assert.assertThrows; import static seedu.address.testutil.TypicalPersons.ALICE; import static seedu.address.testutil.TypicalPersons.BENSON; +import static seedu.address.testutil.TypicalPersons.getTypicalPersons; import static seedu.address.testutil.TypicalTasks.AGENDA; +import static seedu.address.testutil.TypicalTasks.getTypicalTasks; import java.nio.file.Path; import java.nio.file.Paths; @@ -17,6 +19,7 @@ import seedu.address.commons.core.GuiSettings; import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.tag.TagFrequencyTable; import seedu.address.model.task.Status; import seedu.address.model.task.Status.TaskStatus; import seedu.address.model.task.Task; @@ -135,6 +138,19 @@ public void unmarkTask_validTask_success() { new Status(TaskStatus.NOT_DONE))); } + @Test + public void getTagFrequencyTable_returnsSameTable() { + AddressBook typicalAddressBook = new AddressBook(); + typicalAddressBook.setPersons(getTypicalPersons()); + typicalAddressBook.setTasks(getTypicalTasks()); + modelManager.setAddressBook(typicalAddressBook); + + TagFrequencyTable tagFrequencyTable = + new TagFrequencyTable(getTypicalPersons(), getTypicalTasks()); + + assertEquals(modelManager.getTagFrequencyTable(), tagFrequencyTable); + } + @Test public void getFilteredTaskList_modifyList_throwsUnsupportedOperationException() { assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredTaskList().remove(0)); diff --git a/src/test/java/seedu/address/model/ModelStub.java b/src/test/java/seedu/address/model/ModelStub.java index e237922f9e0..bfca60a4f03 100644 --- a/src/test/java/seedu/address/model/ModelStub.java +++ b/src/test/java/seedu/address/model/ModelStub.java @@ -6,6 +6,7 @@ import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; import seedu.address.model.person.Person; +import seedu.address.model.tag.TagFrequencyTable; import seedu.address.model.task.Task; /** @@ -132,4 +133,9 @@ public Task unmarkTask(Task task) { throw new AssertionError("This method should not be called."); } + @Override + public TagFrequencyTable getTagFrequencyTable() { + throw new AssertionError("This method should not be called."); + } + } From 8945156658484690ba6b1ca439bc9fa0df910195 Mon Sep 17 00:00:00 2001 From: jiakai-17 <111171022+jiakai-17@users.noreply.github.com> Date: Wed, 25 Oct 2023 21:34:04 +0800 Subject: [PATCH 05/17] feat: Implement listTag command * Add tests for parsing listTag command --- .../logic/commands/ListTagCommand.java | 25 +++++++++++ .../logic/parser/AddressBookParser.java | 4 ++ .../logic/commands/ListTagCommandTest.java | 43 +++++++++++++++++++ .../logic/parser/AddressBookParserTest.java | 7 +++ 4 files changed, 79 insertions(+) create mode 100644 src/main/java/seedu/address/logic/commands/ListTagCommand.java create mode 100644 src/test/java/seedu/address/logic/commands/ListTagCommandTest.java diff --git a/src/main/java/seedu/address/logic/commands/ListTagCommand.java b/src/main/java/seedu/address/logic/commands/ListTagCommand.java new file mode 100644 index 00000000000..c9d57d9d7a9 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ListTagCommand.java @@ -0,0 +1,25 @@ +package seedu.address.logic.commands; + +import seedu.address.model.Model; +import seedu.address.model.tag.TagFrequencyTable; + +/** + * Lists all tags in CoordiMate to the user. + */ +public class ListTagCommand extends Command { + public static final String COMMAND_WORD = "listTag"; + + public static final String MESSAGE_NO_TAGS_FOUND = "No tags found!"; + public static final String MESSAGE_SUCCESS = "Listed all tags: \n%s\n"; + + @Override + public CommandResult execute(Model model) { + TagFrequencyTable tagFrequencyTable = model.getTagFrequencyTable(); + + if (tagFrequencyTable.isEmpty()) { + return new CommandResult(MESSAGE_NO_TAGS_FOUND); + } + + return new CommandResult(String.format(MESSAGE_SUCCESS, tagFrequencyTable.format())); + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index bddab16a695..e692e327bdd 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -22,6 +22,7 @@ import seedu.address.logic.commands.FindTaskCommand; import seedu.address.logic.commands.HelpCommand; import seedu.address.logic.commands.ListPersonCommand; +import seedu.address.logic.commands.ListTagCommand; import seedu.address.logic.commands.ListTaskCommand; import seedu.address.logic.commands.MarkTaskCommand; import seedu.address.logic.commands.UnmarkTaskCommand; @@ -103,6 +104,9 @@ public Command parseCommand(String userInput) throws ParseException { case DeleteAllTaskCommand.COMMAND_WORD: return new DeleteAllTaskCommand(); + case ListTagCommand.COMMAND_WORD: + return new ListTagCommand(); + case ExitCommand.COMMAND_WORD: return new ExitCommand(); diff --git a/src/test/java/seedu/address/logic/commands/ListTagCommandTest.java b/src/test/java/seedu/address/logic/commands/ListTagCommandTest.java new file mode 100644 index 00000000000..f837560a3f8 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/ListTagCommandTest.java @@ -0,0 +1,43 @@ +package seedu.address.logic.commands; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.tag.TagFrequencyTable; +import seedu.address.testutil.PersonBuilder; +import seedu.address.testutil.TaskBuilder; +import seedu.address.testutil.TypicalPersons; +import seedu.address.testutil.TypicalTasks; + +class ListTagCommandTest { + + @Test + public void execute_noTagsInList_showsNoTagsMessage() { + AddressBook noTagsAddressBook = new AddressBook(); + noTagsAddressBook.addPerson(new PersonBuilder(TypicalPersons.ALICE).withTags().build()); + noTagsAddressBook.addTask(new TaskBuilder(TypicalTasks.AGENDA).withTags().build()); + + Model model = new ModelManager(noTagsAddressBook, new UserPrefs()); + + assertCommandSuccess(new ListTagCommand(), model, ListTagCommand.MESSAGE_NO_TAGS_FOUND, model); + } + + @Test + public void execute_tagsInList_showsCorrectMessage() { + AddressBook typicalAddressBook = new AddressBook(); + typicalAddressBook.setPersons(TypicalPersons.getTypicalPersons()); + typicalAddressBook.setTasks(TypicalTasks.getTypicalTasks()); + + Model model = new ModelManager(typicalAddressBook, new UserPrefs()); + + TagFrequencyTable tagFrequencyTable = model.getTagFrequencyTable(); + String expectedMessage = String.format(ListTagCommand.MESSAGE_SUCCESS, tagFrequencyTable.format()); + + assertCommandSuccess(new ListTagCommand(), model, expectedMessage, model); + } +} diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java index 04ccdebb848..7f2889ac7bf 100644 --- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java +++ b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java @@ -27,6 +27,7 @@ import seedu.address.logic.commands.FindTaskCommand; import seedu.address.logic.commands.HelpCommand; import seedu.address.logic.commands.ListPersonCommand; +import seedu.address.logic.commands.ListTagCommand; import seedu.address.logic.commands.ListTaskCommand; import seedu.address.logic.commands.MarkTaskCommand; import seedu.address.logic.commands.UnmarkTaskCommand; @@ -157,6 +158,12 @@ public void parseCommand_deleteAllTask() throws Exception { assertTrue(parser.parseCommand(DeleteAllTaskCommand.COMMAND_WORD + " 3") instanceof DeleteAllTaskCommand); } + @Test + public void parseCommand_listTag() throws Exception { + assertTrue(parser.parseCommand(ListTagCommand.COMMAND_WORD) instanceof ListTagCommand); + assertTrue(parser.parseCommand(ListTagCommand.COMMAND_WORD + " 3") instanceof ListTagCommand); + } + @Test public void parseCommand_unrecognisedInput_throwsParseException() { assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), () From 295ca3d5d8c0ab84690d38514924e356b5ea8b6b Mon Sep 17 00:00:00 2001 From: Alan Lim Date: Wed, 25 Oct 2023 22:33:05 +0800 Subject: [PATCH 06/17] feat: Find tags in persons and tasks --- .../java/seedu/address/logic/Messages.java | 1 + .../logic/commands/FindTagCommand.java | 68 +++++++++++++++++++ .../logic/parser/AddressBookParser.java | 4 ++ .../logic/parser/FindTagCommandParser.java | 35 ++++++++++ .../person/PersonContainsTagsPredicate.java | 47 +++++++++++++ .../model/task/TaskContainsTagsPredicate.java | 47 +++++++++++++ 6 files changed, 202 insertions(+) create mode 100644 src/main/java/seedu/address/logic/commands/FindTagCommand.java create mode 100644 src/main/java/seedu/address/logic/parser/FindTagCommandParser.java create mode 100644 src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java create mode 100644 src/main/java/seedu/address/model/task/TaskContainsTagsPredicate.java diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java index 5582f765940..b67fbfa5ea7 100644 --- a/src/main/java/seedu/address/logic/Messages.java +++ b/src/main/java/seedu/address/logic/Messages.java @@ -19,6 +19,7 @@ public class Messages { public static final String MESSAGE_INVALID_TASK_DISPLAYED_INDEX = "The task index provided is invalid"; public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; public static final String MESSAGE_TASKS_LISTED_OVERVIEW = "%1$d tasks listed!"; + public static final String MESSAGE_PERSONS_TASKS_LISTED_OVERVIEW = "%1$d persons and %1$d tasks listed!"; public static final String MESSAGE_DUPLICATE_FIELDS = "Multiple values specified for the following single-valued field(s): "; diff --git a/src/main/java/seedu/address/logic/commands/FindTagCommand.java b/src/main/java/seedu/address/logic/commands/FindTagCommand.java new file mode 100644 index 00000000000..f0c23cc3cd4 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/FindTagCommand.java @@ -0,0 +1,68 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.Messages; +import seedu.address.model.Model; +import seedu.address.model.person.PersonContainsTagsPredicate; +import seedu.address.model.task.TaskContainsTagsPredicate; + +/** + * Finds and lists all persons and tasks in address book whose tag contains any of the argument keywords. + */ +public class FindTagCommand extends Command { + + public static final String COMMAND_WORD = "findTag"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons and tasks whose tag 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 + " agenda"; + + private final TaskContainsTagsPredicate taskPredicate; + private final PersonContainsTagsPredicate personPredicate; + + /** + * Creates a FindTagCommand to find the specified {@code TaskContainsTagsPredicate} and + * {@code PersonContainsTagsPredicate} + */ + public FindTagCommand(TaskContainsTagsPredicate taskPredicate, PersonContainsTagsPredicate personPredicate) { + this.taskPredicate = taskPredicate; + this.personPredicate = personPredicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredTaskList(taskPredicate); + model.updateFilteredPersonList(personPredicate); + return new CommandResult( + String.format(Messages.MESSAGE_PERSONS_TASKS_LISTED_OVERVIEW, + model.getFilteredPersonList().size(), model.getFilteredTaskList().size())); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof FindTaskCommand)) { + return false; + } + + FindTagCommand otherFindTagCommand = (FindTagCommand) other; + return taskPredicate.equals(otherFindTagCommand.taskPredicate) + && personPredicate.equals(otherFindTagCommand.personPredicate); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("taskPredicate", taskPredicate) + .add("personPredicate", personPredicate) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index bddab16a695..89cb3caabaa 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -19,6 +19,7 @@ import seedu.address.logic.commands.EditTaskCommand; import seedu.address.logic.commands.ExitCommand; import seedu.address.logic.commands.FindPersonCommand; +import seedu.address.logic.commands.FindTagCommand; import seedu.address.logic.commands.FindTaskCommand; import seedu.address.logic.commands.HelpCommand; import seedu.address.logic.commands.ListPersonCommand; @@ -103,6 +104,9 @@ public Command parseCommand(String userInput) throws ParseException { case DeleteAllTaskCommand.COMMAND_WORD: return new DeleteAllTaskCommand(); + case FindTagCommand.COMMAND_WORD: + return new FindTagCommandParser().parse(arguments); + case ExitCommand.COMMAND_WORD: return new ExitCommand(); diff --git a/src/main/java/seedu/address/logic/parser/FindTagCommandParser.java b/src/main/java/seedu/address/logic/parser/FindTagCommandParser.java new file mode 100644 index 00000000000..e8ed94cda5e --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/FindTagCommandParser.java @@ -0,0 +1,35 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import seedu.address.logic.commands.FindTagCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.PersonContainsTagsPredicate; +import seedu.address.model.task.TaskContainsTagsPredicate; + +/** + * Parses input arguments and creates a new FindTagCommand object + */ +public class FindTagCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FindTagCommand + * and returns a FindTagCommand object for execution. + * + * @throws ParseException if the user input does not conform the expected format + */ + public FindTagCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindTagCommand.MESSAGE_USAGE)); + } + + String[] tagKeywords = trimmedArgs.split("\\s+"); + + return new FindTagCommand(new TaskContainsTagsPredicate(Arrays.asList(tagKeywords)), + new PersonContainsTagsPredicate(Arrays.asList(tagKeywords))); + } +} diff --git a/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java b/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java new file mode 100644 index 00000000000..cc3aa9e91fd --- /dev/null +++ b/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java @@ -0,0 +1,47 @@ +package seedu.address.model.person; + +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import seedu.address.commons.util.StringUtil; +import seedu.address.commons.util.ToStringBuilder; + +/** + * Tests that a {@code Person}'s {@code Tags} matches any of the keywords given. + */ +public class PersonContainsTagsPredicate implements Predicate { + private final List keywords; + + public PersonContainsTagsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Person person) { + return keywords.stream() + .anyMatch(keyword -> + StringUtil.containsWordIgnoreCase(person.getTags().stream().map(Object::toString) + .collect(Collectors.joining(", ")), "[" + keyword + "]")); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof PersonContainsTagsPredicate)) { + return false; + } + + PersonContainsTagsPredicate otherPersonContainsTagsPredicate = (PersonContainsTagsPredicate) other; + return keywords.equals(otherPersonContainsTagsPredicate.keywords); + } + + @Override + public String toString() { + return new ToStringBuilder(this).add("keywords", keywords).toString(); + } +} diff --git a/src/main/java/seedu/address/model/task/TaskContainsTagsPredicate.java b/src/main/java/seedu/address/model/task/TaskContainsTagsPredicate.java new file mode 100644 index 00000000000..2a7975769c8 --- /dev/null +++ b/src/main/java/seedu/address/model/task/TaskContainsTagsPredicate.java @@ -0,0 +1,47 @@ +package seedu.address.model.task; + +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import seedu.address.commons.util.StringUtil; +import seedu.address.commons.util.ToStringBuilder; + +/** + * Tests that a {@code Task}'s {@code Tags} matches any of the keywords given. + */ +public class TaskContainsTagsPredicate implements Predicate { + private final List keywords; + + public TaskContainsTagsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Task task) { + return keywords.stream() + .anyMatch(keyword -> + StringUtil.containsWordIgnoreCase(task.getTags().stream().map(Object::toString) + .collect(Collectors.joining(", ")), "[" + keyword + "]")); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof TaskContainsTagsPredicate)) { + return false; + } + + TaskContainsTagsPredicate otherTaskContainsTagsPredicate = (TaskContainsTagsPredicate) other; + return keywords.equals(otherTaskContainsTagsPredicate.keywords); + } + + @Override + public String toString() { + return new ToStringBuilder(this).add("keywords", keywords).toString(); + } +} From 846dce8dd39d3b99351aacf4fe06774a622d7e83 Mon Sep 17 00:00:00 2001 From: Alan Lim Date: Wed, 25 Oct 2023 23:48:10 +0800 Subject: [PATCH 07/17] test: Add tests for new predicates --- .../person/PersonContainsTagsPredicate.java | 7 +- .../model/task/TaskContainsTagsPredicate.java | 2 +- .../logic/commands/FindTagCommandTest.java | 2 + .../PersonContainsTagsPredicateTest.java | 94 +++++++++++++++++++ .../task/TaskContainsTagsPredicateTest.java | 86 +++++++++++++++++ 5 files changed, 187 insertions(+), 4 deletions(-) create mode 100644 src/test/java/seedu/address/logic/commands/FindTagCommandTest.java create mode 100644 src/test/java/seedu/address/model/person/PersonContainsTagsPredicateTest.java create mode 100644 src/test/java/seedu/address/model/task/TaskContainsTagsPredicateTest.java diff --git a/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java b/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java index cc3aa9e91fd..02ba972b61d 100644 --- a/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java +++ b/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java @@ -20,9 +20,10 @@ public PersonContainsTagsPredicate(List keywords) { @Override public boolean test(Person person) { return keywords.stream() - .anyMatch(keyword -> - StringUtil.containsWordIgnoreCase(person.getTags().stream().map(Object::toString) - .collect(Collectors.joining(", ")), "[" + keyword + "]")); + .anyMatch(keyword -> { + return StringUtil.containsWordIgnoreCase(person.getTags().stream().map(Object::toString) + .collect(Collectors.joining(" ")), "[" + keyword + "]"); + }); } @Override diff --git a/src/main/java/seedu/address/model/task/TaskContainsTagsPredicate.java b/src/main/java/seedu/address/model/task/TaskContainsTagsPredicate.java index 2a7975769c8..f4864cc01dd 100644 --- a/src/main/java/seedu/address/model/task/TaskContainsTagsPredicate.java +++ b/src/main/java/seedu/address/model/task/TaskContainsTagsPredicate.java @@ -22,7 +22,7 @@ public boolean test(Task task) { return keywords.stream() .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(task.getTags().stream().map(Object::toString) - .collect(Collectors.joining(", ")), "[" + keyword + "]")); + .collect(Collectors.joining(" ")), "[" + keyword + "]")); } @Override diff --git a/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java b/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java new file mode 100644 index 00000000000..7e2aaa04dfa --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java @@ -0,0 +1,2 @@ +package seedu.address.logic.commands;public class FindTagCommandTest { +} diff --git a/src/test/java/seedu/address/model/person/PersonContainsTagsPredicateTest.java b/src/test/java/seedu/address/model/person/PersonContainsTagsPredicateTest.java new file mode 100644 index 00000000000..9f5f3748af2 --- /dev/null +++ b/src/test/java/seedu/address/model/person/PersonContainsTagsPredicateTest.java @@ -0,0 +1,94 @@ +package seedu.address.model.person; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.testutil.PersonBuilder; + +public class PersonContainsTagsPredicateTest { + + @Test + public void equals() { + List firstPredicateTagList = Collections.singletonList("first"); + List secondPredicateTagList = Arrays.asList("first", "second"); + + PersonContainsTagsPredicate firstPredicate = + new PersonContainsTagsPredicate(firstPredicateTagList); + PersonContainsTagsPredicate secondPredicate = + new PersonContainsTagsPredicate(secondPredicateTagList); + + // same object -> returns true + assertEquals(firstPredicate, firstPredicate); + + // same values -> returns true + PersonContainsTagsPredicate firstPredicateCopy = + new PersonContainsTagsPredicate(firstPredicateTagList); + assertEquals(firstPredicate, firstPredicateCopy); + + // different types -> returns false + assertNotEquals(1, firstPredicate); + assertFalse(firstPredicate.equals(1)); + + // null -> returns false + assertNotEquals(null, firstPredicate); + + // different values -> returns false + assertNotEquals(firstPredicate, secondPredicate); + } + + @Test + public void test_personContainsTags_returnsTrue() { + Person testPerson = new PersonBuilder() + .withName("Alice Bob") + .withTags("Charlie", "Derrick") + .build(); + // One tag + PersonContainsTagsPredicate predicate = + new PersonContainsTagsPredicate(Collections.singletonList("Charlie")); + assertTrue(predicate.test(testPerson)); + + + // Multiple tags + predicate = new PersonContainsTagsPredicate(Arrays.asList("Charlie", "Derrick")); + assertTrue(predicate.test(testPerson)); + + + // Only one matching tag + predicate = new PersonContainsTagsPredicate(Arrays.asList("Charlie", "Invalid")); + assertTrue(predicate.test(testPerson)); + + + // Mixed-case tags + predicate = new PersonContainsTagsPredicate(Arrays.asList("cHarlIE", "DeRRiCk")); + assertTrue(predicate.test(testPerson)); + } + + @Test + public void test_personDoesNotContainTags_returnsFalse() { + // Zero tags + PersonContainsTagsPredicate predicate = new PersonContainsTagsPredicate(Collections.emptyList()); + assertFalse(predicate.test(new PersonBuilder().withName("Alice").withTags("Charlie").build())); + + // Non-matching tag + predicate = new PersonContainsTagsPredicate(Collections.singletonList("Invalid")); + assertFalse(predicate.test(new PersonBuilder().withName("Alice").withTags("Charlie").build())); + } + + @Test + public void toStringMethod() { + List tags = List.of("tag1", "tag2"); + PersonContainsTagsPredicate predicate = new PersonContainsTagsPredicate(tags); + + String expected = new ToStringBuilder(predicate).add("keywords", tags).toString(); + assertEquals(expected, predicate.toString()); + } +} diff --git a/src/test/java/seedu/address/model/task/TaskContainsTagsPredicateTest.java b/src/test/java/seedu/address/model/task/TaskContainsTagsPredicateTest.java new file mode 100644 index 00000000000..c4430ede008 --- /dev/null +++ b/src/test/java/seedu/address/model/task/TaskContainsTagsPredicateTest.java @@ -0,0 +1,86 @@ +package seedu.address.model.task; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.TaskBuilder; + +public class TaskContainsTagsPredicateTest { + + @Test + public void equals() { + List firstPredicateKeywordList = Collections.singletonList("firstTag"); + List secondPredicateKeywordList = Arrays.asList("firstTag", "secondTag"); + + TaskContainsTagsPredicate firstPredicate = + new TaskContainsTagsPredicate(firstPredicateKeywordList); + TaskContainsTagsPredicate secondPredicate = + new TaskContainsTagsPredicate(secondPredicateKeywordList); + + // same object -> returns true + assertEquals(firstPredicate, firstPredicate); + + // same values -> returns true + TaskContainsTagsPredicate firstPredicateCopy = + new TaskContainsTagsPredicate(firstPredicateKeywordList); + assertEquals(firstPredicate, firstPredicateCopy); + + // different types -> returns false + assertNotEquals(1, firstPredicate); + assertFalse(firstPredicate.equals(1)); + + // null -> returns false + assertNotEquals(null, firstPredicate); + + // different values -> returns false + assertNotEquals(firstPredicate, secondPredicate); + } + + @Test + public void test_taskContainsTags_returnsTrue() { + Task testTask = new TaskBuilder().withTags("firstTag", "secondTag").build(); + + // One keyword (tag) + TaskContainsTagsPredicate predicate = + new TaskContainsTagsPredicate(Collections.singletonList("firstTag")); + assertTrue(predicate.test(testTask)); + + // Multiple keywords (tags) + predicate = new TaskContainsTagsPredicate(Arrays.asList("firstTag", "secondTag")); + assertTrue(predicate.test(testTask)); + + // Mixed-case keywords (tags) + predicate = new TaskContainsTagsPredicate(Arrays.asList("FIRSTtag", "SECONDTAG")); + assertTrue(predicate.test(testTask)); + } + + @Test + public void test_taskDoesNotContainTags_returnsFalse() { + Task testTask = new TaskBuilder().withTags("firstTag", "secondTag").build(); + + // Zero keywords (tags) + TaskContainsTagsPredicate predicate = new TaskContainsTagsPredicate(Collections.emptyList()); + assertFalse(predicate.test(testTask)); + + // Non-matching keyword (tag) + predicate = new TaskContainsTagsPredicate(Collections.singletonList("thirdTag")); + assertFalse(predicate.test(testTask)); + } + + @Test + public void toStringMethod() { + List keywords = List.of("tag1", "tag2"); + TaskContainsTagsPredicate predicate = new TaskContainsTagsPredicate(keywords); + + String expected = TaskContainsTagsPredicate.class.getCanonicalName() + "{keywords=" + keywords + "}"; + assertEquals(expected, predicate.toString()); + } +} From cbebac3d69eb5a71cdced1237f57a5082f339e4a Mon Sep 17 00:00:00 2001 From: Alan Lim Date: Thu, 26 Oct 2023 01:23:00 +0800 Subject: [PATCH 08/17] test: Add tests for findTag --- .../logic/commands/FindTagCommand.java | 7 +- .../logic/commands/FindTagCommandTest.java | 134 +++++++++++++++++- 2 files changed, 136 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/address/logic/commands/FindTagCommand.java b/src/main/java/seedu/address/logic/commands/FindTagCommand.java index f0c23cc3cd4..331af02e909 100644 --- a/src/main/java/seedu/address/logic/commands/FindTagCommand.java +++ b/src/main/java/seedu/address/logic/commands/FindTagCommand.java @@ -19,9 +19,8 @@ public class FindTagCommand extends Command { + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" + "Example: " + COMMAND_WORD + " agenda"; - - private final TaskContainsTagsPredicate taskPredicate; private final PersonContainsTagsPredicate personPredicate; + private final TaskContainsTagsPredicate taskPredicate; /** * Creates a FindTagCommand to find the specified {@code TaskContainsTagsPredicate} and @@ -49,7 +48,7 @@ public boolean equals(Object other) { } // instanceof handles nulls - if (!(other instanceof FindTaskCommand)) { + if (!(other instanceof FindTagCommand)) { return false; } @@ -61,8 +60,8 @@ public boolean equals(Object other) { @Override public String toString() { return new ToStringBuilder(this) - .add("taskPredicate", taskPredicate) .add("personPredicate", personPredicate) + .add("taskPredicate", taskPredicate) .toString(); } } diff --git a/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java b/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java index 7e2aaa04dfa..96cda32fc5f 100644 --- a/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java @@ -1,2 +1,134 @@ -package seedu.address.logic.commands;public class FindTagCommandTest { +package seedu.address.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static seedu.address.logic.Messages.MESSAGE_PERSONS_TASKS_LISTED_OVERVIEW; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalPersons.*; +import static seedu.address.testutil.TypicalTasks.*; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.person.Person; +import seedu.address.model.person.PersonContainsTagsPredicate; +import seedu.address.model.task.Task; +import seedu.address.model.task.TaskContainsTagsPredicate; + + +public class FindTagCommandTest { + private final Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + private final Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + + @Test + public void equals() { + TaskContainsTagsPredicate firstTaskPredicate = + new TaskContainsTagsPredicate(Collections.singletonList("firstTag")); + TaskContainsTagsPredicate secondTaskPredicate = + new TaskContainsTagsPredicate(Collections.singletonList("secondTag")); + + PersonContainsTagsPredicate firstPersonPredicate = + new PersonContainsTagsPredicate(Collections.singletonList("firstTag")); + PersonContainsTagsPredicate secondPersonPredicate = + new PersonContainsTagsPredicate(Collections.singletonList("secondTag")); + + FindTagCommand firstFindTagCommand = new FindTagCommand(firstTaskPredicate, firstPersonPredicate); + FindTagCommand secondFindTagCommand = new FindTagCommand(secondTaskPredicate, secondPersonPredicate); + + // same object -> returns true + assertEquals(firstFindTagCommand, firstFindTagCommand); + System.out.println("firstFindTagCommand: " + firstFindTagCommand); + // same values -> returns true + FindTagCommand firstFindTagCommandCopy = new FindTagCommand(firstTaskPredicate, firstPersonPredicate); + assertEquals(firstFindTagCommand, firstFindTagCommandCopy); + System.out.println("firstFindTagCommandCopy: " + firstFindTagCommandCopy); + // different types -> returns false + assertNotEquals(firstFindTagCommand, 1); + assertNotEquals(1, firstFindTagCommand); + + // null -> returns false + assertNotEquals(null, firstFindTagCommand); + + // different tag -> returns false + assertNotEquals(firstFindTagCommand, secondFindTagCommand); + } + + @Test + public void execute_zeroTags_noPersonsTasksFound() { + String expectedMessage = String.format(MESSAGE_PERSONS_TASKS_LISTED_OVERVIEW, 0, 0); + TaskContainsTagsPredicate taskPredicate = prepareTaskTagPredicate(" "); + PersonContainsTagsPredicate personPredicate = preparePersonTagPredicate(" "); + FindTagCommand command = new FindTagCommand(taskPredicate, personPredicate); + expectedModel.updateFilteredTaskList(taskPredicate); + expectedModel.updateFilteredPersonList(personPredicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getFilteredTaskList()); + assertEquals(Collections.emptyList(), model.getFilteredPersonList()); + } + + @Test + public void toStringMethod() { + TaskContainsTagsPredicate taskPredicate = new TaskContainsTagsPredicate(List.of("tag1")); + PersonContainsTagsPredicate personPredicate = new PersonContainsTagsPredicate(List.of("tag2")); + FindTagCommand findTagCommand = new FindTagCommand(taskPredicate, personPredicate); + String expected = FindTagCommand.class.getCanonicalName() + + "{personPredicate=" + personPredicate + ", taskPredicate=" + taskPredicate + "}"; + assertEquals(expected, findTagCommand.toString()); + } + + @Test + public void execute_multipleTags_multiplePersonsTasksFound() { + // Define some tasks and persons with tags that we can search for in our tests. + TaskContainsTagsPredicate taskPredicate = prepareTaskTagPredicate("finance"); + PersonContainsTagsPredicate personPredicate = preparePersonTagPredicate("friends"); + + FindTagCommand command = new FindTagCommand(taskPredicate, personPredicate); + expectedModel.updateFilteredTaskList(taskPredicate); + expectedModel.updateFilteredPersonList(personPredicate); + + String expectedMessage = String.format(MESSAGE_PERSONS_TASKS_LISTED_OVERVIEW, 3, 3); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + + // For tasks with tags "location" and "finance", we have AGENDA and BUDGET from the TypicalTasks. + assertEquals(Arrays.asList(BUDGET, DRAFT, FUNDING), model.getFilteredTaskList()); + + // For persons with tags "friends" and "owesMoney", we have ALICE, BENSON, and DANIEL from the TypicalPersons. + assertEquals(Arrays.asList(ALICE, BENSON, DANIEL), model.getFilteredPersonList()); + } + + /** + * Parses {@code userInput} into a {@code TaskContainsTagsPredicate}. + */ + private TaskContainsTagsPredicate prepareTaskTagPredicate(String userInput) { + return new TaskContainsTagsPredicate(Arrays.asList(userInput.split("\\s+"))); + } + + /** + * Parses {@code userInput} into a {@code PersonContainsTagsPredicate}. + */ + private PersonContainsTagsPredicate preparePersonTagPredicate(String userInput) { + return new PersonContainsTagsPredicate(Arrays.asList(userInput.split("\\s+"))); + } + + public static AddressBook getTypicalAddressBook() { + AddressBook ab = new AddressBook(); + + for (Task task : getTypicalTasks()) { + ab.addTask(task); + } + + for (Person person : getTypicalPersons()) { + ab.addPerson(person); + } + + return ab; + } + } From dbb8c2a45860ea521b856c41d095407209c92ea0 Mon Sep 17 00:00:00 2001 From: Alan Lim Date: Thu, 26 Oct 2023 01:26:39 +0800 Subject: [PATCH 09/17] refactor: Reorder method signature for findTag --- .../java/seedu/address/logic/Messages.java | 2 +- .../logic/commands/FindTagCommand.java | 2 +- .../logic/parser/FindTagCommandParser.java | 5 +++-- .../person/PersonContainsTagsPredicate.java | 6 ++--- .../logic/commands/FindTagCommandTest.java | 22 ++++++++++++------- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java index b67fbfa5ea7..d62722c9663 100644 --- a/src/main/java/seedu/address/logic/Messages.java +++ b/src/main/java/seedu/address/logic/Messages.java @@ -19,7 +19,7 @@ public class Messages { public static final String MESSAGE_INVALID_TASK_DISPLAYED_INDEX = "The task index provided is invalid"; public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; public static final String MESSAGE_TASKS_LISTED_OVERVIEW = "%1$d tasks listed!"; - public static final String MESSAGE_PERSONS_TASKS_LISTED_OVERVIEW = "%1$d persons and %1$d tasks listed!"; + public static final String MESSAGE_PERSONS_TASKS_LISTED_OVERVIEW = "%1$d persons and %2$d tasks listed!"; public static final String MESSAGE_DUPLICATE_FIELDS = "Multiple values specified for the following single-valued field(s): "; diff --git a/src/main/java/seedu/address/logic/commands/FindTagCommand.java b/src/main/java/seedu/address/logic/commands/FindTagCommand.java index 331af02e909..f38cc346d53 100644 --- a/src/main/java/seedu/address/logic/commands/FindTagCommand.java +++ b/src/main/java/seedu/address/logic/commands/FindTagCommand.java @@ -26,7 +26,7 @@ public class FindTagCommand extends Command { * Creates a FindTagCommand to find the specified {@code TaskContainsTagsPredicate} and * {@code PersonContainsTagsPredicate} */ - public FindTagCommand(TaskContainsTagsPredicate taskPredicate, PersonContainsTagsPredicate personPredicate) { + public FindTagCommand(PersonContainsTagsPredicate personPredicate, TaskContainsTagsPredicate taskPredicate) { this.taskPredicate = taskPredicate; this.personPredicate = personPredicate; } diff --git a/src/main/java/seedu/address/logic/parser/FindTagCommandParser.java b/src/main/java/seedu/address/logic/parser/FindTagCommandParser.java index e8ed94cda5e..0b4ee1fa882 100644 --- a/src/main/java/seedu/address/logic/parser/FindTagCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/FindTagCommandParser.java @@ -29,7 +29,8 @@ public FindTagCommand parse(String args) throws ParseException { String[] tagKeywords = trimmedArgs.split("\\s+"); - return new FindTagCommand(new TaskContainsTagsPredicate(Arrays.asList(tagKeywords)), - new PersonContainsTagsPredicate(Arrays.asList(tagKeywords))); + return new FindTagCommand(new PersonContainsTagsPredicate(Arrays.asList(tagKeywords)), + new TaskContainsTagsPredicate(Arrays.asList(tagKeywords)) + ); } } diff --git a/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java b/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java index 02ba972b61d..41a41e9cfc3 100644 --- a/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java +++ b/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java @@ -20,10 +20,8 @@ public PersonContainsTagsPredicate(List keywords) { @Override public boolean test(Person person) { return keywords.stream() - .anyMatch(keyword -> { - return StringUtil.containsWordIgnoreCase(person.getTags().stream().map(Object::toString) - .collect(Collectors.joining(" ")), "[" + keyword + "]"); - }); + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getTags().stream().map(Object::toString) + .collect(Collectors.joining(" ")), "[" + keyword + "]")); } @Override diff --git a/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java b/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java index 96cda32fc5f..2288a051f28 100644 --- a/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java @@ -4,8 +4,14 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static seedu.address.logic.Messages.MESSAGE_PERSONS_TASKS_LISTED_OVERVIEW; import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.*; -import static seedu.address.testutil.TypicalTasks.*; +import static seedu.address.testutil.TypicalPersons.ALICE; +import static seedu.address.testutil.TypicalPersons.BENSON; +import static seedu.address.testutil.TypicalPersons.DANIEL; +import static seedu.address.testutil.TypicalPersons.getTypicalPersons; +import static seedu.address.testutil.TypicalTasks.BUDGET; +import static seedu.address.testutil.TypicalTasks.DRAFT; +import static seedu.address.testutil.TypicalTasks.FUNDING; +import static seedu.address.testutil.TypicalTasks.getTypicalTasks; import java.util.Arrays; import java.util.Collections; @@ -39,14 +45,14 @@ public void equals() { PersonContainsTagsPredicate secondPersonPredicate = new PersonContainsTagsPredicate(Collections.singletonList("secondTag")); - FindTagCommand firstFindTagCommand = new FindTagCommand(firstTaskPredicate, firstPersonPredicate); - FindTagCommand secondFindTagCommand = new FindTagCommand(secondTaskPredicate, secondPersonPredicate); + FindTagCommand firstFindTagCommand = new FindTagCommand(firstPersonPredicate, firstTaskPredicate); + FindTagCommand secondFindTagCommand = new FindTagCommand(secondPersonPredicate, secondTaskPredicate); // same object -> returns true assertEquals(firstFindTagCommand, firstFindTagCommand); System.out.println("firstFindTagCommand: " + firstFindTagCommand); // same values -> returns true - FindTagCommand firstFindTagCommandCopy = new FindTagCommand(firstTaskPredicate, firstPersonPredicate); + FindTagCommand firstFindTagCommandCopy = new FindTagCommand(firstPersonPredicate, firstTaskPredicate); assertEquals(firstFindTagCommand, firstFindTagCommandCopy); System.out.println("firstFindTagCommandCopy: " + firstFindTagCommandCopy); // different types -> returns false @@ -65,7 +71,7 @@ public void execute_zeroTags_noPersonsTasksFound() { String expectedMessage = String.format(MESSAGE_PERSONS_TASKS_LISTED_OVERVIEW, 0, 0); TaskContainsTagsPredicate taskPredicate = prepareTaskTagPredicate(" "); PersonContainsTagsPredicate personPredicate = preparePersonTagPredicate(" "); - FindTagCommand command = new FindTagCommand(taskPredicate, personPredicate); + FindTagCommand command = new FindTagCommand(personPredicate, taskPredicate); expectedModel.updateFilteredTaskList(taskPredicate); expectedModel.updateFilteredPersonList(personPredicate); assertCommandSuccess(command, model, expectedMessage, expectedModel); @@ -77,7 +83,7 @@ public void execute_zeroTags_noPersonsTasksFound() { public void toStringMethod() { TaskContainsTagsPredicate taskPredicate = new TaskContainsTagsPredicate(List.of("tag1")); PersonContainsTagsPredicate personPredicate = new PersonContainsTagsPredicate(List.of("tag2")); - FindTagCommand findTagCommand = new FindTagCommand(taskPredicate, personPredicate); + FindTagCommand findTagCommand = new FindTagCommand(personPredicate, taskPredicate); String expected = FindTagCommand.class.getCanonicalName() + "{personPredicate=" + personPredicate + ", taskPredicate=" + taskPredicate + "}"; assertEquals(expected, findTagCommand.toString()); @@ -89,7 +95,7 @@ public void execute_multipleTags_multiplePersonsTasksFound() { TaskContainsTagsPredicate taskPredicate = prepareTaskTagPredicate("finance"); PersonContainsTagsPredicate personPredicate = preparePersonTagPredicate("friends"); - FindTagCommand command = new FindTagCommand(taskPredicate, personPredicate); + FindTagCommand command = new FindTagCommand(personPredicate, taskPredicate); expectedModel.updateFilteredTaskList(taskPredicate); expectedModel.updateFilteredPersonList(personPredicate); From 3ae9740ed3b6a7a6cd3ff3e800e2889a6db2683d Mon Sep 17 00:00:00 2001 From: Alan Lim Date: Thu, 26 Oct 2023 01:30:12 +0800 Subject: [PATCH 10/17] feat: Add tests for findTag parser --- .../parser/FindTagCommandParserTest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/test/java/seedu/address/logic/parser/FindTagCommandParserTest.java diff --git a/src/test/java/seedu/address/logic/parser/FindTagCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindTagCommandParserTest.java new file mode 100644 index 00000000000..f699b307921 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/FindTagCommandParserTest.java @@ -0,0 +1,39 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.FindTagCommand; +import seedu.address.model.person.PersonContainsTagsPredicate; +import seedu.address.model.task.TaskContainsTagsPredicate; + + +class FindTagCommandParserTest { + private final FindTagCommandParser parser = new FindTagCommandParser(); + + @Test + public void parse_validArgs_returnsFindTagCommand() { + // Only one tag keyword + FindTagCommand expectedFindTagCommand = + new FindTagCommand(new PersonContainsTagsPredicate(Arrays.asList("friends")), + new TaskContainsTagsPredicate(Arrays.asList("friends"))); + assertParseSuccess(parser, "friends", expectedFindTagCommand); + + // Multiple tag keywords separated by whitespace + expectedFindTagCommand = + new FindTagCommand(new PersonContainsTagsPredicate(Arrays.asList("friends", "owesMoney")), + new TaskContainsTagsPredicate(Arrays.asList("friends", "owesMoney"))); + assertParseSuccess(parser, "friends owesMoney", expectedFindTagCommand); + } + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindTagCommand.MESSAGE_USAGE)); + } +} From 92beb75587b53c0b3f5b271432c1f29c20b89792 Mon Sep 17 00:00:00 2001 From: Alan Lim Date: Thu, 26 Oct 2023 01:44:43 +0800 Subject: [PATCH 11/17] test: Add test in addressbook --- .../logic/parser/AddressBookParserTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java index 04ccdebb848..6c18b5f9c21 100644 --- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java +++ b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java @@ -24,6 +24,7 @@ import seedu.address.logic.commands.EditTaskCommand.EditTaskDescriptor; import seedu.address.logic.commands.ExitCommand; import seedu.address.logic.commands.FindPersonCommand; +import seedu.address.logic.commands.FindTagCommand; import seedu.address.logic.commands.FindTaskCommand; import seedu.address.logic.commands.HelpCommand; import seedu.address.logic.commands.ListPersonCommand; @@ -33,8 +34,10 @@ import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.person.NameContainsKeywordsPredicate; import seedu.address.model.person.Person; +import seedu.address.model.person.PersonContainsTagsPredicate; import seedu.address.model.task.Task; import seedu.address.model.task.TaskContainsKeywordsPredicate; +import seedu.address.model.task.TaskContainsTagsPredicate; import seedu.address.testutil.EditPersonDescriptorBuilder; import seedu.address.testutil.EditTaskDescriptorBuilder; import seedu.address.testutil.PersonBuilder; @@ -122,6 +125,17 @@ public void parseCommand_findTask() throws Exception { assertEquals(new FindTaskCommand(new TaskContainsKeywordsPredicate(keywords)), command); } + @Test + public void parseCommand_findTag() throws Exception { + List tagKeywords = Arrays.asList("tag1", "tag2", "tag3"); + FindTagCommand command = (FindTagCommand) parser.parseCommand( + FindTagCommand.COMMAND_WORD + " " + String.join(" ", tagKeywords)); + assertEquals(new FindTagCommand( + new PersonContainsTagsPredicate(tagKeywords), + new TaskContainsTagsPredicate(tagKeywords)), + command); + } + @Test public void parseCommand_markTask() throws Exception { MarkTaskCommand command = (MarkTaskCommand) parser.parseCommand( From 3083fd09a5ce8190f4c33b4cf997b4b27156f77e Mon Sep 17 00:00:00 2001 From: Alan Lim Date: Thu, 26 Oct 2023 01:48:26 +0800 Subject: [PATCH 12/17] refactor: Remove unnecessary debugging statements --- .../java/seedu/address/logic/commands/FindTagCommandTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java b/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java index 2288a051f28..875d8df86ef 100644 --- a/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java @@ -50,11 +50,11 @@ public void equals() { // same object -> returns true assertEquals(firstFindTagCommand, firstFindTagCommand); - System.out.println("firstFindTagCommand: " + firstFindTagCommand); + // same values -> returns true FindTagCommand firstFindTagCommandCopy = new FindTagCommand(firstPersonPredicate, firstTaskPredicate); assertEquals(firstFindTagCommand, firstFindTagCommandCopy); - System.out.println("firstFindTagCommandCopy: " + firstFindTagCommandCopy); + // different types -> returns false assertNotEquals(firstFindTagCommand, 1); assertNotEquals(1, firstFindTagCommand); From 5594348ee0fb435d671d965a6b0cdc10692f48b3 Mon Sep 17 00:00:00 2001 From: Alan Lim Date: Thu, 26 Oct 2023 01:57:02 +0800 Subject: [PATCH 13/17] refactor: Remove unnecessary comments --- .../java/seedu/address/logic/commands/FindTagCommandTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java b/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java index 875d8df86ef..27c7897f162 100644 --- a/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/FindTagCommandTest.java @@ -102,10 +102,8 @@ public void execute_multipleTags_multiplePersonsTasksFound() { String expectedMessage = String.format(MESSAGE_PERSONS_TASKS_LISTED_OVERVIEW, 3, 3); assertCommandSuccess(command, model, expectedMessage, expectedModel); - // For tasks with tags "location" and "finance", we have AGENDA and BUDGET from the TypicalTasks. assertEquals(Arrays.asList(BUDGET, DRAFT, FUNDING), model.getFilteredTaskList()); - // For persons with tags "friends" and "owesMoney", we have ALICE, BENSON, and DANIEL from the TypicalPersons. assertEquals(Arrays.asList(ALICE, BENSON, DANIEL), model.getFilteredPersonList()); } From 482304111052d066dc7360f6816c13e97cc2d7c1 Mon Sep 17 00:00:00 2001 From: m1oojv <97022614+m1oojv@users.noreply.github.com> Date: Fri, 27 Oct 2023 12:32:29 +0800 Subject: [PATCH 14/17] chore: Add assertions to build gradle file --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index a2951cc709e..98c44b0dc9b 100644 --- a/build.gradle +++ b/build.gradle @@ -20,6 +20,10 @@ checkstyle { toolVersion = '10.2' } +run { + enableAssertions = true +} + test { useJUnitPlatform() finalizedBy jacocoTestReport From 47bad8dcc90a7a5eec0a4a6ecdc4686cc97e9d96 Mon Sep 17 00:00:00 2001 From: jiakai-17 <111171022+jiakai-17@users.noreply.github.com> Date: Fri, 27 Oct 2023 12:37:20 +0800 Subject: [PATCH 15/17] refactor: Rename variable to be more explicit --- .../address/model/tag/TagFrequencyTable.java | 54 +++++++++---------- .../model/tag/TagFrequencyTableTest.java | 24 ++++----- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/main/java/seedu/address/model/tag/TagFrequencyTable.java b/src/main/java/seedu/address/model/tag/TagFrequencyTable.java index 259e58c80dd..5277a030f35 100644 --- a/src/main/java/seedu/address/model/tag/TagFrequencyTable.java +++ b/src/main/java/seedu/address/model/tag/TagFrequencyTable.java @@ -16,9 +16,9 @@ */ public class TagFrequencyTable { - private final Map personTagFrequency; - private final Map taskTagFrequency; - private final Map combinedTagFrequency; + private final Map personTagFrequencyTable; + private final Map taskTagFrequencyTable; + private final Map combinedTagFrequencyTable; /** * Constructs a new {@code TagFrequencyTable} from a list of {@code Person}s and {@code Task}s. @@ -27,9 +27,9 @@ public class TagFrequencyTable { * @param taskList List of {@code Task}s. */ public TagFrequencyTable(List personList, List taskList) { - this.personTagFrequency = new HashMap<>(); - this.taskTagFrequency = new HashMap<>(); - this.combinedTagFrequency = new HashMap<>(); + this.personTagFrequencyTable = new HashMap<>(); + this.taskTagFrequencyTable = new HashMap<>(); + this.combinedTagFrequencyTable = new HashMap<>(); personList.forEach(this::processPerson); taskList.forEach(this::processTask); @@ -42,8 +42,8 @@ public TagFrequencyTable(List personList, List taskList) { */ private void processPerson(Person p) { p.getTags().forEach(tag -> { - personTagFrequency.put(tag, personTagFrequency.getOrDefault(tag, 0) + 1); - combinedTagFrequency.put(tag, combinedTagFrequency.getOrDefault(tag, 0) + 1); + personTagFrequencyTable.put(tag, personTagFrequencyTable.getOrDefault(tag, 0) + 1); + combinedTagFrequencyTable.put(tag, combinedTagFrequencyTable.getOrDefault(tag, 0) + 1); }); } @@ -54,21 +54,21 @@ private void processPerson(Person p) { */ private void processTask(Task t) { t.getTags().forEach(tag -> { - taskTagFrequency.put(tag, taskTagFrequency.getOrDefault(tag, 0) + 1); - combinedTagFrequency.put(tag, combinedTagFrequency.getOrDefault(tag, 0) + 1); + taskTagFrequencyTable.put(tag, taskTagFrequencyTable.getOrDefault(tag, 0) + 1); + combinedTagFrequencyTable.put(tag, combinedTagFrequencyTable.getOrDefault(tag, 0) + 1); }); } - public Map getPersonTagFrequency() { - return new HashMap<>(personTagFrequency); + public Map getPersonTagFrequencyTable() { + return new HashMap<>(personTagFrequencyTable); } - public Map getTaskTagFrequency() { - return new HashMap<>(taskTagFrequency); + public Map getTaskTagFrequencyTable() { + return new HashMap<>(taskTagFrequencyTable); } - public Map getCombinedTagFrequency() { - return new HashMap<>(combinedTagFrequency); + public Map getCombinedTagFrequencyTable() { + return new HashMap<>(combinedTagFrequencyTable); } /** @@ -88,14 +88,14 @@ public String format() { ? e1.getKey().compareTo(e2.getKey()) : e2.getValue().compareTo(e1.getValue()); - combinedTagFrequency.entrySet() + combinedTagFrequencyTable.entrySet() .stream() .sorted(comparator) .map(entry -> String.format("%s: %d items (%d Persons, %d Tasks)", entry.getKey(), entry.getValue(), - personTagFrequency.getOrDefault(entry.getKey(), 0), - taskTagFrequency.getOrDefault(entry.getKey(), 0))) + personTagFrequencyTable.getOrDefault(entry.getKey(), 0), + taskTagFrequencyTable.getOrDefault(entry.getKey(), 0))) .forEach(formattedTagList::add); return String.join("\n", formattedTagList); @@ -107,15 +107,15 @@ public String format() { * @return True if the tag frequency table is empty. */ public boolean isEmpty() { - return combinedTagFrequency.isEmpty(); + return combinedTagFrequencyTable.isEmpty(); } @Override public String toString() { return new ToStringBuilder(this) - .add("personTagFrequency", personTagFrequency) - .add("taskTagFrequency", taskTagFrequency) - .add("combinedTagFrequency", combinedTagFrequency) + .add("personTagFrequencyTable", personTagFrequencyTable) + .add("taskTagFrequencyTable", taskTagFrequencyTable) + .add("combinedTagFrequencyTable", combinedTagFrequencyTable) .toString(); } @@ -131,13 +131,13 @@ public boolean equals(Object other) { } TagFrequencyTable otherTable = (TagFrequencyTable) other; - return personTagFrequency.equals(otherTable.personTagFrequency) - && taskTagFrequency.equals(otherTable.taskTagFrequency) - && combinedTagFrequency.equals(otherTable.combinedTagFrequency); + return personTagFrequencyTable.equals(otherTable.personTagFrequencyTable) + && taskTagFrequencyTable.equals(otherTable.taskTagFrequencyTable) + && combinedTagFrequencyTable.equals(otherTable.combinedTagFrequencyTable); } @Override public int hashCode() { - return Objects.hash(personTagFrequency, taskTagFrequency, combinedTagFrequency); + return Objects.hash(personTagFrequencyTable, taskTagFrequencyTable, combinedTagFrequencyTable); } } diff --git a/src/test/java/seedu/address/model/tag/TagFrequencyTableTest.java b/src/test/java/seedu/address/model/tag/TagFrequencyTableTest.java index a82c8dff1db..9f3c017753b 100644 --- a/src/test/java/seedu/address/model/tag/TagFrequencyTableTest.java +++ b/src/test/java/seedu/address/model/tag/TagFrequencyTableTest.java @@ -41,24 +41,24 @@ static void beforeAll() { } @Test - public void getPersonTagFrequency_addEntry_notModified() { - Map modifiedMap = tagFrequencyTable.getPersonTagFrequency(); + public void getPersonTagFrequencyTable_addEntry_notModified() { + Map modifiedMap = tagFrequencyTable.getPersonTagFrequencyTable(); modifiedMap.put(new Tag("modifiedEntry"), 1); - assertNotEquals(modifiedMap, tagFrequencyTable.getPersonTagFrequency()); + assertNotEquals(modifiedMap, tagFrequencyTable.getPersonTagFrequencyTable()); } @Test - public void getTaskTagFrequency_addEntry_notModified() { - Map modifiedMap = tagFrequencyTable.getTaskTagFrequency(); + public void getTaskTagFrequencyTable_addEntry_notModified() { + Map modifiedMap = tagFrequencyTable.getTaskTagFrequencyTable(); modifiedMap.put(new Tag("modifiedEntry"), 1); - assertNotEquals(modifiedMap, tagFrequencyTable.getTaskTagFrequency()); + assertNotEquals(modifiedMap, tagFrequencyTable.getTaskTagFrequencyTable()); } @Test - public void getCombinedTagFrequency_addEntry_notModified() { - Map modifiedMap = tagFrequencyTable.getCombinedTagFrequency(); + public void getCombinedTagFrequencyTable_addEntry_notModified() { + Map modifiedMap = tagFrequencyTable.getCombinedTagFrequencyTable(); modifiedMap.put(new Tag("modifiedEntry"), 1); - assertNotEquals(modifiedMap, tagFrequencyTable.getCombinedTagFrequency()); + assertNotEquals(modifiedMap, tagFrequencyTable.getCombinedTagFrequencyTable()); } @Test @@ -89,9 +89,9 @@ public void toStringMethod() { // check format String expected = TagFrequencyTable.class.getCanonicalName() - + "{personTagFrequency=" + tagFrequencyTable.getPersonTagFrequency() - + ", taskTagFrequency=" + tagFrequencyTable.getTaskTagFrequency() - + ", combinedTagFrequency=" + tagFrequencyTable.getCombinedTagFrequency() + + "{personTagFrequencyTable=" + tagFrequencyTable.getPersonTagFrequencyTable() + + ", taskTagFrequencyTable=" + tagFrequencyTable.getTaskTagFrequencyTable() + + ", combinedTagFrequencyTable=" + tagFrequencyTable.getCombinedTagFrequencyTable() + "}"; assertEquals(expected, tagFrequencyTable.toString()); } From c2d9612f6771443f3bf904c6a402216f624b6132 Mon Sep 17 00:00:00 2001 From: freshcabbage123 Date: Fri, 27 Oct 2023 12:41:58 +0800 Subject: [PATCH 16/17] fix: Improve efficiency of predicates' test logic --- .../person/PersonContainsTagsPredicate.java | 16 ++++++++++++++-- .../model/task/TaskContainsTagsPredicate.java | 17 ++++++++++++++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java b/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java index 41a41e9cfc3..88858e714bd 100644 --- a/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java +++ b/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java @@ -17,11 +17,23 @@ public PersonContainsTagsPredicate(List keywords) { this.keywords = keywords; } + // Returns true if person's tags contain any of the keywords @Override public boolean test(Person person) { + + // Convert each tag of the person to its string representation and then concatenate them using spaces. + // The square brackets encapsulate each tag to ensure distinctiveness when checking against keywords. + // For example, tags such as [Engineer, Developer] will translate to the string "[Engineer] [Developer]". + String personTags = person.getTags() + .stream() + .map(Object::toString) + .collect(Collectors.joining(" ")); + + // Match against the concatenated string of tags. + // The square brackets ensure that keywords match against full tags rather than partial matches. + // This way, a keyword like "[Eng]" won't match with the tag "[Engineer]". return keywords.stream() - .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getTags().stream().map(Object::toString) - .collect(Collectors.joining(" ")), "[" + keyword + "]")); + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(personTags, keyword)); } @Override diff --git a/src/main/java/seedu/address/model/task/TaskContainsTagsPredicate.java b/src/main/java/seedu/address/model/task/TaskContainsTagsPredicate.java index f4864cc01dd..3bf9818453a 100644 --- a/src/main/java/seedu/address/model/task/TaskContainsTagsPredicate.java +++ b/src/main/java/seedu/address/model/task/TaskContainsTagsPredicate.java @@ -17,12 +17,23 @@ public TaskContainsTagsPredicate(List keywords) { this.keywords = keywords; } + // Returns true if task's tags contain any of the keywords @Override public boolean test(Task task) { + + // Convert each tag of the task to its string representation and then concatenate them using spaces. + // The square brackets encapsulate each tag to ensure distinctiveness when checking against keywords. + // For example, tags such as [Homework, Urgent] will translate to the string "[Homework] [Urgent]". + String taskTags = task.getTags() + .stream() + .map(Object::toString) + .collect(Collectors.joining(" ")); + + // Match against the concatenated string of tags. + // The square brackets ensure that keywords match against full tags rather than partial matches. + // This way, a keyword like "[Home]" won't match with the tag "[Homework]". return keywords.stream() - .anyMatch(keyword -> - StringUtil.containsWordIgnoreCase(task.getTags().stream().map(Object::toString) - .collect(Collectors.joining(" ")), "[" + keyword + "]")); + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(taskTags, "[" + keyword + "]")); } @Override From 2a19bcad72baf146fb90c4d5811ddfeec6b49115 Mon Sep 17 00:00:00 2001 From: freshcabbage123 Date: Fri, 27 Oct 2023 12:45:32 +0800 Subject: [PATCH 17/17] fix: Fix mistake of removing square braces --- .../address/model/person/PersonContainsTagsPredicate.java | 4 ++-- .../seedu/address/model/task/TaskContainsTagsPredicate.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java b/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java index 88858e714bd..d053c59d372 100644 --- a/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java +++ b/src/main/java/seedu/address/model/person/PersonContainsTagsPredicate.java @@ -23,7 +23,7 @@ public boolean test(Person person) { // Convert each tag of the person to its string representation and then concatenate them using spaces. // The square brackets encapsulate each tag to ensure distinctiveness when checking against keywords. - // For example, tags such as [Engineer, Developer] will translate to the string "[Engineer] [Developer]". + // For example, tags such as [[Engineer], [Developer]] will translate to the string "[Engineer] [Developer]". String personTags = person.getTags() .stream() .map(Object::toString) @@ -33,7 +33,7 @@ public boolean test(Person person) { // The square brackets ensure that keywords match against full tags rather than partial matches. // This way, a keyword like "[Eng]" won't match with the tag "[Engineer]". return keywords.stream() - .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(personTags, keyword)); + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(personTags, "[" + keyword + "]")); } @Override diff --git a/src/main/java/seedu/address/model/task/TaskContainsTagsPredicate.java b/src/main/java/seedu/address/model/task/TaskContainsTagsPredicate.java index 3bf9818453a..a013af44c53 100644 --- a/src/main/java/seedu/address/model/task/TaskContainsTagsPredicate.java +++ b/src/main/java/seedu/address/model/task/TaskContainsTagsPredicate.java @@ -23,7 +23,7 @@ public boolean test(Task task) { // Convert each tag of the task to its string representation and then concatenate them using spaces. // The square brackets encapsulate each tag to ensure distinctiveness when checking against keywords. - // For example, tags such as [Homework, Urgent] will translate to the string "[Homework] [Urgent]". + // For example, tags such as [[Homework], [Urgent]] will translate to the string "[Homework] [Urgent]". String taskTags = task.getTags() .stream() .map(Object::toString)