Repository: AY2425S2-CS2103T-T17-2/tp
Pull request #89: Add Tag(s) Without Overwriting
Author: login: Bryce-3D
Labels: name: type.Enhancement
description: Issue type
name: type.Task
description: Issue type
name: priority.High
description: Priority labels
name: type.*
Created: 2025-03-24T05:51:15Z | Merged: 2025-03-26T14:54:18Z

Closes #88 

Currently just a draft PR, will undraft it once completed. **Do not merge yet**

Linked issue #88: Add Tag(s) Without Overwriting
Currently, the command to modify the tags of a patient is to run something like 
> `edit 39 t/tag_1 t/tag_2 ...`

However, this is really clunky since it overwrites all preexisting tags, so if a patient already has a lot of tags, doing a single modification will be very annoying.

As a user, I want to be able to add individual tags without overwriting so that I don't have to repeatedly key in already existing tags

Diff:
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..0d4ec960bbd
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/TagCommand.java
@@ -0,0 +1,126 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.patient.Patient;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Adds a bunch of tags to a specified patient.
+ */
+public class TagCommand extends Command {
+    public static final String COMMAND_WORD = "tag";
+    public static final String MESSAGE_USAGE = COMMAND_WORD
+            + ": Adds the listed tags to the patient identified by the index "
+            + "number used in the last patient listing."
+            + '\n'
+            + "Parameters: INDEX t/tag [t/more_tags]"
+            + '\n'
+            + "Example: " + COMMAND_WORD + "39 t/High Blood Pressure "
+            + "t/Seafood Allergy";
+
+    private final Index index;
+    private final HashSet<Tag> tagsToAdd;
+
+    /**
+     * Creates a {@code TagCommand}.
+     *
+     * @param index The index of the patient to modify.
+     * @param tagsToAdd The set of tags to add.
+     */
+    public TagCommand(Index index, HashSet<Tag> tagsToAdd) {
+        this.index = index;
+        this.tagsToAdd = tagsToAdd;
+    }
+
+    /**
+     * Generates the msg to send upon successful deletion.
+     *
+     * @param patient The newly modified patient.
+     * @return The msg to send upon successful deletion.
+     */
+    private static String generateSuccessMessage(Patient patient,
+              HashSet<Tag> tagsToAdd) {
+        HashSet<String> tagStrsToAdd = new HashSet<String>();
+        for (Tag t : tagsToAdd) {
+            tagStrsToAdd.add(t.toString());
+        }
+        return "Tags " + String.join(", ", tagStrsToAdd)
+                + " successfully added to " + patient.getName();
+    }
+
+    /**
+     * Executes the stored `tag` cmd.
+     *
+     * @param model {@code Model} which the command should operate on.
+     * @return The {@code CommandResult}.
+     * @throws CommandException if the index is out of range.
+     */
+    @Override
+    public CommandResult execute(Model model) throws CommandException {
+        // Basing off of `EditCommand.java`
+        requireNonNull(model);
+        List<Patient> lastShownList = model.getFilteredPatientList();
+
+        if (index.getZeroBased() >= lastShownList.size()) {
+            throw new CommandException(
+                    Messages.MESSAGE_INVALID_PATIENT_DISPLAYED_INDEX);
+        }
+
+        Patient patient = lastShownList.get(index.getZeroBased());
+        Patient editedPatient = addTags(patient, this.tagsToAdd);
+
+        model.setPatient(patient, editedPatient);
+        model.updateFilteredPatientList(Model.PREDICATE_SHOW_ALL_PATIENTS);
+        return new CommandResult(
+                generateSuccessMessage(editedPatient, tagsToAdd)
+        );
+    }
+
+    /**
+     * Adds the list of tags to the given patient.
+     *
+     * @param p The patient we're adding tags to.
+     * @param tagsToAdd The set of tags to add.
+     * @return A new patient with the tags added.
+     */
+    private static Patient addTags(Patient p, HashSet<Tag> tagsToAdd) {
+        assert p != null;
+
+        // This is not modifiable, so everything will have to be copied over
+        Set<Tag> ogTags = p.getTags();
+        Set<Tag> newTags = new HashSet<Tag>();
+        newTags.addAll(ogTags);
+        newTags.addAll(tagsToAdd);
+
+        return new Patient(
+                p.getName(),
+                p.getPhone(),
+                p.getEmail(),
+                p.getAddress(),
+                p.getLastVisit(),
+                newTags,
+                p.getMedicines()
+        );
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other == null) {
+            return false;
+        }
+        if (!(other instanceof TagCommand)) {
+            return false;
+        }
+        TagCommand o = (TagCommand) other;
+        return index.equals(o.index) && tagsToAdd.equals(o.tagsToAdd);
+    }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
index 622c8992040..26b962c20d2 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
@@ -20,6 +20,7 @@
 import seedu.address.logic.commands.LastVisitCommand;
 import seedu.address.logic.commands.ListCommand;
 import seedu.address.logic.commands.PrescribeCommand;
