diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java index bc9ad64dec6..4d50d6698b1 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/address/MainApp.java @@ -50,6 +50,7 @@ public class MainApp extends Application { protected Model model; protected Config config; + @Override public void init() throws Exception { logger.info("=============================[ Initializing AddressBook ]==========================="); diff --git a/src/main/java/seedu/address/logic/commands/AddGradeCommand.java b/src/main/java/seedu/address/logic/commands/AddGradeCommand.java index e013e5a26e1..bf5531d69ab 100644 --- a/src/main/java/seedu/address/logic/commands/AddGradeCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddGradeCommand.java @@ -80,14 +80,25 @@ private static Person createGradeToAddToPerson(Person person, String assignmentN @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - // to add error handling + // check if assignment is in predefined list + if (!model.hasAssignment(assignmentName)) { + throw new CommandException("Invalid assignment name: " + assignmentName); + } + + if (score > model.maxScore(assignmentName) || score < 0) { + throw new CommandException("Score must be between 0.0 and " + model.maxScore(assignmentName)); + } + + if (!model.hasName(personName)) { + throw new CommandException("Person " + personName + " not in address book"); + } Person person = model.getAddressBook().getPersonList().stream() - .filter(p -> p.getName().equals(personName)) + .filter(p -> p.getName().equalIgnoreCase(personName)) .toList() .get(0); - model.setPerson(person, createGradeToAddToPerson(person, assignmentName, score)); + model.setPerson(person, createGradeToAddToPerson(person, model.getAssignmentName(assignmentName), score)); return new CommandResult(""); // placeholder string to be added } diff --git a/src/main/java/seedu/address/logic/parser/AddGradeCommandParser.java b/src/main/java/seedu/address/logic/parser/AddGradeCommandParser.java index a3c3af3e66f..c2a11ca9fe3 100644 --- a/src/main/java/seedu/address/logic/parser/AddGradeCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddGradeCommandParser.java @@ -1,10 +1,13 @@ 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_ASSIGNMENT; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; import static seedu.address.logic.parser.CliSyntax.PREFIX_SCORE; +import java.util.stream.Stream; + import seedu.address.logic.commands.AddGradeCommand; import seedu.address.logic.parser.exceptions.ParseException; @@ -16,11 +19,50 @@ public class AddGradeCommandParser implements Parser { @Override public AddGradeCommand parse(String args) throws ParseException { requireNonNull(args); + + // Tokenize the input arguments ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_ASSIGNMENT, PREFIX_SCORE); - String name = argMultimap.getValue(PREFIX_NAME).get(); - Float score = ParserUtil.parseScore(argMultimap.getValue(PREFIX_SCORE).get()); - String assignmentName = ParserUtil.parseAssignmentName(argMultimap.getValue(PREFIX_ASSIGNMENT).get()); + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_ASSIGNMENT, PREFIX_SCORE); + + // Check that all required prefixes are present + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ASSIGNMENT, PREFIX_SCORE) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddGradeCommand.MESSAGE_USAGE)); + } + + // Parse and validate the name + String name = argMultimap.getValue(PREFIX_NAME).orElse("").trim(); + if (name.isEmpty()) { + throw new ParseException("Name cannot be empty."); + } + // Get and validate the assignment name + String assignmentName = argMultimap.getValue(PREFIX_ASSIGNMENT).orElse("").trim(); + if (assignmentName.isEmpty()) { + throw new ParseException("Assignment name cannot be empty."); + } + + // Get and validate the score as a string + String scoreString = argMultimap.getValue(PREFIX_SCORE).orElse("").trim(); + if (scoreString.isEmpty()) { + throw new ParseException("Score cannot be empty."); + } + + Float score; + try { + score = Float.parseFloat(scoreString); + } catch (NumberFormatException e) { + throw new ParseException("Score must be a valid number."); + } + + // Create and return the AddGradeCommand return new AddGradeCommand(name, score, assignmentName); } + + /** + * 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/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index 73397161e84..baebc0e6a76 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -6,6 +6,7 @@ import javafx.collections.ObservableList; import seedu.address.commons.util.ToStringBuilder; +import seedu.address.model.person.Name; import seedu.address.model.person.Person; import seedu.address.model.person.UniquePersonList; @@ -123,6 +124,21 @@ public boolean equals(Object other) { return persons.equals(otherAddressBook.persons); } + /** + * Checks if name is in address book, ignoring casing + * + * @param name the name + * @return the boolean + */ + public boolean hasName(Name name) { + for (Person x : persons) { + if (x.getName().equalIgnoreCase(name)) { + return true; + } + } + return false; + } + @Override public int hashCode() { return persons.hashCode(); diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index 09165577fae..1f6cb316bcb 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -5,6 +5,7 @@ import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; +import seedu.address.model.person.Name; import seedu.address.model.person.Person; /** @@ -96,4 +97,8 @@ public interface Model { * Returns true if an assignment with the name is present. */ boolean hasAssignment(String name); + + float maxScore(String assignment); + String getAssignmentName(String name); + boolean hasName(Name name); } diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 618211e1e62..bdbbae0c98d 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -13,6 +13,7 @@ import seedu.address.commons.core.LogsCenter; import seedu.address.model.assignment.PredefinedAssignmentsData; import seedu.address.model.assignment.ReadOnlyPredefinedAssignmentsData; +import seedu.address.model.person.Name; import seedu.address.model.person.Person; /** @@ -154,6 +155,18 @@ public boolean equals(Object other) { //=========== Predefined assignments accessors ============================================================= public boolean hasAssignment(String name) { - return predefinedAssignmentsData.hasPerson(name); + return predefinedAssignmentsData.hasAssignment(name); + } + + public float maxScore(String assignment) { + return predefinedAssignmentsData.maxScore(assignment); + } + + public String getAssignmentName(String name) { + return predefinedAssignmentsData.getAssignmentName(name); + } + + public boolean hasName(Name name) { + return addressBook.hasName(name); } } diff --git a/src/main/java/seedu/address/model/assignment/Assignment.java b/src/main/java/seedu/address/model/assignment/Assignment.java index 77412abd80c..98680d7f2a6 100644 --- a/src/main/java/seedu/address/model/assignment/Assignment.java +++ b/src/main/java/seedu/address/model/assignment/Assignment.java @@ -18,6 +18,7 @@ public Assignment(String assignmentName, float score) { this.score = score; } + @Override public String toString() { return "Assignment:" + assignmentName + " " + score; diff --git a/src/main/java/seedu/address/model/assignment/PredefinedAssignment.java b/src/main/java/seedu/address/model/assignment/PredefinedAssignment.java index 485b4f86d75..d57107b48e3 100644 --- a/src/main/java/seedu/address/model/assignment/PredefinedAssignment.java +++ b/src/main/java/seedu/address/model/assignment/PredefinedAssignment.java @@ -8,8 +8,12 @@ public record PredefinedAssignment(String name, Float maxScore) { @Override public boolean equals(Object obj) { if (obj instanceof PredefinedAssignment other) { - return name.equals(other.name) && maxScore.equals(other.maxScore()); + return name.equalsIgnoreCase(other.name) && maxScore.equals(other.maxScore()); } return false; } + + public float getMaxScore() { + return maxScore; + } } diff --git a/src/main/java/seedu/address/model/assignment/PredefinedAssignmentsData.java b/src/main/java/seedu/address/model/assignment/PredefinedAssignmentsData.java index 43c28e4239d..e2a9d7a88e4 100644 --- a/src/main/java/seedu/address/model/assignment/PredefinedAssignmentsData.java +++ b/src/main/java/seedu/address/model/assignment/PredefinedAssignmentsData.java @@ -3,7 +3,7 @@ import static java.util.Objects.requireNonNull; import java.util.ArrayList; -import java.util.Objects; + /** * Wraps all data at the assignment data level. @@ -42,12 +42,36 @@ public void addPredefinedAssignment(PredefinedAssignment predefinedAssignment) { * @param name The name of the assignment to be checked. * @return True if present, false otherwise. */ - public boolean hasPerson(String name) { + public boolean hasAssignment(String name) { for (PredefinedAssignment assignment : predefinedAssignmentArrayList) { - if (Objects.equals(assignment.name(), name)) { + if (assignment.name().equalsIgnoreCase(name)) { return true; } } return false; } + + /** + * Returns the max score of an assignment. + * + * @param name the name of assignment + * @return the float (max score) + */ + public float maxScore(String name) { + for (PredefinedAssignment assignment : predefinedAssignmentArrayList) { + if (assignment.name().equalsIgnoreCase(name)) { + return assignment.maxScore(); + } + } + return -1; + } + + public String getAssignmentName(String name) { + for (PredefinedAssignment assignment : predefinedAssignmentArrayList) { + if (assignment.name().equalsIgnoreCase(name)) { + return assignment.name(); + } + } + return null; + } } diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/person/Name.java index 173f15b9b00..5a46bce4850 100644 --- a/src/main/java/seedu/address/model/person/Name.java +++ b/src/main/java/seedu/address/model/person/Name.java @@ -59,6 +59,18 @@ public boolean equals(Object other) { return fullName.equals(otherName.fullName); } + /** + * Ignore case while comparing name. + * + * @param name the name + * @return the boolean + */ + public boolean equalIgnoreCase(Name name) { + if (name.fullName.equalsIgnoreCase(this.fullName)) { + return true; + } + return false; + } @Override public int hashCode() { return fullName.hashCode(); diff --git a/src/main/java/seedu/address/storage/assignment/JsonSerializablePredefinedAssignmentData.java b/src/main/java/seedu/address/storage/assignment/JsonSerializablePredefinedAssignmentData.java index acfa673a5c8..213663aeb3d 100644 --- a/src/main/java/seedu/address/storage/assignment/JsonSerializablePredefinedAssignmentData.java +++ b/src/main/java/seedu/address/storage/assignment/JsonSerializablePredefinedAssignmentData.java @@ -28,7 +28,7 @@ public JsonSerializablePredefinedAssignmentData( public PredefinedAssignmentsData toModelType() throws IllegalValueException { PredefinedAssignmentsData predefinedAssignmentsData = new PredefinedAssignmentsData(); for (JsonAdaptedPredefinedAssignment jsonAdaptedPredefinedAssignment : assignments) { - if (predefinedAssignmentsData.hasPerson(jsonAdaptedPredefinedAssignment.getName())) { + if (predefinedAssignmentsData.hasAssignment(jsonAdaptedPredefinedAssignment.getName())) { throw new IllegalValueException(DUPLICATE_PREDEFINED_ASSIGNMENT); } predefinedAssignmentsData.addPredefinedAssignment(jsonAdaptedPredefinedAssignment.toModelType()); diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java index c2c30700ee1..8485b6c706b 100644 --- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/AddCommandTest.java @@ -22,6 +22,7 @@ import seedu.address.model.Model; import seedu.address.model.ReadOnlyAddressBook; import seedu.address.model.ReadOnlyUserPrefs; +import seedu.address.model.person.Name; import seedu.address.model.person.Person; import seedu.address.testutil.PersonBuilder; @@ -162,6 +163,21 @@ public void updateFilteredPersonList(Predicate predicate) { public boolean hasAssignment(String name) { throw new AssertionError("This method should not be called"); } + + @Override + public float maxScore(String assignment) { + throw new AssertionError("This method should not be called"); + } + + @Override + public String getAssignmentName(String name) { + throw new AssertionError("This method should not be called"); + } + + @Override + public boolean hasName(Name name) { + throw new AssertionError("This method should not be called"); + } } /** diff --git a/src/test/java/seedu/address/logic/commands/AddGradeCommandTest.java b/src/test/java/seedu/address/logic/commands/AddGradeCommandTest.java index 8aa8cc4346e..5f17db4b344 100644 --- a/src/test/java/seedu/address/logic/commands/AddGradeCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/AddGradeCommandTest.java @@ -7,23 +7,61 @@ import org.junit.jupiter.api.Test; +import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; import seedu.address.model.ModelManager; import seedu.address.model.UserPrefs; +import seedu.address.model.assignment.PredefinedAssignment; import seedu.address.model.assignment.PredefinedAssignmentsData; import seedu.address.model.person.Person; import seedu.address.testutil.TypicalPersons; public class AddGradeCommandTest { private final Model model = new ModelManager( - getTypicalAddressBook(), - new UserPrefs(), - new PredefinedAssignmentsData()); + getTypicalAddressBook(), + new UserPrefs(), + AddGradeCommandTest.getPredefinedAssignmentsData()); + + + public static PredefinedAssignmentsData getPredefinedAssignmentsData() { + PredefinedAssignmentsData predefinedAssignmentsData = new PredefinedAssignmentsData(); + predefinedAssignmentsData.addPredefinedAssignment(new PredefinedAssignment("Ex01", 10.0f)); + predefinedAssignmentsData.addPredefinedAssignment(new PredefinedAssignment("Ex02", 10.0f)); + predefinedAssignmentsData.addPredefinedAssignment(new PredefinedAssignment("Ex09", 10.0f)); + return predefinedAssignmentsData; + } + + @Test public void constructor_nullAssignmentFormat_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new AddGradeCommand(null, 0f, null)); } + @Test + public void assignment_invalidName() { + AddGradeCommand command = new AddGradeCommand("John Doe", 0f, "ex10"); + assertThrows(CommandException.class, () -> command.execute(model)); + } + @Test + public void person_invalidName() { + AddGradeCommand command = new AddGradeCommand("John DoeDoedoe", 0f, "ex01"); + assertThrows(CommandException.class, () -> command.execute(model)); + } + + @Test + public void assignment_invalidHighScore() { + AddGradeCommand command = new AddGradeCommand("John Doe", + 100f, "ex01"); + assertThrows(CommandException.class, () -> command.execute(model)); + } + + @Test + public void assignment_invalidLowScore() { + AddGradeCommand command = new AddGradeCommand("John Doe", + -1f, "ex01"); + assertThrows(CommandException.class, () -> command.execute(model)); + } @Test @@ -38,14 +76,16 @@ public void execute_validPersonGrade_success() throws Exception { AddGradeCommand command = new AddGradeCommand( testPerson.getName().toString(), 9.0f, - "Ex09"); + "Ex02"); command.execute(model); assertEquals(model .getAddressBook() .getPersonList() .stream().filter(person -> person .getName() - .equals(testPerson.getName())).toList().get(0).getAssignment().getScore(), 9.0f); + .equalIgnoreCase(testPerson.getName())).toList().get(0).getAssignment().getScore(), 9.0f); } + + } diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java index d4639f842b9..bb5700d0ad6 100644 --- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java +++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java @@ -62,6 +62,7 @@ public class CommandTestUtil { public static final String ASSIGNMENT_DESC_ONE = " " + PREFIX_ASSIGNMENT + VALID_ASSIGNMENT_ONE; public static final String SCORE_DESC = " " + PREFIX_SCORE + VALID_SCORE; + public static final String GITHUB_DESC_AMY = " " + PREFIX_GITHUB + VALID_GITHUB_AMY; public static final String GITHUB_DESC_BOB = " " + PREFIX_GITHUB + VALID_GITHUB_BOB; public static final String INVALID_NAME_DESC = " " + PREFIX_NAME + "James&"; // '&' not allowed in names @@ -69,6 +70,11 @@ public class CommandTestUtil { 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_ASSIGNMENT_ONE = " " + + PREFIX_ASSIGNMENT + "Ex1000"; //assignment that will not be in database + + public static final float INVALID_SCORE_BELOW_ZERO = -1f; //score that is <0 + public static final float INVALID_SCORE_ABOVE = 10000f; //score that is <0 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/parser/AddGradeCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddGradeCommandParserTest.java index c4e62784c57..ebbcf7ad5dc 100644 --- a/src/test/java/seedu/address/logic/parser/AddGradeCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/AddGradeCommandParserTest.java @@ -1,12 +1,17 @@ package seedu.address.logic.parser; +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import static seedu.address.logic.commands.CommandTestUtil.ASSIGNMENT_DESC_ONE; import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; import static seedu.address.logic.commands.CommandTestUtil.SCORE_DESC; import static seedu.address.logic.commands.CommandTestUtil.VALID_ASSIGNMENT_ONE; import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; import static seedu.address.logic.commands.CommandTestUtil.VALID_SCORE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ASSIGNMENT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SCORE; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; import org.junit.jupiter.api.Test; @@ -26,4 +31,42 @@ public void parse_allFieldsSpecified_success() throws ParseException { VALID_ASSIGNMENT_ONE); assertParseSuccess(parser, userInput, expectedCommand); } + + @Test + public void parse_notAllFieldSpecified_error() throws ParseException { + String userInput = NAME_DESC_AMY + SCORE_DESC; + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddGradeCommand.MESSAGE_USAGE); + assertParseFailure(parser, userInput, expectedMessage); + } + + + @Test + public void parse_emptyNameError() throws ParseException { + String userInput = " " + PREFIX_NAME + ASSIGNMENT_DESC_ONE + SCORE_DESC; + String expectedMessage = "Name cannot be empty."; + assertParseFailure(parser, userInput, expectedMessage); + } + + @Test + public void parse_emptyAsgnNameError() throws ParseException { + String userInput = NAME_DESC_AMY + " " + PREFIX_ASSIGNMENT + SCORE_DESC; + String expectedMessage = "Assignment name cannot be empty."; + assertParseFailure(parser, userInput, expectedMessage); + } + + @Test + public void parse_emptyScoreError() throws ParseException { + String userInput = NAME_DESC_AMY + ASSIGNMENT_DESC_ONE + " " + PREFIX_SCORE; + String expectedMessage = "Score cannot be empty."; + assertParseFailure(parser, userInput, expectedMessage); + } + + + @Test + public void parse_invalidScoreError() throws ParseException { + String userInput = NAME_DESC_AMY + ASSIGNMENT_DESC_ONE + " " + PREFIX_SCORE + "s"; + String expectedMessage = "Score must be a valid number."; + assertParseFailure(parser, userInput, expectedMessage); + } + } diff --git a/src/test/java/seedu/address/testutil/PersonBuilder.java b/src/test/java/seedu/address/testutil/PersonBuilder.java index 54f3ab596db..70703b86a96 100644 --- a/src/test/java/seedu/address/testutil/PersonBuilder.java +++ b/src/test/java/seedu/address/testutil/PersonBuilder.java @@ -25,7 +25,7 @@ public class PersonBuilder { public static final String DEFAULT_ADDRESS = "123, Jurong West Ave 6, #08-111"; public static final String DEFAULT_TELEGRAM = "@viswa"; public static final String DEFAULT_GITHUB = "Amy"; - public static final String DEFAULT_ASSIGNMENT_NAME = "Ex01"; + public static final String DEFAULT_ASSIGNMENT_NAME = "ex01"; public static final Float DEFAULT_ASSIGNMENT_SCORE = 0f; private Name name;