diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 30b2a62503b..c7d8de0cd97 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -56,6 +56,15 @@ Name -[hidden]right-> Phone Phone -[hidden]right-> Address Address -[hidden]right-> Email +Person "1" -- "0..*" Meeting + +' AddMeetingCommand Class and its relation to Meeting +Class AddMeetingCommand { + +execute(): CommandResult +} +AddMeetingCommand ..> Meeting : creates + ModelManager --> "~* filtered" Person ModelManager --> "~* filtered" Meeting @enduml +``` diff --git a/docs/team/zixuan.md b/docs/team/zixuan.md index 333b62161d7..64a3402df8e 100644 --- a/docs/team/zixuan.md +++ b/docs/team/zixuan.md @@ -11,6 +11,10 @@ The user interacts with it using a CLI, and it has a GUI created with JavaFX. It Given below are my contributions to the project. * **New Feature**: + * Added the addMeeting command + * Edited Meeting Model class + * **Code contributed**: + * [RepoSense link](https://nus-cs2103-ay2324s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2024-02-23&tabOpen=true&tabType=authorship&tabAuthor=chewbum&tabRepo=AY2324S2-CS2103-F08-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) * **Project management**: diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java index 935dd329da5..ce7c6b639d9 100644 --- a/src/main/java/seedu/address/logic/Messages.java +++ b/src/main/java/seedu/address/logic/Messages.java @@ -5,8 +5,10 @@ import java.util.stream.Stream; import seedu.address.logic.parser.Prefix; +import seedu.address.model.meeting.Meeting; import seedu.address.model.person.Person; + /** * Container for user visible messages. */ @@ -15,7 +17,7 @@ public class Messages { public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; - public static final String MESSAFE_MISSING_MEETING_IDENTIFIER = "Error: Please specify a meeting identifier"; + public static final String MESSAGE_MISSING_MEETING_IDENTIFIER = "Error: Please specify a meeting identifier"; public static final String MESSAGE_INVALID_MEETING_DISPLAYED_INDEX = "Error: Meeting %1$d not found"; public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; public static final String MESSAGE_DUPLICATE_FIELDS = "Multiple values specified for " @@ -48,5 +50,18 @@ public static String format(Person person) { person.getTags().forEach(builder::append); return builder.toString(); } + /** + * Formats the {@code meeting} for display to the user. + */ + public static String format(Meeting meeting) { + final StringBuilder builder = new StringBuilder(); + builder.append("Client: ") + .append(meeting.getClientName()) + .append("DateTime: ") + .append(meeting.getDateTime()) + .append("; Description: ") + .append(meeting.getDescription()); + return builder.toString(); + } } diff --git a/src/main/java/seedu/address/logic/commands/AddMeetingCommand.java b/src/main/java/seedu/address/logic/commands/AddMeetingCommand.java new file mode 100644 index 00000000000..298b9260c91 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddMeetingCommand.java @@ -0,0 +1,122 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CLIENT_INDEX; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATETIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION; + +import java.time.LocalDateTime; +import java.util.List; + +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.meeting.Meeting; +import seedu.address.model.person.Person; + +/** + * Represents a command to add a meeting associated with a specific client in the address book. + * This command adds a new meeting to the address book, ensuring that no duplicate meetings are added. + * A meeting is considered a duplicate if it has the same client, date, and description as an existing meeting. + */ +public class AddMeetingCommand extends Command { + public static final String COMMAND_WORD = "addMeeting"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a meeting to the " + + "client identified by the index number. \n" + + "Parameters: client/ CLIENT_INDEX dt/ DATE_TIME /d DESCRIPTION \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_CLIENT_INDEX + "1 " + + PREFIX_DATETIME + "02-01-2024 12:00 " + + PREFIX_DESCRIPTION + "sign life plan"; + + + public static final String MESSAGE_SUCCESS = "New meeting added: %1$s"; + public static final String MESSAGE_DUPLICATE_MEETING = "This meeting already exists in the address book"; + + private final Index clientIndex; + private final LocalDateTime dateTime; + private final String description; + + /** + * Creates an {@code AddMeetingCommand} to add the specified {@code Meeting}. + * + * @param dateTime The date and time of the meeting. + * @param description A description of the meeting. + * @param clientIndex The index of the client in the filtered person list to whom the meeting is to be added. + * @throws NullPointerException if any of the input parameters are null. + */ + + public AddMeetingCommand(LocalDateTime dateTime, String description, Index clientIndex) { + if (dateTime == null || description == null || clientIndex == null) { + throw new NullPointerException(); + } + this.dateTime = dateTime; + this.description = description; + this.clientIndex = clientIndex; + } + + /** + * Executes the AddMeeting command to add a new meeting associated with a client in the address book. + *

+ * The method retrieves the client based on the index provided, creates a new meeting with the specified date, time, + * and description, and then adds this meeting to the model if it does not already exist to ensure uniqueness. + *