+import seedu.address.logic.commands.TagCommand;
 import seedu.address.logic.commands.UnprescribeCommand;
 import seedu.address.logic.parser.exceptions.ParseException;
 
@@ -93,6 +94,9 @@ public Command parseCommand(String userInput) throws ParseException {
         case UnprescribeCommand.COMMAND_WORD:
             return new UnprescribeCommandParser().parse(arguments);
 
+        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/TagCommandParser.java b/src/main/java/seedu/address/logic/parser/TagCommandParser.java
new file mode 100644
index 00000000000..506ee6bc708
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/TagCommandParser.java
@@ -0,0 +1,57 @@
+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_TAG;
+
+import java.util.HashSet;
+
+import seedu.address.commons.core.index.Index;
+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
+ */
+public class TagCommandParser implements Parser<TagCommand> {
+    /**
+     * Parses the given {@code String} of arguments in the context of the
+     * {@code TagCommand} and returns a {@code TagCommand} object for
+     * execution.
+     *
+     * @throws ParseException if the user input does not conform to the
+     *     expected format
+     */
+    public TagCommand parse(String args) throws ParseException {
+        // Based on EditCommandParser.java
+        requireNonNull(args);
+        ArgumentMultimap argMM = ArgumentTokenizer.tokenize(args, PREFIX_TAG);
+
+        // Retrieve the index from the user input
+        Index ind;
+        try {
+            ind = ParserUtil.parseIndex(argMM.getPreamble());
+        } catch (ParseException pe) {
+            // Copied from DelLastVisitCommandParser.java
+            throw new ParseException(
+                    String.format(
+                            MESSAGE_INVALID_COMMAND_FORMAT,
+                            TagCommand.MESSAGE_USAGE
+                    ),
+                    pe
+            );
+        }
+
+        // Extract the tags
+        HashSet<String> tagStrsToAdd = new HashSet<String>(
+                argMM.getAllValues(PREFIX_TAG)
+        );
+        HashSet<Tag> tagsToAdd = new HashSet<Tag>();
+        for (String s : tagStrsToAdd) {
+            tagsToAdd.add(new Tag(s));
+        }
+
+        return new TagCommand(ind, tagsToAdd);
+    }
+}
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..8d748ccfa4c
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/TagCommandTest.java
@@ -0,0 +1,56 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.patient.Patient;
+import seedu.address.model.tag.Tag;
+import seedu.address.testutil.PatientBuilder;
+import seedu.address.testutil.TypicalPatients;
+
+public class TagCommandTest {
+    @Test
+    public void execute_onePatient_normal() {
+        // Referencing DelLastVisitCommandTest.java
+
+        // Typical patient + copy with added tags
+        Patient p0 = TypicalPatients.ALICE;
+        p0 = new PatientBuilder(p0)
+                .withTags("a", "b", "c")
+                .build();
+        Patient p1 = new PatientBuilder(p0)
+                .withTags("a", "b", "c", "d", "e")
+                .build();
+
+        // Create a model with `p0` then run tag
+        AddressBook ab0 = new AddressBook();
+        ab0.addPatient(p0);
+        Model m0 = new ModelManager(ab0, new UserPrefs());
+        try {
+            CommandResult cmdRes = new TagCommand(
+                    Index.fromZeroBased(0),
+                    new HashSet<Tag>(Set.of(
+                            new Tag("d"), new Tag("e")
+                    ))
+            ).execute(m0);
+        } catch (Exception e) {
+            fail("fail");
+        }
+
+        // Create a model with `p1` only and compare
+        AddressBook ab1 = new AddressBook();
+        ab1.addPatient(p1);
+        Model m1 = new ModelManager(ab1, new UserPrefs());
+        assertEquals(m0, m1);
+    }
+}
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..2cb6d7fbe74
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/TagCommandParserTest.java
@@ -0,0 +1,44 @@
+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 static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PATIENT;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.TagCommand;
+import seedu.address.model.tag.Tag;
+
+public class TagCommandParserTest {
+    @Test
+    public void parse_validArg_success() {
+        // Referencing DelLastVisitCommandParserTest.java
+        assertParseSuccess(
+                new TagCommandParser(),
+                "1 t/Madoka t/Homura",
+                new TagCommand(
+                        INDEX_FIRST_PATIENT,
+                        new HashSet<Tag>(Set.of(
+                                new Tag("Madoka"),
+                                new Tag("Homura")
+                        ))
+                )
+        );
+    }
+
+    @Test
+    public void parse_invArg_error() {
+        // Referencing DelLastVisitCommandParserTest.java
+        assertParseFailure(
+                new TagCommandParser(),
+                "maybe the real patients were the friends "
+                + "we made along the way",
+                String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+                        TagCommand.MESSAGE_USAGE)
+        );
+    }
+}


