From c7217adafdde2b1f695d0019595da47e961ab97e Mon Sep 17 00:00:00 2001 From: Ivan Lee <84584280+ivanleekk@users.noreply.github.com> Date: Wed, 18 Oct 2023 10:40:47 +0800 Subject: [PATCH 01/42] Add FilterCommand --- .../logic/commands/FilterCommand.java | 70 ++++++++++++++++++ .../staffsnap/logic/commands/SortCommand.java | 1 + .../logic/parser/ApplicantBookParser.java | 7 +- .../logic/parser/FilterCommandParser.java | 66 +++++++++++++++++ .../logic/parser/SortCommandParser.java | 2 +- .../applicant/CustomFilterPredicate.java | 71 +++++++++++++++++++ 6 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 src/main/java/seedu/staffsnap/logic/commands/FilterCommand.java create mode 100644 src/main/java/seedu/staffsnap/logic/parser/FilterCommandParser.java create mode 100644 src/main/java/seedu/staffsnap/model/applicant/CustomFilterPredicate.java diff --git a/src/main/java/seedu/staffsnap/logic/commands/FilterCommand.java b/src/main/java/seedu/staffsnap/logic/commands/FilterCommand.java new file mode 100644 index 00000000000..db2c549364e --- /dev/null +++ b/src/main/java/seedu/staffsnap/logic/commands/FilterCommand.java @@ -0,0 +1,70 @@ +package seedu.staffsnap.logic.commands; + +import seedu.staffsnap.commons.util.ToStringBuilder; +import seedu.staffsnap.logic.Messages; +import seedu.staffsnap.model.Model; +import seedu.staffsnap.model.applicant.CustomFilterPredicate; + +import static java.util.Objects.requireNonNull; +import static seedu.staffsnap.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.staffsnap.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.staffsnap.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.staffsnap.logic.parser.CliSyntax.PREFIX_POSITION; + +/** + * Finds and lists all applicants in address book whose name contains any of the argument keywords. + * Keyword matching is case-insensitive. + */ +public class FilterCommand extends Command { + + public static final String COMMAND_WORD = "filter"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Filters all applicants who match the descriptor."; + public static final String MESSAGE_FAILURE = "Please add at least one field to filter by. " + + "Possible fields include:" + "\n" + + PREFIX_NAME + " [NAME], " + + PREFIX_EMAIL + " [EMAIL], " + + PREFIX_POSITION + " [POSITION], " + + PREFIX_PHONE + " [PHONE]"; + + private final CustomFilterPredicate predicate; + + public FilterCommand(CustomFilterPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredApplicantList(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_APPLICANTS_LISTED_OVERVIEW, model.getFilteredApplicantList().size())); + } + + /** + * Checks if the applicant exists. + * @param other Other applicant. + * @return true if equals, false if not equals. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof FilterCommand)) { + return false; + } + + FilterCommand otherFilterCommand = (FilterCommand) other; + return predicate.equals(otherFilterCommand.predicate); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("predicate", predicate) + .toString(); + } +} diff --git a/src/main/java/seedu/staffsnap/logic/commands/SortCommand.java b/src/main/java/seedu/staffsnap/logic/commands/SortCommand.java index e14dc3d2a0c..cd7090b0565 100644 --- a/src/main/java/seedu/staffsnap/logic/commands/SortCommand.java +++ b/src/main/java/seedu/staffsnap/logic/commands/SortCommand.java @@ -20,6 +20,7 @@ public class SortCommand extends Command { + "Parameters: " + PREFIX_DESCRIPTOR + "DESCRIPTOR "; public static final String MESSAGE_SUCCESS = "Sorted all Applicants"; + public static final String MESSAGE_FAILURE = "Please add a descriptor with d/ [name/phone]."; private final Descriptor descriptor; diff --git a/src/main/java/seedu/staffsnap/logic/parser/ApplicantBookParser.java b/src/main/java/seedu/staffsnap/logic/parser/ApplicantBookParser.java index c9a4f3cc00f..e95e8b55d78 100644 --- a/src/main/java/seedu/staffsnap/logic/parser/ApplicantBookParser.java +++ b/src/main/java/seedu/staffsnap/logic/parser/ApplicantBookParser.java @@ -16,6 +16,7 @@ import seedu.staffsnap.logic.commands.EditCommand; import seedu.staffsnap.logic.commands.ExitCommand; import seedu.staffsnap.logic.commands.FindCommand; +import seedu.staffsnap.logic.commands.FilterCommand; import seedu.staffsnap.logic.commands.HelpCommand; import seedu.staffsnap.logic.commands.ListCommand; import seedu.staffsnap.logic.commands.SortCommand; @@ -60,7 +61,7 @@ public Command parseCommand(String userInput) throws ParseException { IsConfirmed = IsConfirmedNext; IsConfirmedNext = false; - switch (commandWord) { + switch (commandWord.toLowerCase()) { case AddCommand.COMMAND_WORD: return new AddCommandParser().parse(arguments); @@ -97,6 +98,10 @@ public Command parseCommand(String userInput) throws ParseException { case SortCommand.COMMAND_WORD: return new SortCommandParser().parse(arguments); + case FilterCommand.COMMAND_WORD: + return new FilterCommandParser().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/staffsnap/logic/parser/FilterCommandParser.java b/src/main/java/seedu/staffsnap/logic/parser/FilterCommandParser.java new file mode 100644 index 00000000000..9e9df76b4de --- /dev/null +++ b/src/main/java/seedu/staffsnap/logic/parser/FilterCommandParser.java @@ -0,0 +1,66 @@ +package seedu.staffsnap.logic.parser; + +import seedu.staffsnap.logic.commands.FilterCommand; +import seedu.staffsnap.logic.parser.exceptions.ParseException; +import seedu.staffsnap.model.applicant.CustomFilterPredicate; +import seedu.staffsnap.model.applicant.Email; +import seedu.staffsnap.model.applicant.Name; +import seedu.staffsnap.model.applicant.Phone; +import seedu.staffsnap.model.applicant.Position; +import seedu.staffsnap.model.interview.Interview; + +import java.util.Set; + +import static seedu.staffsnap.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.staffsnap.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.staffsnap.logic.parser.CliSyntax.PREFIX_INTERVIEW; +import static seedu.staffsnap.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.staffsnap.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.staffsnap.logic.parser.CliSyntax.PREFIX_POSITION; + +/** + * Parses input arguments and creates a new FilterCommand object + */ +public class FilterCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FilterCommand + * and returns a FilterCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FilterCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_FAILURE)); + } + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, + PREFIX_POSITION, PREFIX_INTERVIEW); + + Name name = null; + Phone phone = null; + Email email = null; + Position position = null; + Set interviewList = null; + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_POSITION); + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + } + if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { + phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); + } + if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { + email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()); + } + if (argMultimap.getValue(PREFIX_POSITION).isPresent()) { + position = ParserUtil.parsePosition(argMultimap.getValue(PREFIX_POSITION).get()); + } + if (argMultimap.getValue(PREFIX_INTERVIEW).isPresent()) { + interviewList = ParserUtil.parseInterviews(argMultimap.getAllValues(PREFIX_INTERVIEW)); + } + + return new FilterCommand(new CustomFilterPredicate(name, phone, email, position, interviewList)); + } + +} diff --git a/src/main/java/seedu/staffsnap/logic/parser/SortCommandParser.java b/src/main/java/seedu/staffsnap/logic/parser/SortCommandParser.java index a67ab4db56e..e830d9cf19d 100644 --- a/src/main/java/seedu/staffsnap/logic/parser/SortCommandParser.java +++ b/src/main/java/seedu/staffsnap/logic/parser/SortCommandParser.java @@ -25,7 +25,7 @@ public SortCommand parse(String args) throws ParseException { if (!arePrefixesPresent(argMultimap, PREFIX_DESCRIPTOR) || !argMultimap.getPreamble().isEmpty()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE)); + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_FAILURE)); } argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_DESCRIPTOR); diff --git a/src/main/java/seedu/staffsnap/model/applicant/CustomFilterPredicate.java b/src/main/java/seedu/staffsnap/model/applicant/CustomFilterPredicate.java new file mode 100644 index 00000000000..454aef1983b --- /dev/null +++ b/src/main/java/seedu/staffsnap/model/applicant/CustomFilterPredicate.java @@ -0,0 +1,71 @@ +package seedu.staffsnap.model.applicant; + +import seedu.staffsnap.commons.util.StringUtil; +import seedu.staffsnap.model.interview.Interview; + +import java.util.Set; +import java.util.function.Predicate; + +public class CustomFilterPredicate implements Predicate { + + // Identity fields + private final Name name; + private final Phone phone; + // Data fields + private final Email email; + private final Position position; + private final Set interviews; + + /** + * @param applicant the input argument + * @return true if applicant matches all specified fields in the predicate + */ + @Override + public boolean test(Applicant applicant) { + if (this.name != null) { + if (!StringUtil.containsWordIgnoreCase(applicant.getName().toString(), this.name.toString())) { + return false; + } + } + if (this.phone != null) { + if (!StringUtil.containsWordIgnoreCase(applicant.getPhone().toString(), this.phone.toString())) { + return false; + } + } + if (this.email != null) { + if (!StringUtil.containsWordIgnoreCase(applicant.getEmail().toString(), this.email.toString())) { + return false; + } + } + if (this.position != null) { + if (!StringUtil.containsWordIgnoreCase(applicant.getPosition().toString(), this.position.toString())) { + return false; + } + } + /* + * TODO: + * add filtering for interviews + */ + return true; + } + + public CustomFilterPredicate(Name name, Phone phone, Email email, Position position, Set interviews) { + this.name = name; + this.phone = phone; + this.email = email; + this.position = position; + this.interviews = interviews; + System.out.println(this); + } + + @Override + public String toString() { + return "CustomFilterPredicate{" + + "name=" + name + + ", phone=" + phone + + ", email=" + email + + ", position=" + position + + ", interviews=" + interviews + + '}'; + } +} From a3c28f8f22295959dd8bbc9701e0cdc9917900d3 Mon Sep 17 00:00:00 2001 From: Ivan Lee <84584280+ivanleekk@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:37:24 +0800 Subject: [PATCH 02/42] Add basic tests for FilterCommand --- .gitignore | 1 + .../staffsnap/commons/util/StringUtil.java | 23 +++- .../applicant/CustomFilterPredicate.java | 2 +- .../logic/commands/FilterCommandTest.java | 121 ++++++++++++++++++ 4 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 src/test/java/seedu/staffsnap/logic/commands/FilterCommandTest.java diff --git a/.gitignore b/.gitignore index eab4c7db6a5..29b729090d3 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ src/test/data/sandbox/ .DS_Store docs/_site/ docs/_markbind/logs/ +/htmlReport/ diff --git a/src/main/java/seedu/staffsnap/commons/util/StringUtil.java b/src/main/java/seedu/staffsnap/commons/util/StringUtil.java index 4d11a031385..5409d3c125e 100644 --- a/src/main/java/seedu/staffsnap/commons/util/StringUtil.java +++ b/src/main/java/seedu/staffsnap/commons/util/StringUtil.java @@ -31,13 +31,32 @@ public static boolean containsWordIgnoreCase(String sentence, String word) { checkArgument(!preppedWord.isEmpty(), "Word parameter cannot be empty"); checkArgument(preppedWord.split("\\s+").length == 1, "Word parameter should be a single word"); - String preppedSentence = sentence; - String[] wordsInPreppedSentence = preppedSentence.split("\\s+"); + String[] wordsInPreppedSentence = sentence.split("\\s+"); return Arrays.stream(wordsInPreppedSentence) .anyMatch(sentenceWord -> sentenceWord.toLowerCase().contains(preppedWord.toLowerCase())); } + /** + * Returns true if the {@code sentence} contains the {@code word}. + * Ignores case, but a full word match is required. + *
examples:
+     *       containsWordIgnoreCase("ABc def", "abc") == true
+     *       containsWordIgnoreCase("ABc def", "DEF") == true
+     *       containsWordIgnoreCase("ABc def", "AB") == false //not a full word match
+     *       
+ * @param sentence cannot be null + * @param word cannot be null, cannot be empty, must be a single word + */ + public static boolean containsStringIgnoreCase(String sentence, String word) { + requireNonNull(sentence); + requireNonNull(word); + + String preppedWord = word.trim(); + checkArgument(!preppedWord.isEmpty(), "Word parameter cannot be empty"); + + return sentence.contains(word); + } /** * Returns a detailed message of the t, including the stack trace. */ diff --git a/src/main/java/seedu/staffsnap/model/applicant/CustomFilterPredicate.java b/src/main/java/seedu/staffsnap/model/applicant/CustomFilterPredicate.java index 454aef1983b..2b6882631b1 100644 --- a/src/main/java/seedu/staffsnap/model/applicant/CustomFilterPredicate.java +++ b/src/main/java/seedu/staffsnap/model/applicant/CustomFilterPredicate.java @@ -23,7 +23,7 @@ public class CustomFilterPredicate implements Predicate { @Override public boolean test(Applicant applicant) { if (this.name != null) { - if (!StringUtil.containsWordIgnoreCase(applicant.getName().toString(), this.name.toString())) { + if (!StringUtil.containsStringIgnoreCase(applicant.getName().toString(), this.name.toString())) { return false; } } diff --git a/src/test/java/seedu/staffsnap/logic/commands/FilterCommandTest.java b/src/test/java/seedu/staffsnap/logic/commands/FilterCommandTest.java new file mode 100644 index 00000000000..b75930a631c --- /dev/null +++ b/src/test/java/seedu/staffsnap/logic/commands/FilterCommandTest.java @@ -0,0 +1,121 @@ +package seedu.staffsnap.logic.commands; + +import org.junit.jupiter.api.Test; +import seedu.staffsnap.model.Model; +import seedu.staffsnap.model.ModelManager; +import seedu.staffsnap.model.UserPrefs; +import seedu.staffsnap.model.applicant.CustomFilterPredicate; +import seedu.staffsnap.model.applicant.Email; +import seedu.staffsnap.model.applicant.Name; +import seedu.staffsnap.model.applicant.Phone; +import seedu.staffsnap.model.applicant.Position; +import seedu.staffsnap.model.interview.Interview; + +import java.util.Arrays; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; +import static seedu.staffsnap.logic.Messages.MESSAGE_APPLICANTS_LISTED_OVERVIEW; +import static seedu.staffsnap.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.staffsnap.testutil.TypicalApplicants.ALICE; +import static seedu.staffsnap.testutil.TypicalApplicants.BENSON; +import static seedu.staffsnap.testutil.TypicalApplicants.CARL; +import static seedu.staffsnap.testutil.TypicalApplicants.DANIEL; +import static seedu.staffsnap.testutil.TypicalApplicants.ELLE; +import static seedu.staffsnap.testutil.TypicalApplicants.FIONA; +import static seedu.staffsnap.testutil.TypicalApplicants.getTypicalApplicantBook; + +class FilterCommandTest { + + private Model model = new ModelManager(getTypicalApplicantBook(), new UserPrefs()); + private Model expectedModel = new ModelManager(getTypicalApplicantBook(), new UserPrefs()); + + @Test + public void equals() { + Name name1 = BENSON.getName(); + Phone phone1 = BENSON.getPhone(); + Email email1 = BENSON.getEmail(); + Position position1 = BENSON.getPosition(); + Set interviewList1 = BENSON.getInterviews(); + + Name name2 = CARL.getName(); + Phone phone2 = CARL.getPhone(); + Email email2 = CARL.getEmail(); + Position position2 = CARL.getPosition(); + Set interviewList2 = CARL.getInterviews(); + + CustomFilterPredicate firstPredicate = new CustomFilterPredicate(name1, phone1, email1, position1, interviewList1); + CustomFilterPredicate secondPredicate = new CustomFilterPredicate(name2, phone2, email2, position2, interviewList2); + + FilterCommand filterFirstCommand = new FilterCommand(firstPredicate); + FilterCommand filterSecondCommand = new FilterCommand(secondPredicate); + + // same object -> returns true + assertTrue(filterFirstCommand.equals(filterFirstCommand)); + + // same values -> returns true + FilterCommand findFirstCommandCopy = new FilterCommand(firstPredicate); + assertTrue(filterFirstCommand.equals(findFirstCommandCopy)); + + // different types -> returns false + assertFalse(filterFirstCommand.equals(1)); + + // null -> returns false + assertFalse(filterFirstCommand.equals(null)); + + // different applicant -> returns false + assertFalse(filterFirstCommand.equals(filterSecondCommand)); + } + + @Test + public void execute_zeroKeywords_allApplicantsFound() { + String expectedMessage = String.format(MESSAGE_APPLICANTS_LISTED_OVERVIEW, 7); + CustomFilterPredicate predicate = new CustomFilterPredicate(null, null, null, null, null); + FilterCommand command = new FilterCommand(predicate); + expectedModel.updateFilteredApplicantList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(expectedModel.getFilteredApplicantList(), model.getFilteredApplicantList()); + } + + @Test + public void execute_partialName_multipleApplicantsFound() { + String expectedMessage = String.format(MESSAGE_APPLICANTS_LISTED_OVERVIEW, 4); + CustomFilterPredicate predicate = new CustomFilterPredicate(new Name("a"), null, null, null, null); + FilterCommand command = new FilterCommand(predicate); + expectedModel.updateFilteredApplicantList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(ALICE, CARL, DANIEL, FIONA), model.getFilteredApplicantList()); + } + + @Test + public void execute_multipleKeywords_singleApplicantFound() { + String expectedMessage = String.format(MESSAGE_APPLICANTS_LISTED_OVERVIEW, 1); + CustomFilterPredicate predicate = new CustomFilterPredicate(FIONA.getName(), FIONA.getPhone(), null, null, null); + FilterCommand command = new FilterCommand(predicate); + expectedModel.updateFilteredApplicantList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(FIONA), model.getFilteredApplicantList()); + } + + @Test + public void execute_multipleKeywords_zeroApplicantsFound() { + String expectedMessage = String.format(MESSAGE_APPLICANTS_LISTED_OVERVIEW, 0); + CustomFilterPredicate predicate = new CustomFilterPredicate( + ALICE.getName(), BENSON.getPhone(), CARL.getEmail(), DANIEL.getPosition(), ELLE.getInterviews()); + FilterCommand command = new FilterCommand(predicate); + expectedModel.updateFilteredApplicantList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + System.out.println(model.getFilteredApplicantList()); + assertEquals(Arrays.asList(), model.getFilteredApplicantList()); + } + + + @Test + public void toStringMethod() { + CustomFilterPredicate predicate = new CustomFilterPredicate(FIONA.getName(), null, null, null, null); + FilterCommand findCommand = new FilterCommand(predicate); + String expected = FilterCommand.class.getCanonicalName() + "{predicate=" + predicate + "}"; + assertEquals(expected, findCommand.toString()); + } + +} \ No newline at end of file From 4ae04a1fafe423b73dbd53de315a844dab57fbe6 Mon Sep 17 00:00:00 2001 From: Ivan Lee <84584280+ivanleekk@users.noreply.github.com> Date: Fri, 20 Oct 2023 01:00:30 +0800 Subject: [PATCH 03/42] Add StatusCommand --- .../java/seedu/staffsnap/logic/Messages.java | 2 + .../logic/commands/StatusCommand.java | 79 +++++++++++++++++++ .../logic/parser/ApplicantBookParser.java | 5 ++ .../logic/parser/StatusCommandParser.java | 41 ++++++++++ .../staffsnap/model/applicant/Applicant.java | 19 +++++ .../staffsnap/model/applicant/Status.java | 21 +++++ 6 files changed, 167 insertions(+) create mode 100644 src/main/java/seedu/staffsnap/logic/commands/StatusCommand.java create mode 100644 src/main/java/seedu/staffsnap/logic/parser/StatusCommandParser.java create mode 100644 src/main/java/seedu/staffsnap/model/applicant/Status.java diff --git a/src/main/java/seedu/staffsnap/logic/Messages.java b/src/main/java/seedu/staffsnap/logic/Messages.java index a5487277528..303ab443b15 100644 --- a/src/main/java/seedu/staffsnap/logic/Messages.java +++ b/src/main/java/seedu/staffsnap/logic/Messages.java @@ -45,6 +45,8 @@ public static String format(Applicant applicant) { .append(applicant.getPosition()) .append("; Interviews: "); applicant.getInterviews().forEach(builder::append); + builder.append("; Status: ") + .append(applicant.getStatus()); return builder.toString(); } diff --git a/src/main/java/seedu/staffsnap/logic/commands/StatusCommand.java b/src/main/java/seedu/staffsnap/logic/commands/StatusCommand.java new file mode 100644 index 00000000000..9000c9955ee --- /dev/null +++ b/src/main/java/seedu/staffsnap/logic/commands/StatusCommand.java @@ -0,0 +1,79 @@ +package seedu.staffsnap.logic.commands; + +import seedu.staffsnap.commons.core.index.Index; +import seedu.staffsnap.logic.Messages; +import seedu.staffsnap.logic.commands.exceptions.CommandException; +import seedu.staffsnap.model.Model; +import seedu.staffsnap.model.applicant.Applicant; +import seedu.staffsnap.model.applicant.Status; + +import java.util.List; + +import static java.util.Objects.requireNonNull; + +/** + * Edits the details of an existing applicant in the applicant book. + */ +public class StatusCommand extends Command { + + public static final String COMMAND_WORD = "status"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the status of the applicant identified " + "by the index number used in the displayed applicant list. " + "Existing values will be overwritten by the input values.\n" + "Parameters: INDEX (must be a positive integer) " + "[u(ndecided)/o(ffered)/r(ejected)]."; + + public static final String MESSAGE_EDIT_STATUS_SUCCESS = "Edited Applicant Status: %1$s"; + public static final String MESSAGE_NO_STATUS = "Missing Status, please follow the following parameters." + + "Parameters: INDEX (must be a positive integer) " + + "[u(ndecided)/o(ffered)/r(ejected)]."; + + private final Index index; + private final Status status; + + /** + * @param index of the applicant in the filtered applicant list to edit + * @param status The status to assign to the applicant + */ + public StatusCommand(Index index, Status status) { + requireNonNull(index); + requireNonNull(status); + + this.index = index; + this.status = status; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredApplicantList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_APPLICANT_DISPLAYED_INDEX); + } + + Applicant applicantToEdit = lastShownList.get(index.getZeroBased()); + applicantToEdit.setStatus(this.status); + System.out.println(applicantToEdit.getStatus()); + + return new CommandResult(String.format(MESSAGE_EDIT_STATUS_SUCCESS, Messages.format(applicantToEdit))); + } + + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof StatusCommand)) { + return false; + } + + StatusCommand otherStatusCommand = (StatusCommand) other; + return index.equals(otherStatusCommand.index) && status.equals(otherStatusCommand.status); + } + + @Override + public String toString() { + return "StatusCommand{" + "index=" + index + ", status=" + status + '}'; + } +} diff --git a/src/main/java/seedu/staffsnap/logic/parser/ApplicantBookParser.java b/src/main/java/seedu/staffsnap/logic/parser/ApplicantBookParser.java index c5590ba7319..3d668cbb31d 100644 --- a/src/main/java/seedu/staffsnap/logic/parser/ApplicantBookParser.java +++ b/src/main/java/seedu/staffsnap/logic/parser/ApplicantBookParser.java @@ -20,6 +20,7 @@ import seedu.staffsnap.logic.commands.HelpCommand; import seedu.staffsnap.logic.commands.ListCommand; import seedu.staffsnap.logic.commands.SortCommand; +import seedu.staffsnap.logic.commands.StatusCommand; import seedu.staffsnap.logic.parser.exceptions.ParseException; /** @@ -103,6 +104,10 @@ public Command parseCommand(String userInput) throws ParseException { case AddInterviewCommand.COMMAND_WORD: return new AddInterviewCommandParser().parse(arguments); + case StatusCommand.COMMAND_WORD: + return new StatusCommandParser().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/staffsnap/logic/parser/StatusCommandParser.java b/src/main/java/seedu/staffsnap/logic/parser/StatusCommandParser.java new file mode 100644 index 00000000000..cc2559b760d --- /dev/null +++ b/src/main/java/seedu/staffsnap/logic/parser/StatusCommandParser.java @@ -0,0 +1,41 @@ +package seedu.staffsnap.logic.parser; + +import seedu.staffsnap.commons.core.index.Index; +import seedu.staffsnap.logic.commands.StatusCommand; +import seedu.staffsnap.logic.parser.exceptions.ParseException; +import seedu.staffsnap.model.applicant.Status; + +import java.util.stream.Stream; + +import static seedu.staffsnap.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +public class StatusCommandParser { + /** + * Parses the given {@code String} of arguments in the context of the AddCommand + * and returns an AddCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public StatusCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args); + + if (argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, StatusCommand.MESSAGE_USAGE)); + } + String[] splitString = argMultimap.getPreamble().split("\\s+"); + Index index = ParserUtil.parseIndex(splitString[0]); + Status status = Status.findByName(splitString[1]); + if (status == null) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, StatusCommand.MESSAGE_NO_STATUS)); + } + return new StatusCommand(index, status); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/main/java/seedu/staffsnap/model/applicant/Applicant.java b/src/main/java/seedu/staffsnap/model/applicant/Applicant.java index e91941f9c11..b6748d18f94 100644 --- a/src/main/java/seedu/staffsnap/model/applicant/Applicant.java +++ b/src/main/java/seedu/staffsnap/model/applicant/Applicant.java @@ -26,6 +26,7 @@ public class Applicant implements Comparable { private final Email email; private final Position position; private final List interviews = new ArrayList<>(); + private Status status; /** * Every field must be present and not null. @@ -37,6 +38,7 @@ public Applicant(Name name, Phone phone, Email email, Position position, List Date: Fri, 20 Oct 2023 01:23:41 +0800 Subject: [PATCH 04/42] Fix checkstyle issue --- .../seedu/staffsnap/logic/commands/StatusCommand.java | 11 ++++++++--- .../staffsnap/logic/parser/StatusCommandParser.java | 10 +++++++--- .../java/seedu/staffsnap/model/applicant/Status.java | 5 ++++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/staffsnap/logic/commands/StatusCommand.java b/src/main/java/seedu/staffsnap/logic/commands/StatusCommand.java index 9000c9955ee..9196485a27f 100644 --- a/src/main/java/seedu/staffsnap/logic/commands/StatusCommand.java +++ b/src/main/java/seedu/staffsnap/logic/commands/StatusCommand.java @@ -1,5 +1,9 @@ package seedu.staffsnap.logic.commands; +import static java.util.Objects.requireNonNull; + +import java.util.List; + import seedu.staffsnap.commons.core.index.Index; import seedu.staffsnap.logic.Messages; import seedu.staffsnap.logic.commands.exceptions.CommandException; @@ -7,9 +11,7 @@ import seedu.staffsnap.model.applicant.Applicant; import seedu.staffsnap.model.applicant.Status; -import java.util.List; -import static java.util.Objects.requireNonNull; /** * Edits the details of an existing applicant in the applicant book. @@ -18,7 +20,10 @@ public class StatusCommand extends Command { public static final String COMMAND_WORD = "status"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the status of the applicant identified " + "by the index number used in the displayed applicant list. " + "Existing values will be overwritten by the input values.\n" + "Parameters: INDEX (must be a positive integer) " + "[u(ndecided)/o(ffered)/r(ejected)]."; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the status of the applicant identified " + + "by the index number used in the displayed applicant list. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + "[u(ndecided)/o(ffered)/r(ejected)]."; public static final String MESSAGE_EDIT_STATUS_SUCCESS = "Edited Applicant Status: %1$s"; public static final String MESSAGE_NO_STATUS = "Missing Status, please follow the following parameters." diff --git a/src/main/java/seedu/staffsnap/logic/parser/StatusCommandParser.java b/src/main/java/seedu/staffsnap/logic/parser/StatusCommandParser.java index cc2559b760d..1217f8b12f1 100644 --- a/src/main/java/seedu/staffsnap/logic/parser/StatusCommandParser.java +++ b/src/main/java/seedu/staffsnap/logic/parser/StatusCommandParser.java @@ -1,14 +1,18 @@ package seedu.staffsnap.logic.parser; +import static seedu.staffsnap.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.stream.Stream; + import seedu.staffsnap.commons.core.index.Index; import seedu.staffsnap.logic.commands.StatusCommand; import seedu.staffsnap.logic.parser.exceptions.ParseException; import seedu.staffsnap.model.applicant.Status; -import java.util.stream.Stream; - -import static seedu.staffsnap.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +/** + * Parses inputs and returns a StatusCommand + */ public class StatusCommandParser { /** * Parses the given {@code String} of arguments in the context of the AddCommand diff --git a/src/main/java/seedu/staffsnap/model/applicant/Status.java b/src/main/java/seedu/staffsnap/model/applicant/Status.java index 13d0aed81bb..3ed06c2cdab 100644 --- a/src/main/java/seedu/staffsnap/model/applicant/Status.java +++ b/src/main/java/seedu/staffsnap/model/applicant/Status.java @@ -1,5 +1,8 @@ package seedu.staffsnap.model.applicant; +/** + * Status enum for valid statuses + */ public enum Status { UNDECIDED, OFFERED, REJECTED; @@ -11,7 +14,7 @@ public enum Status { public static Status findByName(String name) { Status result = null; for (Status status : values()) { - if (status.name().equalsIgnoreCase(name) || status.name().substring(0,1).equalsIgnoreCase(name)) { + if (status.name().equalsIgnoreCase(name) || status.name().substring(0, 1).equalsIgnoreCase(name)) { result = status; break; } From 601a6612b044aade55934810567b7867aceec038 Mon Sep 17 00:00:00 2001 From: Ivan Lee <84584280+ivanleekk@users.noreply.github.com> Date: Fri, 20 Oct 2023 01:27:49 +0800 Subject: [PATCH 05/42] Add status to UI --- src/main/java/seedu/staffsnap/ui/ApplicantCard.java | 3 +++ src/main/resources/view/ApplicantListCard.fxml | 1 + 2 files changed, 4 insertions(+) diff --git a/src/main/java/seedu/staffsnap/ui/ApplicantCard.java b/src/main/java/seedu/staffsnap/ui/ApplicantCard.java index e09a77388ee..42310094920 100644 --- a/src/main/java/seedu/staffsnap/ui/ApplicantCard.java +++ b/src/main/java/seedu/staffsnap/ui/ApplicantCard.java @@ -40,6 +40,8 @@ public class ApplicantCard extends UiPart { private Label email; @FXML private FlowPane interviews; + @FXML + private Label status; /** * Creates a {@code ApplicantCode} with the given {@code Applicant} and index to display. @@ -55,5 +57,6 @@ public ApplicantCard(Applicant applicant, int displayedIndex) { applicant.getInterviews().stream() .sorted(Comparator.comparing(interview -> interview.type)) .forEach(interview -> interviews.getChildren().add(new Label(interview.type))); + status.setText(applicant.getStatus().toString()); } } diff --git a/src/main/resources/view/ApplicantListCard.fxml b/src/main/resources/view/ApplicantListCard.fxml index b8b7450bf45..a0dbf79b1b9 100644 --- a/src/main/resources/view/ApplicantListCard.fxml +++ b/src/main/resources/view/ApplicantListCard.fxml @@ -25,6 +25,7 @@