+ * If the specified client index is invalid or the meeting already exists, it throws a CommandException. + * + * @param model The model in which the meeting will be added. + * @return A CommandResult object containing the success message. + * @throws CommandException If the client index is invalid or the meeting already exists in the model. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredPersonList(); + + if (clientIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person client = lastShownList.get(clientIndex.getZeroBased()); + Meeting meetingToAdd = new Meeting(description, dateTime, client); + + if (model.hasMeeting(meetingToAdd) && client.hasExistingMeeting(meetingToAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_MEETING); + } + model.addMeeting(meetingToAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(meetingToAdd))); + } + + /** + * Checks if another object is equal to this AddMeetingCommand instance. + *

+ * The equality check is based on whether the other object is an instance of AddMeetingCommand + * and whether the meetings to be added are the same in terms of client, date, time, and description. + * This ensures that two AddMeetingCommand objects are considered equal if they are attempting + * to add the same meeting. + *

+ * This method is crucial for command comparisons, especially when testing or when the application + * logic requires comparing different commands. + * + * @param other The object to compare this AddMeetingCommand against. + * @return true if the given object represents an AddMeetingCommand equivalent to this instance, false otherwise. + */ + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AddMeetingCommand)) { + return false; + } + + AddMeetingCommand otherCommand = (AddMeetingCommand) other; + return dateTime.equals(otherCommand.dateTime) + && description.equals(otherCommand.description) + && clientIndex.equals(otherCommand.clientIndex); + } +} diff --git a/src/main/java/seedu/address/logic/commands/DeleteMeetingCommand.java b/src/main/java/seedu/address/logic/commands/DeleteMeetingCommand.java index fb24b6219fa..6815e66f78c 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteMeetingCommand.java +++ b/src/main/java/seedu/address/logic/commands/DeleteMeetingCommand.java @@ -18,7 +18,7 @@ */ public class DeleteMeetingCommand extends Command { - public static final String COMMAND_WORD = "delete meeting"; + public static final String COMMAND_WORD = "deleteMeeting"; public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes the meeting of a particular client identified by the " diff --git a/src/main/java/seedu/address/logic/parser/AddMeetingCommandParser.java b/src/main/java/seedu/address/logic/parser/AddMeetingCommandParser.java new file mode 100644 index 00000000000..de7df31fe15 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/AddMeetingCommandParser.java @@ -0,0 +1,52 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CLIENT_INDEX; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATETIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION; + +import java.time.LocalDateTime; +import java.util.stream.Stream; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.AddMeetingCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new AddMeetingCommand object + */ +public class AddMeetingCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddMeetingCommand + * and returns an AddMeetingCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddMeetingCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_CLIENT_INDEX, PREFIX_DATETIME, PREFIX_DESCRIPTION); + System.out.println(!arePrefixesPresent(argMultimap, PREFIX_CLIENT_INDEX, PREFIX_DATETIME, PREFIX_DESCRIPTION)); + System.out.println(!argMultimap.getPreamble().isEmpty()); + if (!arePrefixesPresent(argMultimap, PREFIX_CLIENT_INDEX, PREFIX_DATETIME, PREFIX_DESCRIPTION) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddMeetingCommand.MESSAGE_USAGE)); + } + + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_CLIENT_INDEX, PREFIX_DATETIME, PREFIX_DESCRIPTION); + Index clientIndex = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_CLIENT_INDEX).get()); + LocalDateTime dateTime = ParserUtil.parseDateTime(argMultimap.getValue(PREFIX_DATETIME).get()); + String description = ParserUtil.parseDescription(argMultimap.getValue(PREFIX_DESCRIPTION).get()); + + + return new AddMeetingCommand(dateTime, description, clientIndex); + } + + /** + * 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/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index 274d3689e85..d0eb657f93f 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -9,6 +9,7 @@ import seedu.address.commons.core.LogsCenter; import seedu.address.logic.commands.AddCommand; +import seedu.address.logic.commands.AddMeetingCommand; import seedu.address.logic.commands.ClearCommand; import seedu.address.logic.commands.Command; import seedu.address.logic.commands.DeleteCommand; @@ -18,7 +19,6 @@ import seedu.address.logic.commands.FindCommand; import seedu.address.logic.commands.HelpCommand; import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.commands.ViewCommand; import seedu.address.logic.parser.exceptions.ParseException; /** @@ -30,8 +30,6 @@ public class AddressBookParser { * Used for initial separation of command word and args. */ private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); - private static final Pattern MEETING_COMMAND_TWO_ARGS_FORMAT = Pattern - .compile("(?\\S+\\s+\\S+)(?:\\s+(?\\d+))(?:\\s+(?\\d+))"); private static final Logger logger = LogsCenter.getLogger(AddressBookParser.class); /** @@ -42,69 +40,53 @@ public class AddressBookParser { * @throws ParseException if the user input does not conform the expected format */ public Command parseCommand(String userInput) throws ParseException { - final Matcher basicCommandMatcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); - final Matcher meetingCommandTwoArgsMatcher = MEETING_COMMAND_TWO_ARGS_FORMAT.matcher(userInput.trim()); - - if (basicCommandMatcher.matches()) { - final String commandWord = basicCommandMatcher.group("commandWord"); - final String arguments = basicCommandMatcher.group("arguments"); - - // Note to developers: Change the log level in config.json to enable lower level - // (i.e., FINE, FINER and lower) - // log messages such as the one below. - // Lower level log messages are used sparingly to minimize noise in the code. - logger.fine("Command word: " + commandWord + "; Arguments: " + arguments); - - switch (commandWord) { + final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); + if (!matcher.matches()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + } - case AddCommand.COMMAND_WORD: - return new AddCommandParser().parse(arguments); + final String commandWord = matcher.group("commandWord"); + final String arguments = matcher.group("arguments"); - case EditCommand.COMMAND_WORD: - return new EditCommandParser().parse(arguments); + // Note to developers: Change the log level in config.json to enable lower level (i.e., FINE, FINER and lower) + // log messages such as the one below. + // Lower level log messages are used sparingly to minimize noise in the code. + logger.fine("Command word: " + commandWord + "; Arguments: " + arguments); - case DeleteCommand.COMMAND_WORD: - return new DeleteCommandParser().parse(arguments); + switch (commandWord) { - case ClearCommand.COMMAND_WORD: - return new ClearCommand(); + case AddCommand.COMMAND_WORD: + return new AddCommandParser().parse(arguments); - case FindCommand.COMMAND_WORD: - return new FindCommandParser().parse(arguments); + case EditCommand.COMMAND_WORD: + return new EditCommandParser().parse(arguments); - case ListCommand.COMMAND_WORD: - return new ListCommand(); + case DeleteCommand.COMMAND_WORD: + return new DeleteCommandParser().parse(arguments); - case ExitCommand.COMMAND_WORD: - return new ExitCommand(); + case ClearCommand.COMMAND_WORD: + return new ClearCommand(); - case HelpCommand.COMMAND_WORD: - return new HelpCommand(); + case FindCommand.COMMAND_WORD: + return new FindCommandParser().parse(arguments); - case ViewCommand.COMMAND_WORD: - return new ViewCommandParser().parse(arguments); + case ListCommand.COMMAND_WORD: + return new ListCommand(); - default: - logger.finer("This user input caused a ParseException: " + userInput); - throw new ParseException(MESSAGE_UNKNOWN_COMMAND); - } - } else if (meetingCommandTwoArgsMatcher.matches()) { - final String commandWord = basicCommandMatcher.group("commandWord"); - final String firstArgument = basicCommandMatcher.group("firstArgument"); - final String secondArgument = basicCommandMatcher.group("secondArgument"); + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); - switch (commandWord) { - case DeleteMeetingCommand.COMMAND_WORD: - return new DeleteMeetingCommandParser().parse(firstArgument, secondArgument); + case HelpCommand.COMMAND_WORD: + return new HelpCommand(); - default: - logger.finer("This user input caused a ParseException: " + userInput); - throw new ParseException(MESSAGE_UNKNOWN_COMMAND); - } - } else { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + case AddMeetingCommand.COMMAND_WORD: + return new AddMeetingCommandParser().parse(arguments); + case DeleteMeetingCommand.COMMAND_WORD: + return new DeleteMeetingCommandParser().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..0f962a52fd6 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -12,4 +12,10 @@ public class CliSyntax { public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); public static final Prefix PREFIX_TAG = new Prefix("t/"); + public static final Prefix PREFIX_DATETIME = new Prefix("dt/"); + public static final Prefix PREFIX_DESCRIPTION = new Prefix("d/"); + + public static final Prefix PREFIX_CLIENT_INDEX = new Prefix("client/"); + public static final Prefix PREFIX_MEETING_INDEX = new Prefix("meeting/"); + } diff --git a/src/main/java/seedu/address/logic/parser/DeleteMeetingCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteMeetingCommandParser.java index a6eadf1f2cb..8a8b8bacd28 100644 --- a/src/main/java/seedu/address/logic/parser/DeleteMeetingCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/DeleteMeetingCommandParser.java @@ -1,31 +1,46 @@ package seedu.address.logic.parser; import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CLIENT_INDEX; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEETING_INDEX; + +import java.util.stream.Stream; import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.DeleteCommand; import seedu.address.logic.commands.DeleteMeetingCommand; import seedu.address.logic.parser.exceptions.ParseException; /** * Parses input arguments and creates a new DeleteCommand object */ -public class DeleteMeetingCommandParser implements MeetingCommandParser { - +public class DeleteMeetingCommandParser implements Parser { /** * Parses the given {@code String} of arguments in the context of the DeleteCommand * and returns a DeleteCommand object for execution. * @throws ParseException if the user input does not conform the expected format */ - public DeleteMeetingCommand parse(String firstArgs, String secondArgs) throws ParseException { - try { - Index clientIndex = ParserUtil.parseIndex(firstArgs); - Index meetingIndex = ParserUtil.parseIndex(secondArgs); - return new DeleteMeetingCommand(clientIndex, meetingIndex); - } catch (ParseException pe) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe); + public DeleteMeetingCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_CLIENT_INDEX, PREFIX_MEETING_INDEX); + System.out.println(!arePrefixesPresent(argMultimap, PREFIX_CLIENT_INDEX, PREFIX_MEETING_INDEX)); + System.out.println(!argMultimap.getPreamble().isEmpty()); + if (!arePrefixesPresent(argMultimap, PREFIX_CLIENT_INDEX, PREFIX_MEETING_INDEX) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteMeetingCommand.MESSAGE_USAGE)); } + + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_CLIENT_INDEX, PREFIX_MEETING_INDEX); + Index clientIndex = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_CLIENT_INDEX).get()); + Index meetingIndex = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_MEETING_INDEX).get()); + return new DeleteMeetingCommand(clientIndex, meetingIndex); + } + + /** + * 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/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java index b117acb9c55..3615d617b3c 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java @@ -2,6 +2,8 @@ import static java.util.Objects.requireNonNull; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -9,6 +11,7 @@ import seedu.address.commons.core.index.Index; import seedu.address.commons.util.StringUtil; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.meeting.Meeting; import seedu.address.model.person.Address; import seedu.address.model.person.Email; import seedu.address.model.person.Name; @@ -121,4 +124,32 @@ public static Set parseTags(Collection tags) throws ParseException } return tagSet; } + /** + * Parses a {@code String dateTime} into a {@code LocalDateTime}. + */ + public static LocalDateTime parseDateTime(String dateTime) throws ParseException { + requireNonNull(dateTime); + String trimmedDateTime = dateTime.trim(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm"); + if (!Meeting.isValidDateTime(trimmedDateTime)) { + throw new ParseException(Meeting.MESSAGE_INVALID_DATE_TIME); + } + LocalDateTime parsedDateTime = LocalDateTime.parse(trimmedDateTime, formatter); + return parsedDateTime; + } + /** + * Parses a {@code String description} into a {@code String}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code description} is invalid. + */ + public static String parseDescription(String description) throws ParseException { + requireNonNull(description); + String trimmedDescription = description.trim(); + if (!Meeting.isValidDescription(trimmedDescription)) { + throw new ParseException(Meeting.MESSAGE_CONSTRAINTS); + } + return trimmedDescription; + } + } diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index 7c0de053985..2dfd1cdc8fb 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -144,6 +144,7 @@ public void setMeetings(List meetings) { public void deleteMeeting(Meeting meeting) { meetings.delete(meeting); } + @Override public boolean equals(Object other) { if (other == this) { diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index 29e6b1a8adc..3397602ba90 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -97,6 +97,17 @@ public interface Model { */ void updateFilteredPersonList(Predicate predicate); + /** + * Adds the given meeting. + * {@code meeting} must not already exist in the address book under the client. + */ + void addMeeting(Meeting meeting); + + /** + * Returns true if a meeting with the same description and dateTime {@code meeting} exists in the address book. + */ + boolean hasMeeting(Meeting meeting); + void updateFilteredMeetingList(Predicate predicate); /** diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 095fa317756..2c60d9a3092 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -19,6 +19,7 @@ /** * Represents the in-memory model of the address book data. */ +@SuppressWarnings("checkstyle:Regexp") public class ModelManager implements Model { private static final Logger logger = LogsCenter.getLogger(ModelManager.class); @@ -116,7 +117,20 @@ public void setPerson(Person target, Person editedPerson) { addressBook.setPerson(target, editedPerson); } - // =========== Filtered Person List Accessors ============================================================= + //=========== Meeting Related ============================================================================= + @Override + public void addMeeting(Meeting meeting) { + addressBook.addMeeting(meeting); + updateFilteredMeetingList(PREDICATE_SHOW_ALL_MEETINGS); + } + + @Override + public boolean hasMeeting(Meeting meeting) { + requireNonNull(meeting); + return addressBook.hasMeeting(meeting); + } + + //=========== Filtered Person List Accessors ============================================================= /** * Returns an unmodifiable view of the list of {@code Person} backed by the @@ -171,8 +185,10 @@ public void deleteAllMeetingsForClient(Index clientIndex) { @Override public void deleteSpecificMeetingForClient(Index clientIndex, Index meetingIndex) { - Person targetClient = filteredPersons.get(clientIndex.getZeroBased()); + Person targetClient = addressBook.getPersonList().get(clientIndex.getZeroBased()); ArrayList targetClientMeetings = targetClient.getMeetings(); + Meeting targetMeeting = targetClientMeetings.get(meetingIndex.getZeroBased()); + addressBook.deleteMeeting(targetMeeting); targetClientMeetings.remove(meetingIndex.getZeroBased()); } diff --git a/src/main/java/seedu/address/model/meeting/Meeting.java b/src/main/java/seedu/address/model/meeting/Meeting.java index b8dd601f48f..92bdf36afc3 100644 --- a/src/main/java/seedu/address/model/meeting/Meeting.java +++ b/src/main/java/seedu/address/model/meeting/Meeting.java @@ -4,7 +4,6 @@ import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; import java.time.LocalDateTime; -import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.Objects; @@ -28,7 +27,7 @@ public class Meeting { /* * description must be alphanumeric */ - public static final String VALIDATION_REGEX = "[^\\s].*"; + public static final String VALIDATION_REGEX = "^[\\p{Alnum}][\\p{Alnum} ]*$"; public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("dd MMM yyyy hh.mma"); private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm"); @@ -42,13 +41,28 @@ public class Meeting { * Constructor for Meeting instance * @param description Description of the meeting * @param dateTime Time and Date of the meeting + * @param client Client of the meeting */ public Meeting(String description, LocalDateTime dateTime, Person client) { requireAllNonNull(description, dateTime); checkArgument(isValidDescription(description), MESSAGE_CONSTRAINTS); + checkArgument(isValidDateTime(dateTime.format(formatter)), MESSAGE_INVALID_DATE_TIME); this.description = description; this.dateTime = dateTime; - this.client = client; + this.client = client.addMeeting(this); + } + /** + * Constructor for Meeting instance + * @param description Description of the meeting + * @param dateTime Time and Date of the meeting + */ + public Meeting(String description, LocalDateTime dateTime) { + requireAllNonNull(description, dateTime); + checkArgument(isValidDescription(description), MESSAGE_CONSTRAINTS); + checkArgument(isValidDateTime(dateTime.format(formatter)), MESSAGE_INVALID_DATE_TIME); + this.description = description; + this.dateTime = dateTime; + this.client = null; } /** @@ -66,18 +80,15 @@ public static boolean isValidDescription(String test) { public static boolean isValidDateTime(String test) { try { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm"); + // Attempt to parse the date-time to check format. The result is not used if parsing is successful. LocalDateTime parsedDateTime = LocalDateTime.parse(test, formatter); - LocalDateTime currentDateTime = LocalDateTime.now(); - - LocalTime startTime = LocalTime.of(8, 0); - LocalTime endTime = LocalTime.of(18, 0); - - return parsedDateTime.isAfter(currentDateTime) - && parsedDateTime.toLocalTime().isAfter(startTime) - && parsedDateTime.toLocalTime().isBefore(endTime) - && parsedDateTime.getMinute() == 0; + // Check if the parsed date-time is before the current system date-time. + if (parsedDateTime.isBefore(LocalDateTime.now())) { + return false; // The date-time is in the past. + } + return true; // The string is in the correct format. } catch (DateTimeParseException e) { - return false; + return false; // The string is not in the correct format. } } diff --git a/src/main/java/seedu/address/model/meeting/UniqueMeetingList.java b/src/main/java/seedu/address/model/meeting/UniqueMeetingList.java index a5af81d9304..f9d1afeb10a 100644 --- a/src/main/java/seedu/address/model/meeting/UniqueMeetingList.java +++ b/src/main/java/seedu/address/model/meeting/UniqueMeetingList.java @@ -16,12 +16,17 @@ import seedu.address.model.person.Person; /** - * A list of Meetings that enforces uniqueness between its elements and does not allow nulls. - * A meeting is considered unique by comparing using {@code Meeting#isSameMeeting(Meeting)}. - * As such, adding and updating of meetings uses Meeting#isSameMeeting(Meeting) for equality - * so as to ensure that the meeting being added or updated is unique in the UniqueMeetingList. - * However, the removal of a meeting uses Meeting#equals(Object) so as to ensure that the - * meeting with exactly the same fields will be removed. + * Manages a list of meetings, ensuring each meeting is unique and nulls are not permitted. + * Uniqueness of a meeting is determined by the {@code Meeting#isSameMeeting(Meeting)} method. This ensures + * that when adding or updating meetings, each meeting is distinct based on its fields within the UniqueMeetingList. + * In contrast, the removal of meetings relies on the {@code Meeting#equals(Object)} method to ensure + * that a meeting is removed only if it matches exactly as a meeting object. + * + * This approach allows for nuanced control over meeting management: + * - When adding or updating, meetings are considered the same based on specific attributes, + * allowing for flexible uniqueness criteria. + * - When removing, the stricter {@code equals} method ensures that only an exact match is removed, + * preventing unintended deletions. * * Supports a minimal set of list operations. * diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java index 1c502600d59..c5629a4ed93 100644 --- a/src/main/java/seedu/address/model/person/Person.java +++ b/src/main/java/seedu/address/model/person/Person.java @@ -87,6 +87,27 @@ public void setMeetings(ArrayList meetings) { this.meetings = meetings; } + /** + * Adds a meeting to the person's list of meetings. + * @param meeting + * @return + */ + public Person addMeeting(Meeting meeting) { + this.meetings.add(meeting); + return this; + } + /** + * Returns true if the person has existing meeting. + */ + public boolean hasExistingMeeting(Meeting meeting) { + for (Meeting m : this.meetings) { + if (m.equals(meeting)) { + return true; + } + } + return false; + } + /** * Returns true if both persons have the same name. * This defines a weaker notion of equality between two persons. diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java index ac882fc50a4..85bd24ece2d 100644 --- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/AddCommandTest.java @@ -170,6 +170,16 @@ public void updateFilteredMeetingList(Predicate predicate) { throw new AssertionError("This method should not be called."); } + @Override + public void addMeeting(Meeting meeting) { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasMeeting(Meeting meeting) { + throw new AssertionError("This method should not be called."); + } + @Override public void deleteAllMeetingsForClient(Index clientIndex) { throw new AssertionError("This method should not be called."); diff --git a/src/test/java/seedu/address/logic/commands/AddMeetingCommandTest.java b/src/test/java/seedu/address/logic/commands/AddMeetingCommandTest.java new file mode 100644 index 00000000000..237beb0926d --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/AddMeetingCommandTest.java @@ -0,0 +1,260 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; + +import java.nio.file.Path; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.function.Predicate; + +import org.junit.jupiter.api.Test; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.address.commons.core.GuiSettings; +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.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyUserPrefs; +import seedu.address.model.meeting.Meeting; +import seedu.address.model.person.Person; +import seedu.address.testutil.MeetingBuilder; + +public class AddMeetingCommandTest { + + @Test + public void constructor_validMeeting_success() { + AddMeetingCommand meetingCommand = new AddMeetingCommand( + LocalDateTime.of(2024, 1, 1, 12, 0), + "Sell Insurance", INDEX_FIRST_PERSON); + assert(meetingCommand != null); + } + @Test + public void constructor_nullMeeting_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new AddMeetingCommand(LocalDateTime.of(2024, 1, 1, 12, 0), + null, null)); + assertThrows(NullPointerException.class, () -> new AddMeetingCommand(null, "Sell Insurance", null)); + assertThrows(NullPointerException.class, () -> new AddMeetingCommand(null, null, INDEX_FIRST_PERSON)); + assertThrows(NullPointerException.class, () -> new AddMeetingCommand(null, null, null)); + } + @Test + public void execute_duplicateMeeting_throwsCommandException() { + Person client = new MeetingBuilder().build(); + Meeting validMeeting = new MeetingBuilder().build().getMeetings().get(0); + ModelStubWithMeeting modelStub = new ModelStubWithMeeting(validMeeting, client); + + AddMeetingCommand addMeetingCommand = new AddMeetingCommand(validMeeting.getDateTime(), + validMeeting.getDescription(), INDEX_FIRST_PERSON); + + assertThrows(CommandException.class, + AddMeetingCommand.MESSAGE_DUPLICATE_MEETING, () -> addMeetingCommand.execute(modelStub)); + } + + @Test + public void equals() { + AddMeetingCommand addMeetingCommand = new AddMeetingCommand( + LocalDateTime.of(2024, 1, 1, 12, 0), "Sell Insurance", INDEX_FIRST_PERSON); + AddMeetingCommand addMeetingCommandCopy = new AddMeetingCommand( + LocalDateTime.of(2024, 1, 1, 12, 0), "Sell Insurance", INDEX_FIRST_PERSON); + AddMeetingCommand addMeetingCommandDifferent = new AddMeetingCommand( + LocalDateTime.of(2024, 1, 1, 12, 0), "Sell Insurance", Index.fromZeroBased(1)); + + // same object -> returns true + assertEquals(addMeetingCommand, addMeetingCommand); + + // same values -> returns true + assertEquals(addMeetingCommand, addMeetingCommandCopy); + + // different types -> returns false + assert(!addMeetingCommand.equals(1)); + + // null -> returns false + assert(!addMeetingCommand.equals(null)); + + // different meeting -> returns false + assert(!addMeetingCommand.equals(addMeetingCommandDifferent)); + } + + @Test + public void execute_invalidClientIndex_throwsCommandException() { + ModelStubWithIndexedPerson modelStub = new ModelStubWithIndexedPerson(); + Index outOfBoundIndex = Index.fromOneBased(modelStub.getFilteredPersonList().size() + 1); + AddMeetingCommand addMeetingCommand = new AddMeetingCommand( + LocalDateTime.of(2024, 1, 1, 12, 0), "Sell Insurance", outOfBoundIndex); + + assertThrows(CommandException.class, + Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX, () -> addMeetingCommand.execute(modelStub)); + } + + private class ModelStub implements Model { + @Override + public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyUserPrefs getUserPrefs() { + throw new AssertionError("This method should not be called."); + } + + @Override + public GuiSettings getGuiSettings() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + throw new AssertionError("This method should not be called."); + } + + @Override + public Path getAddressBookFilePath() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setAddressBookFilePath(Path addressBookFilePath) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addPerson(Person person) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setAddressBook(ReadOnlyAddressBook newData) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyAddressBook getAddressBook() { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasPerson(Person person) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void deletePerson(Person target) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setPerson(Person target, Person editedPerson) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ObservableList getFilteredPersonList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public ObservableList getFilteredMeetingList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateFilteredPersonList(Predicate predicate) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateFilteredMeetingList(Predicate predicate) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addMeeting(Meeting meeting) { + throw new AssertionError("This Meeting method should not be called."); + } + + @Override + public boolean hasMeeting(Meeting meeting) { + throw new AssertionError("This Meeting method should not be called."); + } + @Override + public void deleteAllMeetingsForClient(Index clientIndex) { + throw new AssertionError("This Meeting method should not be called."); + } + @Override + public void deleteSpecificMeetingForClient(Index clientIndex, Index meetingIndex) { + throw new AssertionError("This method should not be called."); + } + } + + + /** + * A Model stub that to micmic the person list and meeting list. + */ + private class ModelStubWithIndexedPerson extends ModelStub { + private ObservableList persons = FXCollections.observableArrayList(); + private ObservableList meetings = FXCollections.observableArrayList(); + + ModelStubWithIndexedPerson(Person... persons) { + this.persons.addAll(Arrays.asList(persons)); // Correctly add the persons to the observable list + } + + @Override + public ObservableList getFilteredPersonList() { + return persons; + } + + @Override + public boolean hasMeeting(Meeting meeting) { + return meetings.stream().anyMatch(meeting::isSameMeeting); + } + + @Override + public void addMeeting(Meeting meeting) { + meetings.add(meeting); + } + + } + + + /** + * A Model stub that contains a single predefined meeting. + * This stub is designed for testing scenarios where the model is expected to already contain a specific meeting. + * It overrides the {@code hasMeeting} method to return true when checking for the predefined meeting, + * simulating the scenario where a meeting already exists in the model. + * Use this stub in tests that require the model to behave as if it already contains a certain meeting, + * such as when testing for duplicate meeting prevention. + */ + + private class ModelStubWithMeeting extends ModelStub { + private ObservableList meetings = FXCollections.observableArrayList(); + private ObservableList persons = FXCollections.observableArrayList(); + + ModelStubWithMeeting(Meeting meeting, Person... persons) { + requireNonNull(meeting); + this.meetings.add(meeting); + this.persons.addAll(Arrays.asList(persons)); + } + + @Override + public boolean hasMeeting(Meeting meeting) { + return meetings.stream().anyMatch(meeting::isSameMeeting); + } + @Override + public void addMeeting(Meeting meeting) { + meetings.add(meeting); + } + @Override + public ObservableList getFilteredPersonList() { + return persons; + } + + + } + + +} diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java index 643a1d08069..c068d4619b8 100644 --- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java +++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java @@ -3,7 +3,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CLIENT_INDEX; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATETIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION; import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEETING_INDEX; 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; @@ -28,15 +32,29 @@ public class CommandTestUtil { public static final String VALID_NAME_AMY = "Amy Bee"; public static final String VALID_NAME_BOB = "Bob Choo"; + public static final String VALID_NAME_CHAD = "Chad Doo"; + public static final String VALID_NAME_JAMAL = "Jamal Eee"; public static final String VALID_PHONE_AMY = "11111111"; public static final String VALID_PHONE_BOB = "22222222"; + public static final String VALID_PHONE_CHAD = "33333333"; + public static final String VALID_PHONE_JAMAL = "44444444"; public static final String VALID_EMAIL_AMY = "amy@example.com"; public static final String VALID_EMAIL_BOB = "bob@example.com"; + public static final String VALID_EMAIL_CHAD = "chad@example.com"; + public static final String VALID_EMAIL_JAMAL = "jamal@example.com"; public static final String VALID_ADDRESS_AMY = "Block 312, Amy Street 1"; public static final String VALID_ADDRESS_BOB = "Block 123, Bobby Street 3"; + public static final String VALID_ADDRESS_CHAD = "Block 231, Chad Street 2"; + public static final String VALID_ADDRESS_JAMAL = "Block 123, Jamal Street 3"; public static final String VALID_TAG_HUSBAND = "husband"; public static final String VALID_TAG_FRIEND = "friend"; + public static final String VALID_DATETIME = "01-01-2030 17:00"; + public static final String VALID_DESCRIPTION = "Project discussion"; + + public static final String VALID_CLIENT_INDEX = "1"; + public static final String VALID_MEETING_INDEX = "1"; + public static final String NAME_DESC_AMY = " " + PREFIX_NAME + VALID_NAME_AMY; public static final String NAME_DESC_BOB = " " + PREFIX_NAME + VALID_NAME_BOB; public static final String PHONE_DESC_AMY = " " + PREFIX_PHONE + VALID_PHONE_AMY; @@ -48,7 +66,16 @@ public class CommandTestUtil { 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 CLIENT_INDEX = " " + PREFIX_CLIENT_INDEX + VALID_CLIENT_INDEX; + public static final String MEETING_INDEX = " " + PREFIX_MEETING_INDEX + VALID_MEETING_INDEX; + public static final String DATETIME = " " + PREFIX_DATETIME + VALID_DATETIME; + public static final String DESCRIPTION = " " + PREFIX_DESCRIPTION + VALID_DESCRIPTION; + public static final String INVALID_NAME_DESC = " " + PREFIX_NAME + "James&"; // '&' not allowed in names + public static final String INVALID_DATETIME_DESC = " " + PREFIX_DATETIME + + "not-a-datetime"; // 'string' not allowed in datetime + public static final String INVALID_DESCRIPTION_DESC = " " + + PREFIX_DESCRIPTION; // empty string not allowed for description 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 diff --git a/src/test/java/seedu/address/logic/commands/DeleteMeetingCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteMeetingCommandTest.java index 7fde40880b2..a5f24cb0565 100644 --- a/src/test/java/seedu/address/logic/commands/DeleteMeetingCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/DeleteMeetingCommandTest.java @@ -9,11 +9,9 @@ import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_MEETING; import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalPersons.JAMAL; +import static seedu.address.testutil.TypicalMeetings.JAMAL_WITH_MEETING; import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; -import java.time.LocalDateTime; - import org.junit.jupiter.api.Test; import seedu.address.commons.core.index.Index; @@ -29,75 +27,81 @@ */ public class DeleteMeetingCommandTest { - private static final Meeting testMeeting = new Meeting("test meeting", LocalDateTime.now(), JAMAL); + static { + System.out.println(JAMAL_WITH_MEETING.getMeetings().size()); + } + private static final Meeting testMeeting = JAMAL_WITH_MEETING.getMeetings().get(0); private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); @Test public void execute_validClientIndexValidMeetingIndex() { - JAMAL.getMeetings().add(testMeeting); - model.addPerson(JAMAL); - int clientIndex = model.getAddressBook().getPersonList().indexOf(JAMAL); + System.out.println(JAMAL_WITH_MEETING.getMeetings()); + JAMAL_WITH_MEETING.getMeetings().add(testMeeting); + System.out.println(JAMAL_WITH_MEETING.getMeetings().size()); + model.addPerson(JAMAL_WITH_MEETING); + int clientIndex = model.getAddressBook().getPersonList().indexOf(JAMAL_WITH_MEETING); Index testClientIndex = Index.fromOneBased(Index.fromZeroBased(clientIndex).getOneBased()); - Index testMeetingIndex = Index.fromOneBased(Index.fromZeroBased(JAMAL.getMeetings().size() - 1).getOneBased()); + Index testMeetingIndex = Index.fromOneBased(Index.fromZeroBased( + JAMAL_WITH_MEETING.getMeetings().size() - 1).getOneBased()); DeleteMeetingCommand deleteMeetingCommand = new DeleteMeetingCommand(testClientIndex, testMeetingIndex); - String expectedMessage = "Meeting 1 deleted successfully "; + String expectedMessage = "Meeting 2 deleted successfully "; ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - assertCommandSuccess(deleteMeetingCommand, model, expectedMessage, expectedModel); } @Test public void execute_invalidClientIndexValidMeetingIndex_throwsCommandException() { - JAMAL.getMeetings().add(testMeeting); - model.addPerson(JAMAL); + JAMAL_WITH_MEETING.getMeetings().add(testMeeting); + model.addPerson(JAMAL_WITH_MEETING); int invalidClientIndex = model.getAddressBook().getPersonList().size(); Index testClientIndex = Index.fromOneBased(Index.fromZeroBased(invalidClientIndex).getOneBased()); - Index testMeetingIndex = Index.fromOneBased(Index.fromZeroBased(JAMAL.getMeetings().size() - 1).getOneBased()); + Index testMeetingIndex = Index.fromOneBased(Index.fromZeroBased( + JAMAL_WITH_MEETING.getMeetings().size() - 1).getOneBased()); DeleteMeetingCommand deleteMeetingCommand = new DeleteMeetingCommand(testClientIndex, testMeetingIndex); String expectedMessage = Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; assertCommandFailure(deleteMeetingCommand, model, expectedMessage); - model.deletePerson(JAMAL); + model.deletePerson(JAMAL_WITH_MEETING); } @Test public void execute_validClientIndexInvalidMeetingIndex_throwsCommandException() { - model.addPerson(JAMAL); - // Do not add any meeting to JAMAL to ensure the meeting index is invalid - int clientIndex = model.getAddressBook().getPersonList().indexOf(JAMAL); - + model.addPerson(JAMAL_WITH_MEETING); + // Do not add any meeting to JAMAL_WITH_MEETING to ensure the meeting index is invalid + int clientIndex = model.getAddressBook().getPersonList().indexOf(JAMAL_WITH_MEETING); Index testClientIndex = Index.fromOneBased(Index.fromZeroBased(clientIndex).getOneBased()); - Index testMeetingIndex = Index.fromOneBased(Index.fromZeroBased(JAMAL.getMeetings().size()).getOneBased()); - + Index testMeetingIndex = Index.fromOneBased(Index.fromZeroBased( + JAMAL_WITH_MEETING.getMeetings().size()).getOneBased()); DeleteMeetingCommand deleteMeetingCommand = new DeleteMeetingCommand(testClientIndex, testMeetingIndex); - - String expectedMessage = "Error: Meeting 2 not found"; + System.out.println(JAMAL_WITH_MEETING.getMeetings()); + String expectedMessage = "Error: Meeting 3 not found"; assertCommandFailure(deleteMeetingCommand, model, expectedMessage); - model.deletePerson(JAMAL); + model.deletePerson(JAMAL_WITH_MEETING); } @Test public void execute_invalidClientIndexInvalidMeetingIndex_throwsCommandException() { - model.addPerson(JAMAL); + model.addPerson(JAMAL_WITH_MEETING); // No meeting added to ensure meeting index is also invalid int invalidClientIndex = model.getAddressBook().getPersonList().size(); Index testClientIndex = Index.fromOneBased(Index.fromZeroBased(invalidClientIndex).getOneBased()); - Index testMeetingIndex = Index.fromOneBased(Index.fromZeroBased(JAMAL.getMeetings().size()).getOneBased()); + Index testMeetingIndex = Index.fromOneBased(Index.fromZeroBased( + JAMAL_WITH_MEETING.getMeetings().size()).getOneBased()); DeleteMeetingCommand deleteMeetingCommand = new DeleteMeetingCommand(testClientIndex, testMeetingIndex); // invalid client index should be caught before invalid meeting index String expectedMessage = Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; assertCommandFailure(deleteMeetingCommand, model, expectedMessage); - model.deletePerson(JAMAL); + model.deletePerson(JAMAL_WITH_MEETING); } diff --git a/src/test/java/seedu/address/logic/parser/AddMeetingCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddMeetingCommandParserTest.java new file mode 100644 index 00000000000..d1ee1b3265a --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/AddMeetingCommandParserTest.java @@ -0,0 +1,70 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.commands.CommandTestUtil.CLIENT_INDEX; +import static seedu.address.logic.commands.CommandTestUtil.DATETIME; +import static seedu.address.logic.commands.CommandTestUtil.DESCRIPTION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CLIENT_INDEX; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATETIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.address.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.AddMeetingCommand; +import seedu.address.model.meeting.Meeting; + +public class AddMeetingCommandParserTest { + + private AddMeetingCommandParser parser = new AddMeetingCommandParser(); + private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm"); + @Test + public void parse_allFieldsPresent_success() { + Index targetIndex = Index.fromOneBased(1); + String description = "Project discussion"; + String dateTimeStr = "01-01-2030 17:00"; + LocalDateTime dateTime = LocalDateTime.parse(dateTimeStr, formatter); + String userInput = " " + CLIENT_INDEX + DATETIME + DESCRIPTION; + + assertParseSuccess(parser, userInput, new AddMeetingCommand(dateTime, description, targetIndex)); + } + + + @Test + public void parse_compulsoryFieldMissing_failure() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddMeetingCommand.MESSAGE_USAGE); + + // Missing client index + assertParseFailure(parser, DATETIME + DESCRIPTION, expectedMessage); + + // Missing date time + assertParseFailure(parser, DESCRIPTION + CLIENT_INDEX, expectedMessage); + + // Missing description + assertParseFailure(parser, DATETIME + CLIENT_INDEX, expectedMessage); + } + + @Test + public void parse_invalidValue_failure() { + String userInput = " " + PREFIX_CLIENT_INDEX + "0 " + DATETIME + DESCRIPTION; + // Invalid client index + assertParseFailure(parser, userInput, MESSAGE_INVALID_INDEX); + + String userInputDateTime = " " + CLIENT_INDEX + " " + PREFIX_DATETIME + "01-01-2024 17:00 " + DESCRIPTION; + // Invalid date time + assertParseFailure(parser, userInputDateTime, Meeting.MESSAGE_INVALID_DATE_TIME); + + String userInputDescription = " " + CLIENT_INDEX + DATETIME + " " + PREFIX_DESCRIPTION + " "; + // Invalid description + assertParseFailure(parser, userInputDescription, Meeting.MESSAGE_CONSTRAINTS); + + } + + // Additional tests for other invalid scenarios, like invalid description, etc. +} diff --git a/src/test/java/seedu/address/logic/parser/DeleteMeetingCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteMeetingCommandParserTest.java index a33ea0d7a1f..b4fb9c61308 100644 --- a/src/test/java/seedu/address/logic/parser/DeleteMeetingCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/DeleteMeetingCommandParserTest.java @@ -1,6 +1,8 @@ package seedu.address.logic.parser; -import static seedu.address.logic.parser.CommandParserTestUtil.assertMeetingParseSuccess; +import static seedu.address.logic.commands.CommandTestUtil.CLIENT_INDEX; +import static seedu.address.logic.commands.CommandTestUtil.MEETING_INDEX; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_MEETING; import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; @@ -21,6 +23,7 @@ public class DeleteMeetingCommandParserTest { @Test public void parse_validArgs_returnsDeleteCommand() { - assertMeetingParseSuccess(parser, "1", "1", new DeleteMeetingCommand(INDEX_FIRST_PERSON, INDEX_FIRST_MEETING)); + String userInput = " " + CLIENT_INDEX + MEETING_INDEX; + assertParseSuccess(parser, userInput, new DeleteMeetingCommand(INDEX_FIRST_PERSON, INDEX_FIRST_MEETING)); } } diff --git a/src/test/java/seedu/address/model/meeting/MeetingBelongingToClientPredicateTest.java b/src/test/java/seedu/address/model/meeting/MeetingBelongingToClientPredicateTest.java index 0d709788268..8d24754902d 100644 --- a/src/test/java/seedu/address/model/meeting/MeetingBelongingToClientPredicateTest.java +++ b/src/test/java/seedu/address/model/meeting/MeetingBelongingToClientPredicateTest.java @@ -14,6 +14,7 @@ import seedu.address.testutil.PersonBuilder; public class MeetingBelongingToClientPredicateTest { + @Test public void equals() { MeetingBelongingToClientPredicate firstPredicate = new MeetingBelongingToClientPredicate(ALICE); @@ -39,14 +40,13 @@ public void equals() { // different person -> returns false assertFalse(firstPredicate.equals(secondPredicate)); } - @Test public void test_clientWithMeeting_returnsTrue() { MeetingBelongingToClientPredicate predicate = new MeetingBelongingToClientPredicate(JAMAL); assertTrue(predicate.test(new MeetingBuilder() .withClient(JAMAL) .withDescription("Financial Aid Document Submission") - .withDateTime("05-02-2024 13:00").build())); + .withDateTime("05-02-2029 13:00").build().getMeetings().get(0))); } @Test @@ -55,9 +55,8 @@ public void test_clientWithMeeting_returnsFalse() { assertFalse(predicate.test(new MeetingBuilder() .withClient(JAMAL) .withDescription("Financial Aid Document Submission") - .withDateTime("05-02-2024 13:00").build())); + .withDateTime("05-02-2029 13:00").build().getMeetings().get(0))); } - @Test public void toStringMethod() { MeetingBelongingToClientPredicate predicate = new MeetingBelongingToClientPredicate(ALICE); diff --git a/src/test/java/seedu/address/model/meeting/MeetingTest.java b/src/test/java/seedu/address/model/meeting/MeetingTest.java index 126891b1961..f04b1d35d1a 100644 --- a/src/test/java/seedu/address/model/meeting/MeetingTest.java +++ b/src/test/java/seedu/address/model/meeting/MeetingTest.java @@ -6,7 +6,6 @@ import org.junit.jupiter.api.Test; -import seedu.address.testutil.MeetingBuilder; import seedu.address.testutil.TypicalMeetings; public class MeetingTest { @@ -14,36 +13,41 @@ public class MeetingTest { @Test public void isSameMeeting() { // same object -> returns true - assertTrue(TypicalMeetings.MEETING_WITH_ALICE.isSameMeeting(TypicalMeetings.MEETING_WITH_ALICE)); + assertTrue(TypicalMeetings.ALICE_WITH_MEETING.hasExistingMeeting(TypicalMeetings + .ALICE_WITH_MEETING.getMeetings().get(0))); // different meeting -> returns false - assertFalse(TypicalMeetings.MEETING_WITH_ALICE.isSameMeeting(TypicalMeetings.MEETING_WITH_BENSON)); + assertFalse(TypicalMeetings.ALICE_WITH_MEETING.hasExistingMeeting(TypicalMeetings + .BENSON_WITH_MEETING.getMeetings().get(0))); } @Test public void equals() { // same values -> returns true - Meeting meetingWithAliceCopy = new MeetingBuilder(TypicalMeetings.MEETING_WITH_ALICE).build(); - assertTrue(TypicalMeetings.MEETING_WITH_ALICE.equals(meetingWithAliceCopy)); + Meeting meetingWithAliceCopy = TypicalMeetings.ALICE_WITH_MEETING.getMeetings().get(0); + + assertTrue(TypicalMeetings.ALICE_WITH_MEETING.getMeetings().get(0).equals(meetingWithAliceCopy)); // same object -> returns true - assertTrue(TypicalMeetings.MEETING_WITH_ALICE.equals(TypicalMeetings.MEETING_WITH_ALICE)); + assertTrue(TypicalMeetings.ALICE_WITH_MEETING.getMeetings().get(0) + .equals(TypicalMeetings.ALICE_WITH_MEETING.getMeetings().get(0))); // null -> returns false - assertFalse(TypicalMeetings.MEETING_WITH_ALICE.equals(null)); + assertFalse(TypicalMeetings.ALICE_WITH_MEETING.getMeetings().get(0).equals(null)); // different type -> returns false - assertFalse(TypicalMeetings.MEETING_WITH_ALICE.equals(5)); + assertFalse(TypicalMeetings.ALICE_WITH_MEETING.getMeetings().get(0).equals(5)); // different meeting -> returns false - assertFalse(TypicalMeetings.MEETING_WITH_ALICE.equals(TypicalMeetings.MEETING_WITH_BENSON)); + assertFalse(TypicalMeetings.ALICE_WITH_MEETING.getMeetings().get(0) + .equals(TypicalMeetings.BENSON_WITH_MEETING)); } @Test public void toStringMethod() { String expected = Meeting.class.getCanonicalName() - + "{description=Financial Aid Application Review, dateTime=2024-01-01T09:00, " + + "{description=Financial Aid Application Review, dateTime=2030-01-01T12:00, " + "client=Alice Pauline}"; - assertEquals(expected, TypicalMeetings.MEETING_WITH_ALICE.toString()); + assertEquals(expected, TypicalMeetings.ALICE_WITH_MEETING.getMeetings().get(0).toString()); } } diff --git a/src/test/java/seedu/address/model/meeting/UniqueMeetingListTest.java b/src/test/java/seedu/address/model/meeting/UniqueMeetingListTest.java index ae213e08db1..35231d4739d 100644 --- a/src/test/java/seedu/address/model/meeting/UniqueMeetingListTest.java +++ b/src/test/java/seedu/address/model/meeting/UniqueMeetingListTest.java @@ -1,20 +1,22 @@ +package seedu.address.model.meeting; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.TypicalMeetings.MEETING_WITH_ALICE; -import static seedu.address.testutil.TypicalMeetings.MEETING_WITH_BENSON; -import static seedu.address.testutil.TypicalMeetings.MEETING_WITH_CARL; +//import static seedu.address.testutil.TypicalMeetings.ALICE_WITH_MEETING; +import static seedu.address.testutil.TypicalMeetings.ALICE_WITH_MEETING; +import static seedu.address.testutil.TypicalMeetings.BENSON_WITH_MEETING; +import static seedu.address.testutil.TypicalMeetings.CARL_WITH_MEETING; import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.Test; -import seedu.address.model.meeting.Meeting; -import seedu.address.model.meeting.UniqueMeetingList; import seedu.address.model.meeting.exceptions.DuplicateMeetingException; import seedu.address.model.meeting.exceptions.MeetingNotFoundException; +import seedu.address.model.person.Person; import seedu.address.testutil.MeetingBuilder; public class UniqueMeetingListTest { @@ -28,13 +30,13 @@ public void contains_nullMeeting_throwsNullPointerException() { @Test public void contains_meetingNotInList_returnsFalse() { - assertFalse(uniqueMeetingList.contains(MEETING_WITH_ALICE)); + assertFalse(uniqueMeetingList.contains(ALICE_WITH_MEETING.getMeetings().get(0))); } @Test public void contains_meetingInList_returnsTrue() { - uniqueMeetingList.add(MEETING_WITH_ALICE); - assertTrue(uniqueMeetingList.contains(MEETING_WITH_ALICE)); + uniqueMeetingList.add(ALICE_WITH_MEETING.getMeetings().get(0)); + assertTrue(uniqueMeetingList.contains(ALICE_WITH_MEETING.getMeetings().get(0))); } @Test @@ -44,62 +46,67 @@ public void add_nullMeeting_throwsNullPointerException() { @Test public void add_duplicateMeeting_throwsDuplicateMeetingException() { - uniqueMeetingList.add(MEETING_WITH_ALICE); - assertThrows(DuplicateMeetingException.class, () -> uniqueMeetingList.add(MEETING_WITH_ALICE)); + uniqueMeetingList.add(ALICE_WITH_MEETING.getMeetings().get(0)); + assertThrows(DuplicateMeetingException.class, () -> uniqueMeetingList + .add(ALICE_WITH_MEETING.getMeetings().get(0))); } @Test public void setMeeting_nullTargetMeeting_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniqueMeetingList.setMeeting(null, MEETING_WITH_ALICE)); + assertThrows(NullPointerException.class, () -> uniqueMeetingList.setMeeting(null, + ALICE_WITH_MEETING.getMeetings().get(0))); } @Test public void setMeeting_nullEditedMeeting_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniqueMeetingList.setMeeting(MEETING_WITH_ALICE, null)); + assertThrows(NullPointerException.class, () -> uniqueMeetingList.setMeeting(ALICE_WITH_MEETING + .getMeetings().get(0), null)); } @Test public void setMeeting_targetMeetingNotInList_throwsMeetingNotFoundException() { - assertThrows(MeetingNotFoundException.class, () -> uniqueMeetingList.setMeeting(MEETING_WITH_ALICE, - MEETING_WITH_ALICE)); + assertThrows(MeetingNotFoundException.class, () -> uniqueMeetingList.setMeeting(ALICE_WITH_MEETING + .getMeetings().get(0), + ALICE_WITH_MEETING.getMeetings().get(0))); } @Test public void setMeeting_editedMeetingIsSameMeeting_success() { - uniqueMeetingList.add(MEETING_WITH_ALICE); - uniqueMeetingList.setMeeting(MEETING_WITH_ALICE, MEETING_WITH_ALICE); + uniqueMeetingList.add(ALICE_WITH_MEETING.getMeetings().get(0)); + uniqueMeetingList.setMeeting(ALICE_WITH_MEETING.getMeetings().get(0), ALICE_WITH_MEETING.getMeetings().get(0)); UniqueMeetingList expectedUniqueMeetingList = new UniqueMeetingList(); - expectedUniqueMeetingList.add(MEETING_WITH_ALICE); + expectedUniqueMeetingList.add(ALICE_WITH_MEETING.getMeetings().get(0)); assertEquals(expectedUniqueMeetingList, uniqueMeetingList); } @Test public void setMeeting_editedMeetingHasSameIdentity_success() { - uniqueMeetingList.add(MEETING_WITH_ALICE); - Meeting editedAlice = new MeetingBuilder(MEETING_WITH_ALICE) + uniqueMeetingList.add(ALICE_WITH_MEETING.getMeetings().get(0)); + Person editedAlice = new MeetingBuilder(ALICE_WITH_MEETING.getMeetings().get(0)) .withDescription("Updated Description") .build(); - uniqueMeetingList.setMeeting(MEETING_WITH_ALICE, editedAlice); + uniqueMeetingList.setMeeting(ALICE_WITH_MEETING.getMeetings().get(0), editedAlice.getMeetings().get(0)); UniqueMeetingList expectedUniqueMeetingList = new UniqueMeetingList(); - expectedUniqueMeetingList.add(editedAlice); + expectedUniqueMeetingList.add(editedAlice.getMeetings().get(0)); assertEquals(expectedUniqueMeetingList, uniqueMeetingList); } @Test public void setMeeting_editedMeetingHasDifferentIdentity_success() { - uniqueMeetingList.add(MEETING_WITH_ALICE); - uniqueMeetingList.setMeeting(MEETING_WITH_ALICE, MEETING_WITH_BENSON); + uniqueMeetingList.add(ALICE_WITH_MEETING.getMeetings().get(0)); + uniqueMeetingList.setMeeting(ALICE_WITH_MEETING.getMeetings().get(0), BENSON_WITH_MEETING.getMeetings().get(0)); UniqueMeetingList expectedUniqueMeetingList = new UniqueMeetingList(); - expectedUniqueMeetingList.add(MEETING_WITH_BENSON); + expectedUniqueMeetingList.add(BENSON_WITH_MEETING.getMeetings().get(0)); assertEquals(expectedUniqueMeetingList, uniqueMeetingList); } @Test public void setMeeting_editedMeetingHasNonUniqueIdentity_throwsDuplicateMeetingException() { - uniqueMeetingList.add(MEETING_WITH_ALICE); - uniqueMeetingList.add(MEETING_WITH_BENSON); - assertThrows(DuplicateMeetingException.class, () -> uniqueMeetingList.setMeeting(MEETING_WITH_ALICE, - MEETING_WITH_BENSON)); + uniqueMeetingList.add(ALICE_WITH_MEETING.getMeetings().get(0)); + uniqueMeetingList.add(BENSON_WITH_MEETING.getMeetings().get(0)); + assertThrows(DuplicateMeetingException.class, () -> uniqueMeetingList.setMeeting( + ALICE_WITH_MEETING.getMeetings().get(0), + BENSON_WITH_MEETING.getMeetings().get(0))); } @Test @@ -109,13 +116,14 @@ public void remove_nullMeeting_throwsNullPointerException() { @Test public void remove_meetingDoesNotExist_throwsMeetingNotFoundException() { - assertThrows(MeetingNotFoundException.class, () -> uniqueMeetingList.remove(MEETING_WITH_ALICE)); + assertThrows(MeetingNotFoundException.class, () -> uniqueMeetingList.remove( + ALICE_WITH_MEETING.getMeetings().get(0))); } @Test public void remove_existingMeeting_removesMeeting() { - uniqueMeetingList.add(MEETING_WITH_ALICE); - uniqueMeetingList.remove(MEETING_WITH_ALICE); + uniqueMeetingList.add(ALICE_WITH_MEETING.getMeetings().get(0)); + uniqueMeetingList.remove(ALICE_WITH_MEETING.getMeetings().get(0)); UniqueMeetingList expectedUniqueMeetingList = new UniqueMeetingList(); assertEquals(expectedUniqueMeetingList, uniqueMeetingList); } @@ -127,9 +135,9 @@ public void setMeetings_nullUniqueMeetingList_throwsNullPointerException() { @Test public void setMeetings_uniqueMeetingList_replacesOwnListWithProvidedUniqueMeetingList() { - uniqueMeetingList.add(MEETING_WITH_ALICE); + uniqueMeetingList.add(ALICE_WITH_MEETING.getMeetings().get(0)); UniqueMeetingList expectedUniqueMeetingList = new UniqueMeetingList(); - expectedUniqueMeetingList.add(MEETING_WITH_BENSON); + expectedUniqueMeetingList.add(BENSON_WITH_MEETING.getMeetings().get(0)); uniqueMeetingList.setMeetings(expectedUniqueMeetingList); assertEquals(expectedUniqueMeetingList, uniqueMeetingList); } @@ -141,18 +149,20 @@ public void setMeetings_nullList_throwsNullPointerException() { @Test public void setMeetings_list_replacesOwnListWithProvidedList() { - uniqueMeetingList.add(MEETING_WITH_ALICE); - List meetingList = Arrays.asList(MEETING_WITH_BENSON, MEETING_WITH_CARL); + uniqueMeetingList.add(ALICE_WITH_MEETING.getMeetings().get(0)); + List meetingList = Arrays.asList(BENSON_WITH_MEETING.getMeetings().get(0), + CARL_WITH_MEETING.getMeetings().get(0)); uniqueMeetingList.setMeetings(meetingList); UniqueMeetingList expectedUniqueMeetingList = new UniqueMeetingList(); - expectedUniqueMeetingList.add(MEETING_WITH_BENSON); - expectedUniqueMeetingList.add(MEETING_WITH_CARL); + expectedUniqueMeetingList.add(BENSON_WITH_MEETING.getMeetings().get(0)); + expectedUniqueMeetingList.add(CARL_WITH_MEETING.getMeetings().get(0)); assertEquals(expectedUniqueMeetingList, uniqueMeetingList); } @Test public void setMeetings_listWithDuplicateMeetings_throwsDuplicateMeetingException() { - List listWithDuplicateMeetings = Arrays.asList(MEETING_WITH_ALICE, MEETING_WITH_ALICE); + List listWithDuplicateMeetings = Arrays.asList(ALICE_WITH_MEETING.getMeetings().get(0), + ALICE_WITH_MEETING.getMeetings().get(0)); assertThrows(DuplicateMeetingException.class, () -> uniqueMeetingList.setMeetings(listWithDuplicateMeetings)); } diff --git a/src/test/java/seedu/address/testutil/MeetingBuilder.java b/src/test/java/seedu/address/testutil/MeetingBuilder.java index 958fea144c3..5f9efe30b71 100644 --- a/src/test/java/seedu/address/testutil/MeetingBuilder.java +++ b/src/test/java/seedu/address/testutil/MeetingBuilder.java @@ -12,7 +12,7 @@ public class MeetingBuilder { public static final String DEFAULT_DESCRIPTION = "Description of Meeting"; - public static final String DEFAULT_DATE = "01-01-2024 00:00"; + public static final String DEFAULT_DATE = "01-01-2030 17:00"; public static final Person DEFAULT_CLIENT = new PersonBuilder().build(); private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm"); @@ -28,6 +28,14 @@ public MeetingBuilder() { dateTime = LocalDateTime.parse(DEFAULT_DATE, formatter); client = DEFAULT_CLIENT; } + /** + * Creates a {@code MeetingBuilder} with the default details, but with the specified person. + */ + public MeetingBuilder(Person person) { + description = DEFAULT_DESCRIPTION; + dateTime = LocalDateTime.parse(DEFAULT_DATE, formatter); + client = person; + } /** * Constructs a {@code MeetingBuilder} with the specified meeting to copy. @@ -68,7 +76,8 @@ public MeetingBuilder withClient(Person client) { /** * Builds and returns a {@code Meeting} object with the specified details. */ - public Meeting build() { - return new Meeting(description, dateTime, client); + public Person build() { + Meeting meeting = new Meeting(description, dateTime, client); + return client; } } diff --git a/src/test/java/seedu/address/testutil/PersonBuilder.java b/src/test/java/seedu/address/testutil/PersonBuilder.java index e20160ba41f..0870df36928 100644 --- a/src/test/java/seedu/address/testutil/PersonBuilder.java +++ b/src/test/java/seedu/address/testutil/PersonBuilder.java @@ -1,8 +1,5 @@ package seedu.address.testutil; -import static seedu.address.testutil.TypicalPersons.CHAD; -import static seedu.address.testutil.TypicalPersons.JAMAL; - import java.util.ArrayList; import java.util.HashSet; import java.util.Set; @@ -19,6 +16,7 @@ /** * A utility class to help with building Person objects. */ +@SuppressWarnings("checkstyle:Regexp") public class PersonBuilder { public static final String DEFAULT_NAME = "Amy Bee"; @@ -96,29 +94,16 @@ public PersonBuilder withEmail(String email) { this.email = new Email(email); return this; } - /** - * Sets the {@code Meeting} of the {@code Person} that we are building. + * Adds a meeting to the person that we are building. */ - public PersonBuilder withDefaultMeetingA() { - this.meetings.add(new MeetingBuilder() - .withClient(JAMAL) - .withDescription("Financial Aid Document Submission") - .withDateTime("05-02-2024 13:00").build()); + public PersonBuilder withMeeting(Meeting meeting) { + this.meetings.add(meeting); return this; } - /** - * Sets the {@code Meeting} of the {@code Person} that we are building. + * Builds the person with the meetings. */ - public PersonBuilder withDefaultMeetingB() { - this.meetings.add(new MeetingBuilder() - .withClient(CHAD) - .withDescription("Financial Aid Application Review") - .withDateTime("01-01-2024 09:00").build()); - return this; - } - public Person build() { return new Person(name, phone, email, address, tags); } diff --git a/src/test/java/seedu/address/testutil/TypicalMeetings.java b/src/test/java/seedu/address/testutil/TypicalMeetings.java index b9170a1b466..733a4119b07 100644 --- a/src/test/java/seedu/address/testutil/TypicalMeetings.java +++ b/src/test/java/seedu/address/testutil/TypicalMeetings.java @@ -3,54 +3,67 @@ import static seedu.address.testutil.TypicalPersons.ALICE; import static seedu.address.testutil.TypicalPersons.BENSON; import static seedu.address.testutil.TypicalPersons.CARL; +import static seedu.address.testutil.TypicalPersons.CHAD; import static seedu.address.testutil.TypicalPersons.DANIEL; import static seedu.address.testutil.TypicalPersons.ELLE; import static seedu.address.testutil.TypicalPersons.FIONA; import static seedu.address.testutil.TypicalPersons.GEORGE; +import static seedu.address.testutil.TypicalPersons.JAMAL; import java.util.ArrayList; import java.util.Arrays; -import seedu.address.model.meeting.Meeting; +import seedu.address.model.person.Person; /** * A utility class containing a list of {@code Meeting} objects to be used in tests. */ public class TypicalMeetings { - public static final Meeting MEETING_WITH_ALICE = new MeetingBuilder() + public static final Person ALICE_WITH_MEETING = new MeetingBuilder() .withClient(ALICE) .withDescription("Financial Aid Application Review") - .withDateTime("01-01-2024 09:00").build(); - public static final Meeting MEETING_WITH_BENSON = new MeetingBuilder() + .withDateTime("01-01-2030 12:00").build(); + public static final Person BENSON_WITH_MEETING = new MeetingBuilder() .withClient(BENSON) .withDescription("Financial Aid Eligibility Check") - .withDateTime("01-11-2023 11:00").build(); - public static final Meeting MEETING_WITH_CARL = new MeetingBuilder() + .withDateTime("01-11-2030 11:00").build(); + public static final Person CARL_WITH_MEETING = new MeetingBuilder() .withClient(CARL) .withDescription("Financial Aid Document Submission") - .withDateTime("05-02-2024 13:00").build(); - public static final Meeting MEETING_WITH_DANIEL = new MeetingBuilder() + .withDateTime("05-02-2030 13:00").build(); + public static final Person DANIEL_WITH_MEETING = new MeetingBuilder() .withClient(DANIEL) .withDescription("Financial Aid Interview") - .withDateTime("05-02-2024 15:00").build(); - public static final Meeting MEETING_WITH_ELLE = new MeetingBuilder() + .withDateTime("05-02-2030 15:00").build(); + public static final Person ELLE_WITH_MEETING = new MeetingBuilder() .withClient(ELLE) .withDescription("Financial Aid Consultation") - .withDateTime("05-02-2024 12:00").build(); - public static final Meeting MEETING_WITH_FIONA = new MeetingBuilder() + .withDateTime("05-02-2029 12:00").build(); + public static final Person FIONA_WITH_MEETING = new MeetingBuilder() .withClient(FIONA) .withDescription("Financial Aid Application Review") - .withDateTime("05-02-2024 11:00").build(); - public static final Meeting MEETING_WITH_GEORGE = new MeetingBuilder() + .withDateTime("05-02-2028 11:00").build(); + public static final Person GEORGE_WITH_MEETING = new MeetingBuilder() .withClient(GEORGE) .withDescription("Financial Aid Document Submission") - .withDateTime("05-02-2024 17:00").build(); + .withDateTime("05-02-2029 17:00").build(); + + public static final Person JAMAL_WITH_MEETING = new MeetingBuilder() + .withClient(JAMAL) + .withDescription("Financial Support") + .withDateTime("05-02-2028 11:00").build(); + + public static final Person CHAD_WITH_MEETING = new MeetingBuilder() + .withClient(CHAD) + .withDescription("Financial Aid Application Review") + .withDateTime("05-02-2028 11:00").build(); private TypicalMeetings() {} // prevents instantiation - public static ArrayList getTypicalMeetings() { - return new ArrayList<>(Arrays.asList(MEETING_WITH_ALICE, MEETING_WITH_BENSON, MEETING_WITH_CARL, - MEETING_WITH_DANIEL, MEETING_WITH_ELLE, MEETING_WITH_FIONA, MEETING_WITH_GEORGE)); + public static ArrayList getTypicalMeetings() { + return new ArrayList<>(Arrays.asList(ALICE_WITH_MEETING, BENSON_WITH_MEETING, CARL_WITH_MEETING, + DANIEL_WITH_MEETING, ELLE_WITH_MEETING, FIONA_WITH_MEETING, GEORGE_WITH_MEETING, + JAMAL_WITH_MEETING, CHAD_WITH_MEETING)); } } diff --git a/src/test/java/seedu/address/testutil/TypicalPersons.java b/src/test/java/seedu/address/testutil/TypicalPersons.java index 83082c5537e..eed362bfc2d 100644 --- a/src/test/java/seedu/address/testutil/TypicalPersons.java +++ b/src/test/java/seedu/address/testutil/TypicalPersons.java @@ -2,12 +2,20 @@ import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_CHAD; +import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_JAMAL; import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_CHAD; +import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_JAMAL; import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_CHAD; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_JAMAL; import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY; import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_CHAD; +import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_JAMAL; import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; @@ -56,10 +64,10 @@ public class TypicalPersons { .build(); // To for Clients with meetings - public static final Person JAMAL = new PersonBuilder().withName("Jamal").withPhone("88888888") - .withEmail("jamal@example.com").withAddress("little india").withDefaultMeetingA().build(); - public static final Person CHAD = new PersonBuilder().withName("Chad").withPhone("88888888") - .withEmail("chad@example.com").withAddress("little india").withDefaultMeetingB().build(); + public static final Person JAMAL = new PersonBuilder().withName(VALID_NAME_JAMAL).withPhone(VALID_PHONE_JAMAL) + .withEmail(VALID_EMAIL_JAMAL).withAddress(VALID_ADDRESS_JAMAL).build(); + public static final Person CHAD = new PersonBuilder().withName(VALID_NAME_CHAD).withPhone(VALID_PHONE_CHAD) + .withEmail(VALID_EMAIL_CHAD).withAddress(VALID_ADDRESS_CHAD).build(); public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER private TypicalPersons() {} // prevents instantiation