Reviews:
user: login: szelingtan
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-24T14:24:49Z
user: login: Bryce-3D
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-25T07:11:56Z
user: login: Eun777
body: Minor changes to variable names recommended but otherwise, code looks good to me.
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-26T08:19:03Z

Review comments:
diff_hunk: @@ -0,0 +1,104 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.patient.Patient;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Adds a bunch of tags to a specified patient.
+ */
+public class TagCommand extends Command {
+    public static final String COMMAND_WORD = "tag";
+    public static final String MESSAGE_USAGE = COMMAND_WORD
+            + ": Adds the listed tags to the patient identified by the index "
+            + "numebr used in the last patient listing."
+            + '\n'
+            + "Parameters: INDEX t/tag [t/more_tags]"
+            + '\n'
+            + "Example: " + COMMAND_WORD + "39 t/Likes Madoka Magica "
path: src/main/java/seedu/address/logic/commands/TagCommand.java
user: login: szelingtan
body: Perhaps the tag examples can be more about the condition of the patient (e.g. t/diabetes) as we previously discussed tag will help to highlight / categorize the patient's condition for our social workers. 
created_at: 2025-03-24T14:24:49Z
author_association: NONE
diff_hunk: @@ -0,0 +1,104 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.patient.Patient;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Adds a bunch of tags to a specified patient.
+ */
+public class TagCommand extends Command {
+    public static final String COMMAND_WORD = "tag";
+    public static final String MESSAGE_USAGE = COMMAND_WORD
+            + ": Adds the listed tags to the patient identified by the index "
+            + "numebr used in the last patient listing."
+            + '\n'
+            + "Parameters: INDEX t/tag [t/more_tags]"
+            + '\n'
+            + "Example: " + COMMAND_WORD + "39 t/Likes Madoka Magica "
path: src/main/java/seedu/address/logic/commands/TagCommand.java
user: login: Bryce-3D
body: Thanks for the feedback! Will update this part accordingly
created_at: 2025-03-25T07:11:56Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,126 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.patient.Patient;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Adds a bunch of tags to a specified patient.
+ */
+public class TagCommand extends Command {
+    public static final String COMMAND_WORD = "tag";
+    public static final String MESSAGE_USAGE = COMMAND_WORD
+            + ": Adds the listed tags to the patient identified by the index "
+            + "number used in the last patient listing."
+            + '\n'
+            + "Parameters: INDEX t/tag [t/more_tags]"
+            + '\n'
+            + "Example: " + COMMAND_WORD + "39 t/High Blood Pressure "
+            + "t/Seafood Allergy";
+
+    private final Index index;
+    private final HashSet<Tag> tagsToAdd;
+
+    /**
+     * Creates a {@code TagCommand}.
+     *
+     * @param index The index of the patient to modify.
+     * @param tagsToAdd The set of tags to add.
+     */
+    public TagCommand(Index index, HashSet<Tag> tagsToAdd) {
+        this.index = index;
+        this.tagsToAdd = tagsToAdd;
+    }
+
+    /**
+     * Generates the msg to send upon successful deletion.
+     *
+     * @param patient The newly modified patient.
+     * @return The msg to send upon successful deletion.
+     */
+    private static String generateSuccessMessage(Patient patient,
+              HashSet<Tag> tagsToAdd) {
+        HashSet<String> tagStrsToAdd = new HashSet<String>();
+        for (Tag t : tagsToAdd) {
+            tagStrsToAdd.add(t.toString());
+        }
+        return "Tags " + String.join(", ", tagStrsToAdd)
+                + " successfully added to " + patient.getName();
+    }
+
+    /**
+     * Executes the stored `tag` cmd.
+     *
+     * @param model {@code Model} which the command should operate on.
+     * @return The {@code CommandResult}.
+     * @throws CommandException if the index is out of range.
+     */
+    @Override
+    public CommandResult execute(Model model) throws CommandException {
+        // Basing off of `EditCommand.java`
+        requireNonNull(model);
+        List<Patient> lastShownList = model.getFilteredPatientList();
+
+        if (index.getZeroBased() >= lastShownList.size()) {
+            throw new CommandException(
+                    Messages.MESSAGE_INVALID_PATIENT_DISPLAYED_INDEX);
+        }
+
+        Patient patient = lastShownList.get(index.getZeroBased());
+        Patient editedPatient = addTags(patient, this.tagsToAdd);
+
+        model.setPatient(patient, editedPatient);
+        model.updateFilteredPatientList(Model.PREDICATE_SHOW_ALL_PATIENTS);
+        return new CommandResult(
+                generateSuccessMessage(editedPatient, tagsToAdd)
+        );
+    }
+
+    /**
+     * Adds the list of tags to the given patient.
+     *
+     * @param p The patient we're adding tags to.
+     * @param tagsToAdd The set of tags to add.
+     * @return A new patient with the tags added.
+     */
+    private static Patient addTags(Patient p, HashSet<Tag> tagsToAdd) {
+        assert p != null;
+
+        // This is not modifiable, so everything will have to be copied over
+        Set<Tag> ogTags = p.getTags();
path: src/main/java/seedu/address/logic/commands/TagCommand.java
user: login: Eun777
body: Should we rename ogTags to be originalTags for more clarity?
created_at: 2025-03-26T08:13:21Z
author_association: NONE
diff_hunk: @@ -0,0 +1,57 @@
+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_TAG;
+
+import java.util.HashSet;
+
+import seedu.address.commons.core.index.Index;
+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
+ */
+public class TagCommandParser implements Parser<TagCommand> {
+    /**
+     * Parses the given {@code String} of arguments in the context of the
+     * {@code TagCommand} and returns a {@code TagCommand} object for
+     * execution.
+     *
+     * @throws ParseException if the user input does not conform to the
+     *     expected format
+     */
+    public TagCommand parse(String args) throws ParseException {
+        // Based on EditCommandParser.java
+        requireNonNull(args);
+        ArgumentMultimap argMM = ArgumentTokenizer.tokenize(args, PREFIX_TAG);
+
+        // Retrieve the index from the user input
+        Index ind;
path: src/main/java/seedu/address/logic/parser/TagCommandParser.java
user: login: Eun777
body: Maybe can change to Index index for standardisation with other files?
created_at: 2025-03-26T08:17:46Z
author_association: NONE

PR comments:
user: login: codecov[bot]
created_at: 2025-03-25T08:16:19Z
body: ## [Codecov](https://app.codecov.io/gh/AY2425S2-CS2103T-T17-2/tp/pull/89?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=AY2425S2-CS2103T-T17-2) Report
Attention: Patch coverage is `81.81818%` with `10 lines` in your changes missing coverage. Please review.

| [Files with missing lines](https://app.codecov.io/gh/AY2425S2-CS2103T-T17-2/tp/pull/89?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=AY2425S2-CS2103T-T17-2) | Patch % | Lines |
|---|---|---|
| [.../java/seedu/address/logic/commands/TagCommand.java](https://app.codecov.io/gh/AY2425S2-CS2103T-T17-2/tp/pull/89?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Fseedu%2Faddress%2Flogic%2Fcommands%2FTagCommand.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=AY2425S2-CS2103T-T17-2#diff-c3JjL21haW4vamF2YS9zZWVkdS9hZGRyZXNzL2xvZ2ljL2NvbW1hbmRzL1RhZ0NvbW1hbmQuamF2YQ==) | 76.92% | [3 Missing and 6 partials :warning: ](https://app.codecov.io/gh/AY2425S2-CS2103T-T17-2/tp/pull/89?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=AY2425S2-CS2103T-T17-2) |
| [.../seedu/address/logic/parser/AddressBookParser.java](https://app.codecov.io/gh/AY2425S2-CS2103T-T17-2/tp/pull/89?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Fseedu%2Faddress%2Flogic%2Fparser%2FAddressBookParser.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=AY2425S2-CS2103T-T17-2#diff-c3JjL21haW4vamF2YS9zZWVkdS9hZGRyZXNzL2xvZ2ljL3BhcnNlci9BZGRyZXNzQm9va1BhcnNlci5qYXZh) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/AY2425S2-CS2103T-T17-2/tp/pull/89?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=AY2425S2-CS2103T-T17-2) |

| [Files with missing lines](https://app.codecov.io/gh/AY2425S2-CS2103T-T17-2/tp/pull/89?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=AY2425S2-CS2103T-T17-2) | Coverage Δ | Complexity Δ | |
|---|---|---|---|
| [...a/seedu/address/logic/parser/TagCommandParser.java](https://app.codecov.io/gh/AY2425S2-CS2103T-T17-2/tp/pull/89?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Fseedu%2Faddress%2Flogic%2Fparser%2FTagCommandParser.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=AY2425S2-CS2103T-T17-2#diff-c3JjL21haW4vamF2YS9zZWVkdS9hZGRyZXNzL2xvZ2ljL3BhcnNlci9UYWdDb21tYW5kUGFyc2VyLmphdmE=) | `100.00% <100.00%> (ø)` | `3.00 <3.00> (?)` | |
| [.../seedu/address/logic/parser/AddressBookParser.java](https://app.codecov.io/gh/AY2425S2-CS2103T-T17-2/tp/pull/89?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Fseedu%2Faddress%2Flogic%2Fparser%2FAddressBookParser.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=AY2425S2-CS2103T-T17-2#diff-c3JjL21haW4vamF2YS9zZWVkdS9hZGRyZXNzL2xvZ2ljL3BhcnNlci9BZGRyZXNzQm9va1BhcnNlci5qYXZh) | `80.00% <0.00%> (-3.34%)` | `13.00 <0.00> (ø)` | |
| [.../java/seedu/address/logic/commands/TagCommand.java](https://app.codecov.io/gh/AY2425S2-CS2103T-T17-2/tp/pull/89?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Fseedu%2Faddress%2Flogic%2Fcommands%2FTagCommand.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=AY2425S2-CS2103T-T17-2#diff-c3JjL21haW4vamF2YS9zZWVkdS9hZGRyZXNzL2xvZ2ljL2NvbW1hbmRzL1RhZ0NvbW1hbmQuamF2YQ==) | `76.92% <76.92%> (ø)` | `7.00 <7.00> (?)` | |

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: Bryce-3D
created_at: 2025-03-25T12:03:42Z
body: Finally done (?)
Almost forgot to add tests
Pls add comments on the code itself for your PR quota thing before merging
author_association: COLLABORATOR