diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 8cf4ab68e9e..5abab2fd969 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -11,51 +11,51 @@ You can reach us at the email `seer[at]comp.nus.edu.sg`
## Project team
-### John Doe
+### Kieron Seven Lee Jun Wei
-
+
-[[homepage](http://www.comp.nus.edu.sg/~damithch)]
-[[github](https://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](https://github.com/cikguseven)]
+[[portfolio](team/cikguseven.md)]
* Role: Project Advisor
-### Jane Doe
+### Liang Wenzhong
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/lwz19)]
+[[portfolio](team/lwz19.md)]
* Role: Team Lead
* Responsibilities: UI
-### Johnny Doe
+### Lim Ngee Yong
-
+
-[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)]
+[[github](http://github.com/ngeeyonglim)]
+[[portfolio](team/ngeeyonglim.md)]
* Role: Developer
* Responsibilities: Data
-### Jean Doe
+### Qian Changru
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/changruhenryqian)]
+[[portfolio](team/changruhenryqian.md)]
* Role: Developer
* Responsibilities: Dev Ops + Threading
-### James Doe
+### Yeo Boh Shin
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/yeobohshin)]
+[[portfolio](team/yeobohshin.md)]
* Role: Developer
* Responsibilities: UI
diff --git a/docs/images/changruhenryqian.png b/docs/images/changruhenryqian.png
new file mode 100644
index 00000000000..8218d07bca8
Binary files /dev/null and b/docs/images/changruhenryqian.png differ
diff --git a/docs/images/cikguseven.png b/docs/images/cikguseven.png
new file mode 100644
index 00000000000..0ce80ee9cb1
Binary files /dev/null and b/docs/images/cikguseven.png differ
diff --git a/docs/images/lwz19.png b/docs/images/lwz19.png
new file mode 100644
index 00000000000..56cfb7957d8
Binary files /dev/null and b/docs/images/lwz19.png differ
diff --git a/docs/images/ngeeyonglim.png b/docs/images/ngeeyonglim.png
new file mode 100644
index 00000000000..3595a623809
Binary files /dev/null and b/docs/images/ngeeyonglim.png differ
diff --git a/docs/images/yeobohshin.png b/docs/images/yeobohshin.png
new file mode 100644
index 00000000000..fdab851212a
Binary files /dev/null and b/docs/images/yeobohshin.png differ
diff --git a/docs/team/changruhenryqian.md b/docs/team/changruhenryqian.md
new file mode 100644
index 00000000000..4d0f48f24f2
--- /dev/null
+++ b/docs/team/changruhenryqian.md
@@ -0,0 +1,18 @@
+# Class Manager 2023
+
+*Class Manager 2023 aims to provide fast access to CS2103S TAs who need help in maintaining student information across multiple classes. It also aims to help TAs visualize data across and within classes.*
+
+## Summary of Contributions
+to be added soon
+
+### Code Contributed
+[Individual Dashboard](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=changruhenryqian&breakdown=true)
+
+### Enhancements Implemented
+to be added soon
+
+### Contributions to the UG
+to be added soon
+
+### Contributions beyond the Project
+to be added soon
diff --git a/docs/team/cikguseven.md b/docs/team/cikguseven.md
index 164a94353dc..64a5728a21c 100644
--- a/docs/team/cikguseven.md
+++ b/docs/team/cikguseven.md
@@ -9,7 +9,7 @@ Class Manager 2023 aims to provide fast access to CS2103T TAs who need help in m
Given below are my contributions to the project:
-* **Code contributed**: to be added soon
+* **Code contributed**: [Individual Dashboard](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=cikguseven&breakdown=true)
* **Enhancements implemented**: to be added soon
diff --git a/docs/team/lwz19.md b/docs/team/lwz19.md
index 1a0cf75ffad..11a2dec919f 100644
--- a/docs/team/lwz19.md
+++ b/docs/team/lwz19.md
@@ -26,7 +26,7 @@ Given below are my contributions to the project.
* **Code contributed**:
- * _To be added soon_
+ * [Individual Dashboard](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=lwz19&breakdown=true)
* **Project management**:
diff --git a/docs/team/ngeeyonglim.md b/docs/team/ngeeyonglim.md
new file mode 100644
index 00000000000..e00f2a085ed
--- /dev/null
+++ b/docs/team/ngeeyonglim.md
@@ -0,0 +1,18 @@
+# Class Manager 2023
+
+*Class Manager 2023 aims to provide fast access to CS2103S TAs who need help in maintaining student information across multiple classes. It also aims to help TAs visualize data across and within classes.*
+
+## Summary of Contributions
+to be added soon
+
+### Code Contributed
+[Individual Dashboard](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=ngeeyonglim&breakdown=true)
+
+### Enhancements Implemented
+to be added soon
+
+### Contributions to the UG
+to be added soon
+
+### Contributions beyond the Project
+to be added soon
diff --git a/docs/team/yeobohshin.md b/docs/team/yeobohshin.md
new file mode 100644
index 00000000000..62bb5226617
--- /dev/null
+++ b/docs/team/yeobohshin.md
@@ -0,0 +1,18 @@
+# Class Manager 2023
+
+*Class Manager 2023 aims to provide fast access to CS2103S TAs who need help in maintaining student information across multiple classes. It also aims to help TAs visualize data across and within classes.*
+
+## Summary of Contributions
+to be added soon
+
+### Code Contributed
+[Individual Dashboard](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=yeobohshin&breakdown=true)
+
+### Enhancements Implemented
+to be added soon
+
+### Contributions to the UG
+to be added soon
+
+### Contributions beyond the Project
+to be added soon
diff --git a/src/main/java/seedu/address/logic/commands/TagCommand.java b/src/main/java/seedu/address/logic/commands/TagCommand.java
new file mode 100644
index 00000000000..b0725bc3457
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/TagCommand.java
@@ -0,0 +1,104 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX;
+
+import java.util.List;
+import java.util.Set;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.Person;
+import seedu.address.model.tag.Tag;
+
+
+/**
+ * Changes the tags of an existing person in the address book.
+ */
+public class TagCommand extends Command {
+
+ public static final String COMMAND_WORD = "tag";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Edits the tags of the person identified "
+ + "by the student number. "
+ + "Existing tags will be overwritten by the input.\n"
+ + "Parameters: Student number (must be exist in address book) "
+ + "/t [LABEL]\n"
+ + "Example: " + COMMAND_WORD + " A1234567N "
+ + "/t smart.";
+ public static final String MESSAGE_ADD_TAG_SUCCESS = "Added tag to Person: %1$s";
+ public static final String MESSAGE_DELETE_TAG_SUCCESS = "Removed tag from Person: %1$s";
+ public static final String MESSAGE_TAG_FAILED = COMMAND_WORD
+ + ": There was an issue tagging the student.\n Please check "
+ + "that the index of the student exists or each label has "
+ + "the “/t ” prefix.";
+ private final Index index;
+ private final Set tags;
+
+ /**
+ * @param index of the person in the filtered person list to edit the remark
+ * @param tags of the person to be updated to
+ */
+ public TagCommand(Index index, Set tags) {
+ requireAllNonNull(index, tags);
+
+ this.index = index;
+ this.tags = tags;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ List lastShownList = model.getFilteredPersonList();
+
+ if (index.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ Person personToEdit = lastShownList.get(index.getZeroBased());
+ Person editedPerson = new Person(
+ personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(),
+ personToEdit.getAddress(), this.tags);
+
+ model.setPerson(personToEdit, editedPerson);
+ model.updateFilteredPersonList(Model.PREDICATE_SHOW_ALL_PERSONS);
+
+ return new CommandResult(generateSuccessMessage(editedPerson));
+ }
+
+ /**
+ * Generates a command execution success message based on whether
+ * the tag is added to or removed from
+ * {@code personToEdit}.
+ */
+ private String generateSuccessMessage(Person personToEdit) {
+ String message = !tags.isEmpty() ? MESSAGE_ADD_TAG_SUCCESS : MESSAGE_DELETE_TAG_SUCCESS;
+ return String.format(message, Messages.format(personToEdit));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof TagCommand)) {
+ return false;
+ }
+
+ TagCommand e = (TagCommand) other;
+ return index.equals(e.index)
+ && tags.equals(e.tags);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("tags", tags)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
index 3149ee07e0b..12fc7b8b115 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
@@ -17,6 +17,7 @@
import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.TagCommand;
import seedu.address.logic.parser.exceptions.ParseException;
/**
@@ -77,6 +78,9 @@ public Command parseCommand(String userInput) throws ParseException {
case HelpCommand.COMMAND_WORD:
return new HelpCommand();
+ case TagCommand.COMMAND_WORD:
+ return new TagCommandParser().parse(arguments);
+
default:
logger.finer("This user input caused a ParseException: " + userInput);
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java
index 75b1a9bf119..2bd7a410e37 100644
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java
@@ -11,5 +11,6 @@ public class CliSyntax {
public static final Prefix PREFIX_EMAIL = new Prefix("e/");
public static final Prefix PREFIX_ADDRESS = new Prefix("a/");
public static final Prefix PREFIX_TAG = new Prefix("t/");
+ public static final Prefix PREFIX_TAGS = new Prefix("/t");
}
diff --git a/src/main/java/seedu/address/logic/parser/TagCommandParser.java b/src/main/java/seedu/address/logic/parser/TagCommandParser.java
new file mode 100644
index 00000000000..eb3ccd47b25
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/TagCommandParser.java
@@ -0,0 +1,77 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TAGS;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.logic.commands.TagCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Parses input arguments and creates a new TagCommand object
+ */
+public class TagCommandParser implements Parser {
+
+ private Set tags;
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the TagCommand
+ * and returns an TagCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public TagCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args,
+ PREFIX_TAGS);
+
+ Index index;
+ try {
+ index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ } catch (IllegalValueException ive) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ TagCommand.MESSAGE_USAGE), ive);
+ }
+
+ parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAGS)).ifPresent(this::setTags);
+
+ if (this.tags == null) {
+ throw new ParseException(TagCommand.MESSAGE_TAG_FAILED);
+ }
+
+ return new TagCommand(index, this.tags);
+ }
+
+ /**
+ * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty.
+ * If {@code tags} contain only one element which is an empty string, it will be parsed into a
+ * {@code Set} containing zero tags.
+ */
+ private Optional> parseTagsForEdit(Collection tags) throws ParseException {
+ assert tags != null;
+
+ if (tags.isEmpty()) {
+ return Optional.empty();
+ }
+
+ Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags;
+
+ return Optional.of(ParserUtil.parseTags(tagSet));
+ }
+
+ /**
+ * Sets {@code tags} to this object's {@code tags}.
+ * A defensive copy of {@code tags} is used internally.
+ */
+ public void setTags(Set tags) {
+ this.tags = (tags != null) ? new HashSet<>(tags) : null;
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
index 643a1d08069..b62550df379 100644
--- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
+++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
@@ -7,6 +7,7 @@
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TAGS;
import static seedu.address.testutil.Assert.assertThrows;
import java.util.ArrayList;
@@ -36,6 +37,7 @@ public class CommandTestUtil {
public static final String VALID_ADDRESS_BOB = "Block 123, Bobby Street 3";
public static final String VALID_TAG_HUSBAND = "husband";
public static final String VALID_TAG_FRIEND = "friend";
+ public static final String VALID_TAG_FRIENDS = "friends";
public static final String NAME_DESC_AMY = " " + PREFIX_NAME + VALID_NAME_AMY;
public static final String NAME_DESC_BOB = " " + PREFIX_NAME + VALID_NAME_BOB;
@@ -47,13 +49,14 @@ public class CommandTestUtil {
public static final String ADDRESS_DESC_BOB = " " + PREFIX_ADDRESS + VALID_ADDRESS_BOB;
public static final String TAG_DESC_FRIEND = " " + PREFIX_TAG + VALID_TAG_FRIEND;
public static final String TAG_DESC_HUSBAND = " " + PREFIX_TAG + VALID_TAG_HUSBAND;
-
+ public static final String LABEL_DESC_FRIEND = " " + PREFIX_TAGS + VALID_TAG_FRIEND;
+ public static final String LABEL_DESC_HUSBAND = " " + PREFIX_TAGS + VALID_TAG_HUSBAND;
public static final String INVALID_NAME_DESC = " " + PREFIX_NAME + "James&"; // '&' not allowed in names
public static final String INVALID_PHONE_DESC = " " + PREFIX_PHONE + "911a"; // 'a' not allowed in phones
public static final String INVALID_EMAIL_DESC = " " + PREFIX_EMAIL + "bob!yahoo"; // missing '@' symbol
public static final String INVALID_ADDRESS_DESC = " " + PREFIX_ADDRESS; // empty string not allowed for addresses
public static final String INVALID_TAG_DESC = " " + PREFIX_TAG + "hubby*"; // '*' not allowed in tags
-
+ public static final String INVALID_LABEL_DESC = " " + PREFIX_TAGS + "hubby*"; // '*' not allowed in tags
public static final String PREAMBLE_WHITESPACE = "\t \r \n";
public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble";
diff --git a/src/test/java/seedu/address/logic/commands/TagCommandTest.java b/src/test/java/seedu/address/logic/commands/TagCommandTest.java
new file mode 100644
index 00000000000..7561a9fee36
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/TagCommandTest.java
@@ -0,0 +1,95 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIENDS;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON;
+import static seedu.address.testutil.TypicalIndexes.INDEX_THIRD_PERSON;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.util.SampleDataUtil;
+import seedu.address.testutil.TypicalPersons;
+
+/**
+ * Contains integration tests (interaction with the Model) and unit tests for TagCommand.
+ */
+public class TagCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_addTag_success() {
+ TagCommand tagCommand = new TagCommand(INDEX_FIRST_PERSON, TypicalPersons.ALICE.getTags());
+
+ String expectedMessage = String.format(TagCommand.MESSAGE_ADD_TAG_SUCCESS,
+ Messages.format(TypicalPersons.ALICE));
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(model.getFilteredPersonList().get(0), TypicalPersons.ALICE);
+
+ assertCommandSuccess(tagCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_deleteTag_success() {
+ TagCommand tagCommand = new TagCommand(INDEX_THIRD_PERSON, TypicalPersons.CARL.getTags());
+
+ String expectedMessage = String.format(TagCommand.MESSAGE_DELETE_TAG_SUCCESS,
+ Messages.format(TypicalPersons.CARL));
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(model.getFilteredPersonList().get(2), TypicalPersons.CARL);
+
+ assertCommandSuccess(tagCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_indexOutOfBound_failure() {
+ showPersonAtIndex(model, INDEX_FIRST_PERSON);
+ Index outOfBoundIndex = INDEX_SECOND_PERSON;
+ // ensures that outOfBoundIndex is still in bounds of address book list
+ assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size());
+
+ TagCommand tagCommand = new TagCommand(outOfBoundIndex, TypicalPersons.ALICE.getTags());
+
+ assertCommandFailure(tagCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void equals() {
+ final TagCommand standardCommand = new TagCommand(INDEX_FIRST_PERSON,
+ TypicalPersons.ALICE.getTags());
+
+ TagCommand commandWithSameValue = new TagCommand(INDEX_FIRST_PERSON,
+ SampleDataUtil.getTagSet(VALID_TAG_FRIENDS));
+
+ assertTrue(standardCommand.equals(commandWithSameValue));
+
+ assertTrue(standardCommand.equals(standardCommand));
+
+ assertFalse(standardCommand.equals(null));
+
+ assertFalse(standardCommand.equals(new ClearCommand()));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index targetIndex = Index.fromOneBased(1);
+ TagCommand tagCommand = new TagCommand(targetIndex, TypicalPersons.ALICE.getTags());
+ String expected = TagCommand.class.getCanonicalName() + "{tags=" + TypicalPersons.ALICE.getTags() + "}";
+ assertEquals(expected, tagCommand.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
index 5a1ab3dbc0c..980b8946d3d 100644
--- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
@@ -22,12 +22,14 @@
import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.TagCommand;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.person.NameContainsKeywordsPredicate;
import seedu.address.model.person.Person;
import seedu.address.testutil.EditPersonDescriptorBuilder;
import seedu.address.testutil.PersonBuilder;
import seedu.address.testutil.PersonUtil;
+import seedu.address.testutil.TypicalPersons;
public class AddressBookParserTest {
@@ -62,6 +64,13 @@ public void parseCommand_edit() throws Exception {
assertEquals(new EditCommand(INDEX_FIRST_PERSON, descriptor), command);
}
+ @Test
+ public void parseCommand_tag() throws Exception {
+ TagCommand command = (TagCommand) parser.parseCommand(TagCommand.COMMAND_WORD + " "
+ + INDEX_FIRST_PERSON.getOneBased() + " " + PersonUtil.getTagDetails(TypicalPersons.ALICE));
+ assertEquals(new TagCommand(INDEX_FIRST_PERSON, TypicalPersons.ALICE.getTags()), command);
+ }
+
@Test
public void parseCommand_exit() throws Exception {
assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD) instanceof ExitCommand);
diff --git a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
index cc7175172d4..1a8f6b1c59f 100644
--- a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
@@ -179,7 +179,7 @@ public void parse_multipleRepeatedFields_failure() {
assertParseFailure(parser, userInput, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
- // mulltiple valid fields repeated
+ // multiple valid fields repeated
userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY
+ TAG_DESC_FRIEND + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY + TAG_DESC_FRIEND
+ PHONE_DESC_BOB + ADDRESS_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_HUSBAND;
diff --git a/src/test/java/seedu/address/logic/parser/TagCommandParserTest.java b/src/test/java/seedu/address/logic/parser/TagCommandParserTest.java
new file mode 100644
index 00000000000..aafc953929c
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/TagCommandParserTest.java
@@ -0,0 +1,95 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_LABEL_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.LABEL_DESC_FRIEND;
+import static seedu.address.logic.commands.CommandTestUtil.LABEL_DESC_HUSBAND;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TAGS;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON;
+import static seedu.address.testutil.TypicalIndexes.INDEX_THIRD_PERSON;
+
+import java.util.HashSet;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.TagCommand;
+import seedu.address.model.person.Person;
+import seedu.address.model.tag.Tag;
+import seedu.address.testutil.PersonBuilder;
+
+public class TagCommandParserTest {
+
+ private static final String TAG_EMPTY = " " + PREFIX_TAGS;
+
+ private static final String MESSAGE_INVALID_FORMAT =
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, TagCommand.MESSAGE_USAGE);
+ private TagCommandParser parser = new TagCommandParser();
+
+ @Test
+ public void parse_missingParts_failure() {
+ // no index specified
+ assertParseFailure(parser, TAG_EMPTY, MESSAGE_INVALID_FORMAT);
+
+ // no field specified
+ assertParseFailure(parser, "1", TagCommand.MESSAGE_TAG_FAILED);
+
+ // no index and no field specified
+ assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT);
+
+ }
+
+ @Test
+ public void parse_invalidPreamble_failure() {
+ // negative index
+ assertParseFailure(parser, "-1" + TAG_EMPTY, MESSAGE_INVALID_FORMAT);
+
+ // zero index
+ assertParseFailure(parser, "0" + TAG_EMPTY, MESSAGE_INVALID_FORMAT);
+
+ // invalid arguments being parsed as preamble
+ assertParseFailure(parser, "1 some random tag", MESSAGE_INVALID_FORMAT);
+
+ // invalid prefix being parsed as preamble
+ assertParseFailure(parser, "1 t/ label", MESSAGE_INVALID_FORMAT);
+ }
+
+ @Test
+ public void parse_invalidValue_failure() {
+ assertParseFailure(parser, "1" + INVALID_LABEL_DESC, Tag.MESSAGE_CONSTRAINTS); // invalid tag
+
+ // while parsing {@code PREFIX_TAGS} alone will reset the tags of the {@code Person} being edited,
+ // parsing it together with a valid tag results in error
+ assertParseFailure(parser, "1" + LABEL_DESC_FRIEND + LABEL_DESC_HUSBAND + TAG_EMPTY,
+ Tag.MESSAGE_CONSTRAINTS);
+ assertParseFailure(parser, "1" + LABEL_DESC_FRIEND + TAG_EMPTY + LABEL_DESC_HUSBAND,
+ Tag.MESSAGE_CONSTRAINTS);
+ assertParseFailure(parser, "1" + TAG_EMPTY + LABEL_DESC_FRIEND + LABEL_DESC_HUSBAND,
+ Tag.MESSAGE_CONSTRAINTS);
+ }
+
+ @Test
+ public void parse_allFieldsSpecified_success() {
+ Index targetIndex = INDEX_SECOND_PERSON;
+ String userInput = targetIndex.getOneBased() + LABEL_DESC_HUSBAND + LABEL_DESC_FRIEND;
+
+ Person person = new PersonBuilder().withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build();
+ TagCommand expectedCommand = new TagCommand(targetIndex, person.getTags());
+
+ assertParseSuccess(parser, userInput, expectedCommand);
+ }
+
+ @Test
+ public void parse_resetTags_success() {
+ Index targetIndex = INDEX_THIRD_PERSON;
+ String userInput = targetIndex.getOneBased() + TAG_EMPTY;
+
+ TagCommand expectedCommand = new TagCommand(targetIndex, new HashSet<>());
+
+ assertParseSuccess(parser, userInput, expectedCommand);
+ }
+}
diff --git a/src/test/java/seedu/address/testutil/PersonUtil.java b/src/test/java/seedu/address/testutil/PersonUtil.java
index 90849945183..1b1417fb4ec 100644
--- a/src/test/java/seedu/address/testutil/PersonUtil.java
+++ b/src/test/java/seedu/address/testutil/PersonUtil.java
@@ -5,6 +5,7 @@
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TAGS;
import java.util.Set;
@@ -59,4 +60,18 @@ public static String getEditPersonDescriptorDetails(EditPersonDescriptor descrip
}
return sb.toString();
}
+ /**
+ * Returns the tag command string for the given {@code person}'s details.
+ */
+ public static String getTagDetails(Person person) {
+ StringBuilder sb = new StringBuilder();
+ Set tags = person.getTags();
+ if (tags.isEmpty()) {
+ sb.append(PREFIX_TAGS);
+ } else {
+ tags.forEach(s -> sb.append(PREFIX_TAGS).append(" ").append(s.tagName).append(" "));
+ }
+
+ return sb.toString();
+ }
}