From 29d6f8df89c44a8e03c90583ad9d4f37626853a2 Mon Sep 17 00:00:00 2001 From: Wang Zihan Date: Wed, 1 Nov 2023 23:56:13 +0800 Subject: [PATCH 1/3] Add filter and sorter to `list event` command and bug fixes - Add filter and sorter to `list event` command - Fix a bug that allows adding events with a start time after the end time - Fix a bug in the JUnit Test Cases that is sharing a same set of typical person instance among all testcases (which makes the testcases interfere with each other) --- docs/UserGuide.md | 17 ++- .../address/commons/util/DateTimeUtil.java | 48 +++++++- .../address/commons/util/StringUtil.java | 17 +++ .../java/seedu/address/logic/Messages.java | 1 + .../logic/commands/AddEventCommand.java | 7 +- .../logic/commands/DeleteEventCommand.java | 10 +- .../logic/commands/ListEventCommand.java | 58 +++++++++- .../logic/parser/AddEventCommandParser.java | 18 +-- .../seedu/address/logic/parser/CliSyntax.java | 5 +- .../logic/parser/HelpCommandParser.java | 2 +- .../logic/parser/ListCommandParser.java | 2 +- .../logic/parser/ListEventCommandParser.java | 47 ++++++++ .../java/seedu/address/model/AddressBook.java | 42 +++++-- src/main/java/seedu/address/model/Model.java | 38 +++++++ .../seedu/address/model/ModelManager.java | 39 +++++++ .../address/model/ReadOnlyAddressBook.java | 7 ++ .../java/seedu/address/model/event/Event.java | 19 +++- .../seedu/address/model/event/EventTime.java | 19 +++- .../address/model/event/UniqueEventList.java | 62 ++++++++--- .../exceptions/EventOverlapException.java | 12 ++ .../seedu/address/model/person/Person.java | 3 +- .../address/model/util/SampleDataUtil.java | 24 ++-- .../commons/util/DateTimeUtilTest.java | 104 +++++++++++++++++- .../logic/commands/AddPersonCommandTest.java | 35 ++++++ .../commands/DeleteEventCommandTest.java | 29 +++-- .../logic/commands/ListEventCommandTest.java | 19 +++- ...st.java => AddEventCommandParserTest.java} | 11 +- ...java => DeleteEventCommandParserTest.java} | 2 +- .../logic/parser/ListCommandParserTest.java | 5 +- .../parser/ListEventCommandParserTest.java | 95 ++++++++++++++++ .../seedu/address/model/AddressBookTest.java | 7 ++ .../address/testutil/TypicalPersons.java | 16 ++- 32 files changed, 733 insertions(+), 87 deletions(-) create mode 100644 src/main/java/seedu/address/logic/parser/ListEventCommandParser.java create mode 100644 src/main/java/seedu/address/model/event/exceptions/EventOverlapException.java rename src/test/java/seedu/address/logic/parser/{AddCommandParserTest.java => AddEventCommandParserTest.java} (87%) rename src/test/java/seedu/address/logic/parser/{DeleteCommandParserTest.java => DeleteEventCommandParserTest.java} (98%) create mode 100644 src/test/java/seedu/address/logic/parser/ListEventCommandParserTest.java diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 318d0da9ae5..aea3eec985d 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -165,9 +165,20 @@ Examples: ### Listing all events : `list events` -Shows a list of all events. +Shows a list of all events or events within a specified time interval. -Format: `list events` +Format: `list events [-descending] [-st filter_start_time] [-et filter_end_time]` (start time and end time are inclusive) + +Arguments `-st` and `-et` must both present or both not present. + - If they both not present, the application will show you all events in the address book. + - If they both present, the application will show you the events within the time interval. + +By default, the list of events are sorted by the start time in ascending order (the event with the earliest start time is in the beginning). If you want to use descending order, add `-descending` to the command. + +Examples +* `list events` +* `list events -st 2023-11-01 -et 2023-11-02` +* `list events -descending -st 2023-11-01 -et 2023-11-02` ### Deleting an event : `delete event` @@ -226,5 +237,5 @@ Action | Format, Examples **List Notes** | `list notes` **Add Event** | `add event -id CONTACT_ID -en EVENT_NAME -st START_TIME [-et END_TIME] [-loc LOCATION] [-info INFORMATION]`
e.g., `add event -id 1 -en Meeting with professor -st 12:00 -et 01:00 -loc COM 1 Basement -info Discuss the project implementation with the professor` **Delete Event** | `delete event -id CONTACT_ID -eid EVENT_ID`
e.g., `delete event -id 1 -eid 1` -**List Events** | `list events` +**List Events** | `list events [-descending] [-st filter_start_time] [-et filter_end_time]`
e.g., `list events -descending -st 2023-11-01 -et 2023-11-02` **Help** | `help` diff --git a/src/main/java/seedu/address/commons/util/DateTimeUtil.java b/src/main/java/seedu/address/commons/util/DateTimeUtil.java index 0ebf249ab67..7ec9fed40a5 100644 --- a/src/main/java/seedu/address/commons/util/DateTimeUtil.java +++ b/src/main/java/seedu/address/commons/util/DateTimeUtil.java @@ -1,5 +1,8 @@ package seedu.address.commons.util; +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; @@ -15,6 +18,7 @@ public class DateTimeUtil { * @throws DateTimeParseException If the string cannot be parsed into LocalDateTime */ public static LocalDateTime parseString(String str) throws DateTimeParseException { + requireNonNull(str); DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm[:ss]"); LocalDateTime result = null; try { @@ -25,7 +29,10 @@ public static LocalDateTime parseString(String str) throws DateTimeParseExceptio String appendTime = " 00:00:00"; result = LocalDateTime.parse(str + appendTime, formatter1); } else { - String appendDate = now.getYear() + "-" + now.getMonthValue() + "-" + now.getDayOfMonth() + " "; + int month = now.getMonthValue(); + int day = now.getDayOfMonth(); + String appendDate = now.getYear() + "-" + (month <= 9 ? "0" + month : month) + + "-" + (day <= 9 ? "0" + day : day) + " "; result = LocalDateTime.parse(appendDate + str, formatter1); } } @@ -39,7 +46,46 @@ public static LocalDateTime parseString(String str) throws DateTimeParseExceptio * @return The String representation of this {@code LocalDateTime} object, in the format {@code yyyy-MM-dd HH:mm:ss} */ public static String toFormattedString(LocalDateTime time) { + requireNonNull(time); return time.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } + /** + * Return {@code true} if {@code timeToCheck} is between {@code intervalStart} and {@code intervalEnd} (inclusive). + * In another word, returns {@code true} when {@code intervalStart} <= {@code timeToCheck} <= {@code intervalEnd} + * + * @param intervalStart The start time for the interval + * @param intervalEnd The end time for the interval + * @param timeToCheck The time that is needed for checking + * @return Whether {@code timeToCheck} is between {@code intervalStart} and {@code intervalEnd} or not + */ + public static boolean withinTimeInterval(LocalDateTime intervalStart, + LocalDateTime intervalEnd, LocalDateTime timeToCheck) { + requireAllNonNull(intervalStart, intervalEnd, timeToCheck); + assert intervalStart.equals(intervalEnd) || intervalStart.isBefore(intervalEnd); + return timeToCheck.equals(intervalStart) + || timeToCheck.equals(intervalEnd) + || (timeToCheck.isAfter(intervalStart) && timeToCheck.isBefore(intervalEnd)); + } + + /** + * Return {@code true} if the time interval {@code intervalAStart}~{@code intervalAEnd} + * overlaps with the time interval {@code intervalBStart}~{@code intervalBEnd} (inclusive of start and end time). + * Start time is inclusive, end time is exclusive + * + * @param intervalAStart The start time for the first interval + * @param intervalAEnd The end time for the first interval + * @param intervalBStart The start time for the second interval + * @param intervalBEnd The end time for the second interval + * @return Whether the two time intervals overlap or not + */ + public static boolean timeIntervalsOverlap(LocalDateTime intervalAStart, LocalDateTime intervalAEnd, + LocalDateTime intervalBStart, LocalDateTime intervalBEnd) { + requireAllNonNull(intervalAStart, intervalAEnd, intervalBStart, intervalBEnd); + return withinTimeInterval(intervalAStart, intervalAEnd, intervalBStart) + || withinTimeInterval(intervalAStart, intervalAEnd, intervalBEnd) + || withinTimeInterval(intervalBStart, intervalBEnd, intervalAStart) + || withinTimeInterval(intervalBStart, intervalBEnd, intervalAEnd); + } + } diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/address/commons/util/StringUtil.java index 61cc8c9a1cb..b76b2a9c37b 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/seedu/address/commons/util/StringUtil.java @@ -6,6 +6,9 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.Arrays; +import java.util.List; + +import seedu.address.model.event.Event; /** * Helper functions for handling strings. @@ -65,4 +68,18 @@ public static boolean isNonZeroUnsignedInteger(String s) { return false; } } + + /** + * Convert the event list to a human-readable string + * + * @param eventList The event list + * @return The filtered event list as string + */ + public static String eventListToString(List eventList) { + StringBuilder str = new StringBuilder(); + eventList.forEach( + evt -> str.append(evt.getUiText()).append("\n") + ); + return str.toString(); + } } diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java index 6a38ade6d61..1933b1a2a80 100644 --- a/src/main/java/seedu/address/logic/Messages.java +++ b/src/main/java/seedu/address/logic/Messages.java @@ -32,6 +32,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_START_TIME_AFTER_END_TIME = "Start time %s is after the end time %s!\n"; public static final String MESSAGE_INVALID_INTEGER_ARGUMENT = "The provided argument is not a valid integer! \n%1$s"; public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; diff --git a/src/main/java/seedu/address/logic/commands/AddEventCommand.java b/src/main/java/seedu/address/logic/commands/AddEventCommand.java index c4c3ddd0dca..5a29e97fc1e 100644 --- a/src/main/java/seedu/address/logic/commands/AddEventCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddEventCommand.java @@ -15,6 +15,7 @@ public class AddEventCommand extends AddCommand { public static final String SECONDARY_COMMAND_WORD = "event"; public static final String MESSAGE_SUCCESS = "New event added: "; + public static final String MESSAGE_ERROR = "Error: "; public static final String MESSAGE_CONTACT_NOT_FOUND = "Can not find the target contact with ID: "; public static final String MESSAGE_USAGE = COMMAND_WORD + " " + SECONDARY_COMMAND_WORD @@ -41,7 +42,11 @@ public CommandResult execute(Model model) throws CommandException { if (person == null) { throw new CommandException(MESSAGE_CONTACT_NOT_FOUND + this.contactId.getId()); } - person.addEvent(this.toAdd); + try { + model.addEvent(this.toAdd, person); + } catch (Exception e) { + return new CommandResult(MESSAGE_ERROR + e.getMessage()); + } return new CommandResult(MESSAGE_SUCCESS + toAdd.getName()); } diff --git a/src/main/java/seedu/address/logic/commands/DeleteEventCommand.java b/src/main/java/seedu/address/logic/commands/DeleteEventCommand.java index ad4efeb5561..0d599a3a89c 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteEventCommand.java +++ b/src/main/java/seedu/address/logic/commands/DeleteEventCommand.java @@ -6,6 +6,7 @@ import seedu.address.model.Model; import seedu.address.model.event.Event; import seedu.address.model.event.EventID; +import seedu.address.model.event.exceptions.EventNotFoundException; import seedu.address.model.person.ContactID; import seedu.address.model.person.Person; @@ -39,11 +40,12 @@ public CommandResult execute(Model model) throws CommandException { if (person == null) { throw new CommandException(MESSAGE_PERSON_NOT_FOUND + this.contactId); } - Event deletedEvent = person.removeEventByUserFriendlyId(this.eventIdToDelete); - if (deletedEvent == null) { + try { + Event deletedEvent = model.removeEventByID(this.eventIdToDelete, person); + return new CommandResult(MESSAGE_SUCCESS + this.eventIdToDelete + + ". " + deletedEvent.getName()); + } catch (EventNotFoundException e) { throw new CommandException(MESSAGE_EVENT_NOT_FOUND + this.eventIdToDelete); } - - return new CommandResult(MESSAGE_SUCCESS + this.eventIdToDelete + ". " + deletedEvent.getName()); } } diff --git a/src/main/java/seedu/address/logic/commands/ListEventCommand.java b/src/main/java/seedu/address/logic/commands/ListEventCommand.java index 15c94b7219f..3bb06f08835 100644 --- a/src/main/java/seedu/address/logic/commands/ListEventCommand.java +++ b/src/main/java/seedu/address/logic/commands/ListEventCommand.java @@ -2,27 +2,73 @@ import static java.util.Objects.requireNonNull; +import java.time.LocalDateTime; import java.util.List; +import seedu.address.commons.util.DateTimeUtil; +import seedu.address.commons.util.StringUtil; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.address.model.event.Event; +import seedu.address.model.event.EventTime; /** * The command handler for {@code list events} command */ public class ListEventCommand extends ListCommand { public static final String SECONDARY_COMMAND_WORD = "events"; - public static final String MESSAGE = "Here are all the events in this address book:\n"; + public static final String MESSAGE_ALL = "Here are all the events in this address book "; + public static final String MESSAGE_FILTERED = "Here are the events in this address book within the time interval "; + public static final String MESSAGE_ASCENDING = "(in ascending order):\n"; + public static final String MESSAGE_DESCENDING = "(in descending order):\n"; - public static final String MESSAGE_USAGE = COMMAND_WORD + SECONDARY_COMMAND_WORD - + ": Lists all events in the address book"; + public static final String MESSAGE_USAGE = COMMAND_WORD + " " + SECONDARY_COMMAND_WORD + + " [-descending] [-st filter_start_time] [-et filter_end_time]" + + " (-st and -et must either both present or both not present)"; + + private LocalDateTime filterStartTime; + private LocalDateTime filterEndTime; + private boolean sortAscending; + + /** + * Constructor for {@code ListEventCommand} class + * @param filterStartTime The start time for filter + * @param filterEndTime The end time for filter + * @param sortAscending Set to {@code true} to sort the events by start time in ascending order, + * {@code false} in descending order. + */ + public ListEventCommand(EventTime filterStartTime, EventTime filterEndTime, boolean sortAscending) { + this.filterStartTime = filterStartTime.getTime(); + this.filterEndTime = filterEndTime.getTime(); + this.sortAscending = sortAscending; + } @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - List persons = model.getAddressBook().getPersonList(); - String result = MESSAGE + model.getAddressBook().eventListToString(); + assert (filterStartTime == null && filterEndTime == null) || (filterStartTime != null && filterEndTime != null); + String result = ""; + if (filterStartTime == null) { + model.updateFilteredEventList(evt -> true); // Remove the filter to get the full event list + result = MESSAGE_ALL; + } else { + model.updateFilteredEventList( + evt -> DateTimeUtil.withinTimeInterval(this.filterStartTime, this.filterEndTime, + evt.getStartTime()) + ); + result = MESSAGE_FILTERED; + } + result += this.sortAscending ? MESSAGE_ASCENDING : MESSAGE_DESCENDING; + List eventList = model.getSortedFilteredEventList((o1, o2) -> { + LocalDateTime startTime1 = o1.getStartTime(); + LocalDateTime startTime2 = o2.getStartTime(); + return (startTime1.isBefore(startTime2) + ? 1 + : startTime1.equals(startTime2) + ? 0 + : -1) * (sortAscending ? -1 : 1); + }); + result += StringUtil.eventListToString(eventList); return new CommandResult(result); } } diff --git a/src/main/java/seedu/address/logic/parser/AddEventCommandParser.java b/src/main/java/seedu/address/logic/parser/AddEventCommandParser.java index 4a7fa12ece7..40ef42aea51 100644 --- a/src/main/java/seedu/address/logic/parser/AddEventCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddEventCommandParser.java @@ -1,12 +1,13 @@ package seedu.address.logic.parser; import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EVENT_END_TIME; +import static seedu.address.logic.Messages.MESSAGE_START_TIME_AFTER_END_TIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_END_TIME; import static seedu.address.logic.parser.CliSyntax.PREFIX_EVENT_INFORMATION; import static seedu.address.logic.parser.CliSyntax.PREFIX_EVENT_LOCATION; import static seedu.address.logic.parser.CliSyntax.PREFIX_EVENT_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EVENT_START_TIME; import static seedu.address.logic.parser.CliSyntax.PREFIX_PERSON_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_START_TIME; import seedu.address.logic.commands.AddEventCommand; import seedu.address.logic.parser.exceptions.ParseException; @@ -26,26 +27,29 @@ public class AddEventCommandParser implements Parser { @Override public AddEventCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_PERSON_ID, - PREFIX_EVENT_NAME, PREFIX_EVENT_START_TIME, PREFIX_EVENT_END_TIME, + PREFIX_EVENT_NAME, PREFIX_START_TIME, PREFIX_END_TIME, PREFIX_EVENT_LOCATION, PREFIX_EVENT_INFORMATION); if (!ParserUtil.arePrefixesPresent(argMultimap, PREFIX_PERSON_ID, PREFIX_EVENT_NAME, - PREFIX_EVENT_START_TIME) || !argMultimap.getPreamble().isEmpty()) { + PREFIX_START_TIME) || !argMultimap.getPreamble().isEmpty()) { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddEventCommand.MESSAGE_USAGE)); } argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_PERSON_ID, - PREFIX_EVENT_NAME, PREFIX_EVENT_START_TIME, PREFIX_EVENT_END_TIME, PREFIX_EVENT_LOCATION, + PREFIX_EVENT_NAME, PREFIX_START_TIME, PREFIX_END_TIME, PREFIX_EVENT_LOCATION, PREFIX_EVENT_INFORMATION); EventName eventName = ParserUtil.parseEventName(argMultimap.getValue(PREFIX_EVENT_NAME).get()); - EventTime startTime = ParserUtil.parseEventTime(argMultimap.getValue(PREFIX_EVENT_START_TIME).get()); - EventTime endTime = ParserUtil.parseEventTime(argMultimap.getValue(PREFIX_EVENT_END_TIME).orElseGet(()->null)); + EventTime startTime = ParserUtil.parseEventTime(argMultimap.getValue(PREFIX_START_TIME).get()); + EventTime endTime = ParserUtil.parseEventTime(argMultimap.getValue(PREFIX_END_TIME).orElseGet(()->null)); EventLocation location = ParserUtil.parseEventLocation(argMultimap.getValue(PREFIX_EVENT_LOCATION).orElseGet(()->null)); EventInformation information = ParserUtil.parseEventInformation(argMultimap.getValue(PREFIX_EVENT_INFORMATION).orElseGet(()->null)); ContactID contactId = ParserUtil.parseContactID(argMultimap.getValue(PREFIX_PERSON_ID).get()); + if (startTime.isAfter(endTime)) { + throw new ParseException(String.format(MESSAGE_START_TIME_AFTER_END_TIME, startTime, endTime)); + } Event newEvent = new Event(eventName, startTime, endTime, location, information); return new AddEventCommand(contactId, newEvent); diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java index b8222073ce8..4119c8bc7af 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -27,10 +27,11 @@ public class CliSyntax { public static final Prefix PREFIX_NOTE_TITLE = new Prefix("-tit"); public static final Prefix PREFIX_NOTE_CONTENT = new Prefix("-con"); public static final Prefix PREFIX_EVENT_NAME = new Prefix("-en"); - public static final Prefix PREFIX_EVENT_START_TIME = new Prefix("-st"); - public static final Prefix PREFIX_EVENT_END_TIME = new Prefix("-et"); + public static final Prefix PREFIX_START_TIME = new Prefix("-st"); + public static final Prefix PREFIX_END_TIME = new Prefix("-et"); public static final Prefix PREFIX_EVENT_LOCATION = new Prefix("-loc"); public static final Prefix PREFIX_EVENT_INFORMATION = new Prefix("-info"); + public static final Prefix PREFIX_SORT_DESCENDING = new Prefix("-descending"); public static final ArrayList COMMAND_LIST = new ArrayList(Arrays.asList( AddCommand.COMMAND_WORD, diff --git a/src/main/java/seedu/address/logic/parser/HelpCommandParser.java b/src/main/java/seedu/address/logic/parser/HelpCommandParser.java index d8c41f871cb..23c0713ab18 100644 --- a/src/main/java/seedu/address/logic/parser/HelpCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/HelpCommandParser.java @@ -14,7 +14,7 @@ import seedu.address.logic.parser.exceptions.ParseException; /** - * The parser for all secondary {@code delete} commands + * The parser for {@code help} command */ public class HelpCommandParser implements Parser { @Override diff --git a/src/main/java/seedu/address/logic/parser/ListCommandParser.java b/src/main/java/seedu/address/logic/parser/ListCommandParser.java index b24956dec3a..5e0288f6697 100644 --- a/src/main/java/seedu/address/logic/parser/ListCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/ListCommandParser.java @@ -22,7 +22,7 @@ public ListCommand parse(String userInput) throws ParseException { case ListNoteCommand.SECONDARY_COMMAND_WORD: return new ListNoteCommand(); case ListEventCommand.SECONDARY_COMMAND_WORD: - return new ListEventCommand(); + return new ListEventCommandParser().parse(args); default: throw new ParseException(MESSAGE_UNKNOWN_COMMAND); } diff --git a/src/main/java/seedu/address/logic/parser/ListEventCommandParser.java b/src/main/java/seedu/address/logic/parser/ListEventCommandParser.java new file mode 100644 index 00000000000..2ad50e284da --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ListEventCommandParser.java @@ -0,0 +1,47 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.Messages.MESSAGE_START_TIME_AFTER_END_TIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_END_TIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SORT_DESCENDING; +import static seedu.address.logic.parser.CliSyntax.PREFIX_START_TIME; + +import seedu.address.logic.commands.ListEventCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.event.EventTime; + + +/** + * The parser for {@code list event} command + */ +public class ListEventCommandParser implements Parser { + + @Override + public ListEventCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, + PREFIX_START_TIME, PREFIX_END_TIME, PREFIX_SORT_DESCENDING); + + if (( + ParserUtil.arePrefixesPresent(argMultimap, PREFIX_START_TIME) + && !ParserUtil.arePrefixesPresent(argMultimap, PREFIX_END_TIME)) + || (!ParserUtil.arePrefixesPresent(argMultimap, PREFIX_START_TIME) + && ParserUtil.arePrefixesPresent(argMultimap, PREFIX_END_TIME)) + || !argMultimap.getPreamble().isEmpty() + || !argMultimap.getValue(PREFIX_SORT_DESCENDING).orElseGet(()->"").isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListEventCommand.MESSAGE_USAGE)); + } + + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_START_TIME, PREFIX_END_TIME, PREFIX_SORT_DESCENDING); + + EventTime filterStartTime = ParserUtil.parseEventTime(argMultimap.getValue(PREFIX_START_TIME) + .orElseGet(()->null)); + EventTime filterEndTime = ParserUtil.parseEventTime(argMultimap.getValue(PREFIX_END_TIME).orElseGet(()->null)); + + boolean useAscendingOrder = !ParserUtil.arePrefixesPresent(argMultimap, PREFIX_SORT_DESCENDING); + + if (filterStartTime.isAfter(filterEndTime)) { + throw new ParseException(String.format(MESSAGE_START_TIME_AFTER_END_TIME, filterStartTime, filterEndTime)); + } + return new ListEventCommand(filterStartTime, filterEndTime, useAscendingOrder); + } +} diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index 440a421d3c4..ce15788e788 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -7,6 +7,7 @@ import javafx.collections.ObservableList; import seedu.address.commons.util.ToStringBuilder; import seedu.address.model.event.Event; +import seedu.address.model.event.EventID; import seedu.address.model.event.UniqueEventList; import seedu.address.model.note.Note; import seedu.address.model.person.Person; @@ -19,6 +20,7 @@ public class AddressBook implements ReadOnlyAddressBook { private final UniquePersonList persons; + private final UniqueEventList allEvents; /* * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication @@ -29,6 +31,7 @@ public class AddressBook implements ReadOnlyAddressBook { */ { persons = new UniquePersonList(); + allEvents = new UniqueEventList(); } public AddressBook() {} @@ -51,6 +54,15 @@ public void setPersons(List persons) { this.persons.setPersons(persons); } + /** + * Replaces the contents of the event list with {@code events}. + * {@code events} must not contain duplicate persons. + */ + public void setEvents(List events) { + this.allEvents.setEvents(events); + } + + /** * Resets the existing data of this {@code AddressBook} with {@code newData}. */ @@ -58,6 +70,7 @@ public void resetData(ReadOnlyAddressBook newData) { requireNonNull(newData); setPersons(newData.getPersonList()); + setEvents(newData.getEventList()); } //// person-level operations @@ -76,6 +89,26 @@ public boolean hasPerson(Person person) { */ public void addPerson(Person p) { persons.add(p); + p.getEvents().forEach(this.allEvents::add); + } + + /** + * Adds an event to the address book with the specified person as owner. + * The event must not already exists in the address book. + */ + public void addEvent(Event toAdd, Person owner) { + allEvents.add(toAdd); + owner.addEvent(toAdd); + } + + /** + * Removes an event to the address book for the specified person and the global event list. + * The event must exist in the address book. + */ + public Event removeEventByID(EventID id, Person owner) { + Event event = owner.removeEventByUserFriendlyId(id); + allEvents.remove(event); + return event; } /** @@ -143,14 +176,9 @@ public ObservableList getPersonList() { return persons.asUnmodifiableObservableList(); } + @Override public ObservableList getEventList() { - UniqueEventList out = new UniqueEventList(); - persons.forEach( - person -> person.getEvents().forEach( - event -> out.add(event) - ) - ); - return out.asUnmodifiableObservableList(); + return this.allEvents.asUnmodifiableObservableList(); } @Override diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index c95a414a0f2..84255f11295 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -1,10 +1,14 @@ package seedu.address.model; import java.nio.file.Path; +import java.util.Comparator; +import java.util.List; import java.util.function.Predicate; import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; +import seedu.address.model.event.Event; +import seedu.address.model.event.EventID; import seedu.address.model.person.ContactID; import seedu.address.model.person.Person; @@ -86,6 +90,21 @@ public interface Model { */ void updateFilteredPersonList(Predicate predicate); + /** Returns an unmodifiable view of the filtered event list */ + ObservableList getFilteredEventList(); + + /** + * Updates the filter of the filtered event list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredEventList(Predicate predicate); + + /** + * Get a list from sorting the filtered event list by the given {@code comparator}. + * @throws NullPointerException if {@code comparator} is null. + */ + List getSortedFilteredEventList(Comparator comparator); + /** * Find a person by index. * If the index is invalid, returns null. @@ -97,4 +116,23 @@ public interface Model { * If the ID is invalid, returns null. */ Person findPersonByUserFriendlyId(ContactID id); + + /** + * Add an event under the event list of the specified person and also the global event list + */ + void addEvent(Event toAdd, Person owner); + + /** + * Remove an event by its user-friendly id for the specified person and also in the global event list + * @param eventID The id of the event you want to remove + * @return The event object that is just deleted if the operation is successful + * or {@code null} if the event with this name does not exist + */ + Event removeEventByID(EventID eventID, Person owner); + + /** + * Convert the filtered event list to a human-readable string + * @return The filtered event list as string + */ + String filteredEventListToString(); } diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index b365f200412..ab9c093cf6a 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -4,14 +4,19 @@ import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; import java.nio.file.Path; +import java.util.Comparator; import java.util.List; import java.util.function.Predicate; import java.util.logging.Logger; +import java.util.stream.Collectors; import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.util.StringUtil; +import seedu.address.model.event.Event; +import seedu.address.model.event.EventID; import seedu.address.model.person.ContactID; import seedu.address.model.person.Person; @@ -24,6 +29,7 @@ public class ModelManager implements Model { private final AddressBook addressBook; private final UserPrefs userPrefs; private final FilteredList filteredPersons; + private final FilteredList filteredEvents; /** * Initializes a ModelManager with the given addressBook and userPrefs. @@ -36,6 +42,7 @@ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs this.addressBook = new AddressBook(addressBook); this.userPrefs = new UserPrefs(userPrefs); filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); + filteredEvents = new FilteredList<>(this.addressBook.getEventList()); } public ModelManager() { @@ -130,6 +137,23 @@ public void updateFilteredPersonList(Predicate predicate) { filteredPersons.setPredicate(predicate); } + @Override + public ObservableList getFilteredEventList() { + return filteredEvents; + } + + @Override + public void updateFilteredEventList(Predicate predicate) { + requireNonNull(predicate); + filteredEvents.setPredicate(predicate); + } + + @Override + public List getSortedFilteredEventList(Comparator comparator) { + requireNonNull(comparator); + return filteredEvents.stream().sorted(comparator).collect(Collectors.toUnmodifiableList()); + } + @Override public Person findPersonByIndex(int index) { List personList = this.addressBook.getPersonList(); @@ -161,4 +185,19 @@ public boolean equals(Object other) { && filteredPersons.equals(otherModelManager.filteredPersons); } + @Override + public void addEvent(Event toAdd, Person owner) { + this.addressBook.addEvent(toAdd, owner); + } + + @Override + public Event removeEventByID(EventID eventID, Person owner) { + return this.addressBook.removeEventByID(eventID, owner); + } + + @Override + public String filteredEventListToString() { + return StringUtil.eventListToString(this.filteredEvents); + } + } diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java index d136369bc0f..207ae866a8e 100644 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java @@ -1,6 +1,7 @@ package seedu.address.model; import javafx.collections.ObservableList; +import seedu.address.model.event.Event; import seedu.address.model.person.Person; /** @@ -14,6 +15,12 @@ public interface ReadOnlyAddressBook { */ ObservableList getPersonList(); + /** + * Returns an unmodifiable view of the event list. + * This list will not contain any duplicate events. + */ + ObservableList getEventList(); + /** * Convert the event list to a human-readable string * @return The event list as string diff --git a/src/main/java/seedu/address/model/event/Event.java b/src/main/java/seedu/address/model/event/Event.java index 950a2a33a80..b61d20ae910 100644 --- a/src/main/java/seedu/address/model/event/Event.java +++ b/src/main/java/seedu/address/model/event/Event.java @@ -2,6 +2,8 @@ import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import java.time.LocalDateTime; + /** * The class for holding an Event */ @@ -60,7 +62,6 @@ public String getName() { * @return The start time in string */ public String getStartString() { - // Temporary, can use Util class instead return this.start.toString(); } @@ -72,6 +73,22 @@ public String getEndString() { return this.end.toString(); } + /** + * Get the start time of the event, represented in {@code LocalDateTime} + * @return The start time in {@code LocalDateTime} + */ + public LocalDateTime getStartTime() { + return this.start.getTime(); + } + + /** + * Get the end time of the event, represented in {@code LocalDateTime} + * @return The end time in {@code LocalDateTime} + */ + public LocalDateTime getEndTime() { + return this.end.getTime(); + } + /** * Get the location of the event, represented in string * @return The location in string diff --git a/src/main/java/seedu/address/model/event/EventTime.java b/src/main/java/seedu/address/model/event/EventTime.java index fcf964e2a2f..7b594bb564d 100644 --- a/src/main/java/seedu/address/model/event/EventTime.java +++ b/src/main/java/seedu/address/model/event/EventTime.java @@ -10,7 +10,7 @@ */ public class EventTime { public static final String MESSAGE_INVALID_DATETIME_FORMAT = "Invalid date-time format! "; - public static final String MESSAGE_NON_EMPTY = "Event time can not be empty!"; + public static final String MESSAGE_NON_EMPTY = "Time can not be empty!"; private final LocalDateTime time; @@ -30,6 +30,14 @@ public static EventTime fromString(String timeStr) throws DateTimeParseException return timeStr.isEmpty() ? new EventTime() : new EventTime(timeStr); } + /** + * Return the date-time as {@code LocalDateTime} + * @return The date-time as {@code LocalDateTime} + */ + public LocalDateTime getTime() { + return this.time; + } + /** * Get the String representation of this {@code EventTime} object * @return The String representation of this {@code EventTime} object, in the format {@code yyyy-MM-dd HH:mm:ss} @@ -53,4 +61,13 @@ public boolean equals(Object other) { EventTime otherName = (EventTime) other; return time.equals(otherName.time); } + + /** + * Return {@code true} if this {@code EventTime} is after {@code other}. + * Always returns false when this {@code EventTime} or {@code other} contains null time + * @param other The other {@code EventTime} + */ + public boolean isAfter(EventTime other) { + return this.time != null && other.time != null && this.time.isAfter(other.time); + } } diff --git a/src/main/java/seedu/address/model/event/UniqueEventList.java b/src/main/java/seedu/address/model/event/UniqueEventList.java index 8495bc3a1dd..6a6cb2a58b9 100644 --- a/src/main/java/seedu/address/model/event/UniqueEventList.java +++ b/src/main/java/seedu/address/model/event/UniqueEventList.java @@ -3,24 +3,23 @@ import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import java.time.LocalDateTime; import java.util.Iterator; import java.util.List; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import seedu.address.commons.util.DateTimeUtil; import seedu.address.model.event.exceptions.DuplicateEventException; import seedu.address.model.event.exceptions.EventNotFoundException; +import seedu.address.model.event.exceptions.EventOverlapException; /** - * A list of persons that enforces uniqueness between its elements and does not allow nulls. - * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of - * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is - * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so - * as to ensure that the person with exactly the same fields will be removed. - * + * A list of events that enforces uniqueness between its elements, does not allow overlaps and does not allow nulls. + * An event can be added only if it does not already exist in the list, + * and it does not overlap with all currently existed events by time * Supports a minimal set of list operations. * - * @see Person#isSamePerson(Person) */ public class UniqueEventList implements Iterable { @@ -29,7 +28,7 @@ public class UniqueEventList implements Iterable { FXCollections.unmodifiableObservableList(internalList); /** - * Returns true if the list contains an equivalent person as the given argument. + * Returns true if the list contains an equivalent event as the given argument. */ public boolean contains(Event toCheck) { requireNonNull(toCheck); @@ -44,15 +43,19 @@ public void add(Event toAdd) { if (this.contains(toAdd)) { throw new DuplicateEventException(); } + Event overlappingEvent = this.getOverlappingEvent(toAdd); + if (overlappingEvent != null) { + throw new EventOverlapException(toAdd, overlappingEvent); + } internalList.add(toAdd); } /** - * Replaces the person {@code target} in the list with {@code editedPerson}. + * Replaces the event {@code target} in the list with {@code editedEvent}. * {@code target} must exist in the list. - * The person identity of {@code editedPerson} must not be the same as another existing person in the list. + * The event identity of {@code editedPerson} must not be the same as another existing event in the list. */ - public void setPerson(Event target, Event editedEvent) { + public void setEvent(Event target, Event editedEvent) { requireAllNonNull(target, editedEvent); int index = internalList.indexOf(target); @@ -68,8 +71,8 @@ public void setPerson(Event target, Event editedEvent) { } /** - * Removes the equivalent person from the list. - * The person must exist in the list. + * Removes the equivalent event from the list. + * The event must exist in the list. */ public void remove(Event toRemove) { requireNonNull(toRemove); @@ -78,16 +81,16 @@ public void remove(Event toRemove) { } } - public void setPersons(UniqueEventList replacement) { + public void setEvents(UniqueEventList replacement) { requireNonNull(replacement); internalList.setAll(replacement.internalList); } /** - * Replaces the contents of this list with {@code persons}. - * {@code persons} must not contain duplicate persons. + * Replaces the contents of this list with {@code events}. + * {@code persons} must not contain duplicate events. */ - public void setPersons(List events) { + public void setEvents(List events) { requireAllNonNull(events); if (!eventsAreUnique(events)) { throw new DuplicateEventException(); @@ -134,7 +137,7 @@ public String toString() { } /** - * Returns true if {@code persons} contains only unique persons. + * Returns true if {@code events} contains only unique events. */ private boolean eventsAreUnique(List events) { for (int i = 0; i < events.size() - 1; i++) { @@ -146,4 +149,27 @@ private boolean eventsAreUnique(List events) { } return true; } + + /** + * Return an event in the list that is overlapping with {@code newEvent}. + * If no overlapping event is found, return {@code null} + * {@code newEvent} must not exists in the list. + * + * @param newEvent The event for checking overlapping + * @return The overlapping event or {@code null}. + */ + private Event getOverlappingEvent(Event newEvent) { + requireNonNull(newEvent); + assert !this.contains(newEvent); + LocalDateTime newEventStartTime = newEvent.getStartTime(); + LocalDateTime newEventEndTime = newEvent.getEndTime(); + for (Event e : this.internalList) { + LocalDateTime startTime = e.getStartTime(); + LocalDateTime endTime = e.getEndTime(); + if (DateTimeUtil.timeIntervalsOverlap(newEventStartTime, newEventEndTime, startTime, endTime)) { + return e; + } + } + return null; + } } diff --git a/src/main/java/seedu/address/model/event/exceptions/EventOverlapException.java b/src/main/java/seedu/address/model/event/exceptions/EventOverlapException.java new file mode 100644 index 00000000000..30fbf114c10 --- /dev/null +++ b/src/main/java/seedu/address/model/event/exceptions/EventOverlapException.java @@ -0,0 +1,12 @@ +package seedu.address.model.event.exceptions; + +import seedu.address.model.event.Event; + +/** + * Thrown when two events overlap (clash) by time + */ +public class EventOverlapException extends RuntimeException { + public EventOverlapException(Event e1, Event e2) { + super("Event " + e1.getUiText() + " overlaps with event " + e2.getUiText()); + } +} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java index a1ba6e9e069..e1e64d890ef 100644 --- a/src/main/java/seedu/address/model/person/Person.java +++ b/src/main/java/seedu/address/model/person/Person.java @@ -13,6 +13,7 @@ import seedu.address.commons.util.ToStringBuilder; import seedu.address.model.event.Event; import seedu.address.model.event.EventID; +import seedu.address.model.event.exceptions.EventNotFoundException; import seedu.address.model.note.Note; import seedu.address.model.note.NoteID; import seedu.address.model.tag.Tag; @@ -138,7 +139,7 @@ public Event removeEventByUserFriendlyId(EventID id) { private Event removeEventByIndex(int index) { if (index < 0 || index >= this.events.size()) { - return null; + throw new EventNotFoundException(); } return this.events.remove(index); } diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java index 3695246538f..dff96c97b39 100644 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java @@ -27,35 +27,41 @@ public static Person[] getSamplePersons() { ArrayList sampleNotes = new ArrayList(); sampleNotes.add(new Note("Hello", "Sample content")); - ArrayList sampleEvents = new ArrayList(); - sampleEvents.add(new Event("Sample event", + ArrayList sampleEvents1 = new ArrayList(); + sampleEvents1.add(new Event("Sample event", LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")), LocalDateTime.now().plusHours(1).format(DateTimeFormatter.ofPattern("HH:mm:ss")), "Some Location", "Some Information")); + ArrayList sampleEvents2 = new ArrayList(); + sampleEvents2.add(new Event("Another sample event", + LocalDateTime.now().plusHours(2).format(DateTimeFormatter.ofPattern("HH:mm:ss")), + LocalDateTime.now().plusHours(3).format(DateTimeFormatter.ofPattern("HH:mm:ss")), + "Some Location", + "Some Information")); return new Person[] { new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), new Address("Blk 30 Geylang Street 29, #06-40"), - getTagSet("friends"), sampleNotes, sampleEvents), + getTagSet("friends"), sampleNotes, sampleEvents1), new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), - getTagSet("colleagues", "friends"), sampleNotes, sampleEvents), + getTagSet("colleagues", "friends"), sampleNotes, sampleEvents2), new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), - getTagSet("neighbours"), sampleNotes, sampleEvents), + getTagSet("neighbours"), sampleNotes, new ArrayList()), new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), - getTagSet("family"), sampleNotes, sampleEvents), + getTagSet("family"), sampleNotes, new ArrayList()), new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), new Address("Blk 47 Tampines Street 20, #17-35"), - getTagSet("classmates"), sampleNotes, sampleEvents), + getTagSet("classmates"), sampleNotes, new ArrayList()), new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), new Address("Blk 45 Aljunied Street 85, #11-31"), - getTagSet("colleagues"), sampleNotes, sampleEvents), + getTagSet("colleagues"), sampleNotes, new ArrayList()), new Person(new Name("Professor XXX"), new Phone("11111111"), new Email("123@example.com"), new Address("Example Addres"), - getTagSet("colleagues"), sampleNotes, sampleEvents) + getTagSet("colleagues"), sampleNotes, new ArrayList()) }; } diff --git a/src/test/java/seedu/address/commons/util/DateTimeUtilTest.java b/src/test/java/seedu/address/commons/util/DateTimeUtilTest.java index f7a866f4556..1ab4f91b160 100644 --- a/src/test/java/seedu/address/commons/util/DateTimeUtilTest.java +++ b/src/test/java/seedu/address/commons/util/DateTimeUtilTest.java @@ -1,7 +1,9 @@ package seedu.address.commons.util; 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 java.time.LocalDateTime; import java.time.format.DateTimeParseException; @@ -10,7 +12,7 @@ public class DateTimeUtilTest { @Test - public void test_correctFormat_pass() { + public void test_parseStringCorrectFormat_pass() { assertEquals(DateTimeUtil.parseString("12:00"), LocalDateTime.now() .withHour(12).withMinute(0).withSecond(0).withNano(0)); assertEquals(DateTimeUtil.parseString("02:00"), LocalDateTime.now() @@ -34,10 +36,108 @@ public void test_correctFormat_pass() { } @Test - public void test_wrongFormat_fails() { + public void test_parseStringWrongFormat_fails() { assertThrows(DateTimeParseException.class, () -> DateTimeUtil.parseString("002:00")); assertThrows(DateTimeParseException.class, () -> DateTimeUtil.parseString("02:0")); assertThrows(DateTimeParseException.class, () -> DateTimeUtil.parseString("12:1")); assertThrows(DateTimeParseException.class, () -> DateTimeUtil.parseString("2023-10-12 0:0:0")); } + + @Test + public void test_withinInterval_pass() { + // start time == check time == end time + assertTrue(DateTimeUtil.withinTimeInterval( + LocalDateTime.parse("2023-11-02T20:10:00"), + LocalDateTime.parse("2023-11-02T20:10:00"), + LocalDateTime.parse("2023-11-02T20:10:00"))); + // start time < check time < end time + assertTrue(DateTimeUtil.withinTimeInterval( + LocalDateTime.parse("2023-11-01T20:10:00"), + LocalDateTime.parse("2023-11-03T20:10:00"), + LocalDateTime.parse("2023-11-02T20:10:00"))); + // start time <= check time < end time + assertTrue(DateTimeUtil.withinTimeInterval( + LocalDateTime.parse("2023-11-01T20:10:00"), + LocalDateTime.parse("2023-11-03T20:10:00"), + LocalDateTime.parse("2023-11-01T20:10:00"))); + // start time < check time <= end time + assertTrue(DateTimeUtil.withinTimeInterval( + LocalDateTime.parse("2023-11-01T20:10:00"), + LocalDateTime.parse("2023-11-03T20:10:00"), + LocalDateTime.parse("2023-11-03T20:10:00"))); + // check time < start time + assertFalse(DateTimeUtil.withinTimeInterval( + LocalDateTime.parse("2023-11-02T20:10:00"), + LocalDateTime.parse("2023-11-03T20:10:00"), + LocalDateTime.parse("2023-11-01T20:10:00"))); + // check time > end time + assertFalse(DateTimeUtil.withinTimeInterval( + LocalDateTime.parse("2023-11-01T20:10:00"), + LocalDateTime.parse("2023-11-03T20:10:00"), + LocalDateTime.parse("2023-11-05T20:10:00"))); + } + + @Test + public void test_timeIntervalsOverlap_pass() { + // interval A and B does not overlap + assertFalse(DateTimeUtil.timeIntervalsOverlap( + LocalDateTime.parse("2023-11-02T20:10:00"), + LocalDateTime.parse("2023-11-03T20:10:00"), + LocalDateTime.parse("2023-11-04T20:10:00"), + LocalDateTime.parse("2023-11-05T20:10:00"))); + // same intervals + assertTrue(DateTimeUtil.timeIntervalsOverlap( + LocalDateTime.parse("2023-11-02T20:10:00"), + LocalDateTime.parse("2023-11-05T20:10:00"), + LocalDateTime.parse("2023-11-02T20:10:00"), + LocalDateTime.parse("2023-11-05T20:10:00"))); + // start A == start B + assertTrue(DateTimeUtil.timeIntervalsOverlap( + LocalDateTime.parse("2023-11-02T20:10:00"), + LocalDateTime.parse("2023-11-03T20:10:00"), + LocalDateTime.parse("2023-11-02T20:10:00"), + LocalDateTime.parse("2023-11-04T20:10:00"))); + // end A == end B + assertTrue(DateTimeUtil.timeIntervalsOverlap( + LocalDateTime.parse("2023-11-01T20:10:00"), + LocalDateTime.parse("2023-11-05T20:10:00"), + LocalDateTime.parse("2023-11-02T20:10:00"), + LocalDateTime.parse("2023-11-05T20:10:00"))); + // start A within interval B + assertTrue(DateTimeUtil.timeIntervalsOverlap( + LocalDateTime.parse("2023-11-03T20:10:00"), + LocalDateTime.parse("2023-11-05T20:10:00"), + LocalDateTime.parse("2023-11-02T20:10:00"), + LocalDateTime.parse("2023-11-04T20:10:00"))); + // end A within interval B + assertTrue(DateTimeUtil.timeIntervalsOverlap( + LocalDateTime.parse("2023-11-01T20:10:00"), + LocalDateTime.parse("2023-11-03T20:10:00"), + LocalDateTime.parse("2023-11-02T20:10:00"), + LocalDateTime.parse("2023-11-04T20:10:00"))); + // start B within interval A + assertTrue(DateTimeUtil.timeIntervalsOverlap( + LocalDateTime.parse("2023-11-01T20:10:00"), + LocalDateTime.parse("2023-11-03T20:10:00"), + LocalDateTime.parse("2023-11-02T20:10:00"), + LocalDateTime.parse("2023-11-05T20:10:00"))); + // end B within interval A + assertTrue(DateTimeUtil.timeIntervalsOverlap( + LocalDateTime.parse("2023-11-04T20:10:00"), + LocalDateTime.parse("2023-11-06T20:10:00"), + LocalDateTime.parse("2023-11-02T20:10:00"), + LocalDateTime.parse("2023-11-05T20:10:00"))); + // interval A totally covers interval B + assertTrue(DateTimeUtil.timeIntervalsOverlap( + LocalDateTime.parse("2023-11-01T20:10:00"), + LocalDateTime.parse("2023-11-05T20:10:00"), + LocalDateTime.parse("2023-11-02T20:10:00"), + LocalDateTime.parse("2023-11-03T20:10:00"))); + // interval B totally covers interval A + assertTrue(DateTimeUtil.timeIntervalsOverlap( + LocalDateTime.parse("2023-11-02T20:10:00"), + LocalDateTime.parse("2023-11-03T20:10:00"), + LocalDateTime.parse("2023-11-01T20:10:00"), + LocalDateTime.parse("2023-11-05T20:10:00"))); + } } diff --git a/src/test/java/seedu/address/logic/commands/AddPersonCommandTest.java b/src/test/java/seedu/address/logic/commands/AddPersonCommandTest.java index 23b988479fe..9cf5de17190 100644 --- a/src/test/java/seedu/address/logic/commands/AddPersonCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/AddPersonCommandTest.java @@ -10,6 +10,8 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; +import java.util.List; import java.util.function.Predicate; import org.junit.jupiter.api.Test; @@ -22,6 +24,8 @@ import seedu.address.model.Model; import seedu.address.model.ReadOnlyAddressBook; import seedu.address.model.ReadOnlyUserPrefs; +import seedu.address.model.event.Event; +import seedu.address.model.event.EventID; import seedu.address.model.person.ContactID; import seedu.address.model.person.Person; import seedu.address.testutil.PersonBuilder; @@ -160,6 +164,22 @@ public void updateFilteredPersonList(Predicate predicate) { throw new AssertionError("This method should not be called."); } + @Override + public ObservableList getFilteredEventList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateFilteredEventList(Predicate predicate) { + throw new AssertionError("This method should not be called."); + } + + @Override + public List getSortedFilteredEventList(Comparator comparator) { + throw new AssertionError("This method should not be called."); + } + + @Override public Person findPersonByIndex(int index) { throw new AssertionError("This method should not be called."); @@ -169,6 +189,21 @@ public Person findPersonByIndex(int index) { public Person findPersonByUserFriendlyId(ContactID id) { throw new AssertionError("This method should not be called."); } + + @Override + public void addEvent(Event toAdd, Person owner) { + throw new AssertionError("This method should not be called."); + } + + @Override + public Event removeEventByID(EventID eventID, Person owner) { + throw new AssertionError("This method should not be called."); + } + + @Override + public String filteredEventListToString() { + return null; + } } /** diff --git a/src/test/java/seedu/address/logic/commands/DeleteEventCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteEventCommandTest.java index 020095f5a65..7bb31275897 100644 --- a/src/test/java/seedu/address/logic/commands/DeleteEventCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/DeleteEventCommandTest.java @@ -16,10 +16,13 @@ import seedu.address.model.person.ContactID; public class DeleteEventCommandTest { - private static final Event VALID_EVENT_0 = new Event("Have a meeting", "02:00", "04:00", - "COM1", "Discuss project"); - private static final Event VALID_EVENT_1 = new Event("Have a meeting again", "05:00", "07:00", - "COM1", "Discuss project again"); + private static final Event VALID_EVENT_0 = new Event("Career Fair", + "12:00", "14:00", + "COM1 Level2", "Go to booth #01-01 first"); + + private static final Event VALID_EVENT_1 = new Event("Career Fair (Second Day)", + "2023-11-02 12:00", "2023-11-02 14:00", + "COM1 Level2", "Go to booth #02-01 first"); private Model model; @@ -29,16 +32,16 @@ public void setUp() { } @Test - public void execute_correctCommand_success() throws CommandException { + public void execute_correctCommand_success() { ContactID contactId = ContactID.fromInt(1); EventID eventID = EventID.fromInt(1); - model.findPersonByUserFriendlyId(contactId).addEvent(VALID_EVENT_0); + model.addEvent(VALID_EVENT_0, model.findPersonByUserFriendlyId(contactId)); assertCommandSuccessWithFeedback(() -> new DeleteEventCommand(contactId, eventID) .execute(model), DeleteEventCommand.MESSAGE_SUCCESS + eventID + ". " + VALID_EVENT_0.getName()); } @Test - public void execute_contactNotFound_fails() throws CommandException { + public void execute_contactNotFound_fails() { ContactID contactId = ContactID.fromInt(999); EventID eventId = EventID.fromInt(1); assertCommandFailWithFeedback(() -> new DeleteEventCommand(contactId, eventId) @@ -46,21 +49,23 @@ public void execute_contactNotFound_fails() throws CommandException { } @Test - public void execute_eventNotFound_fails() throws CommandException { + public void execute_eventNotFound_fails() { ContactID contactId = ContactID.fromInt(1); EventID invalidEventId = EventID.fromInt(99999); - model.findPersonByUserFriendlyId(contactId).addEvent(VALID_EVENT_0); + model.addEvent(VALID_EVENT_1, model.findPersonByUserFriendlyId(contactId)); assertCommandFailWithFeedback(() -> new DeleteEventCommand(contactId, invalidEventId) .execute(model), DeleteEventCommand.MESSAGE_EVENT_NOT_FOUND + invalidEventId); } private void assertCommandSuccessWithFeedback(ThrowingSupplier function, String result) { + CommandResult actualResult = null; try { - assertEquals(function.get(), new CommandResult(result)); + actualResult = function.get(); } catch (Throwable e) { - throw new AssertionError("Execution of command should not fail.", e); + throw new AssertionError("Execution of command should not fail, but caught: " + e); } + assertEquals(new CommandResult(result), actualResult); } private void assertCommandFailWithFeedback(ThrowingSupplier function, String errResult) { @@ -68,7 +73,7 @@ private void assertCommandFailWithFeedback(ThrowingSupplier funct function.get(); } catch (Throwable e) { if (!(e instanceof CommandException)) { - throw new AssertionError("Execution of command failed but not due to CommandException."); + throw new AssertionError("Execution of command failed but not due to CommandException. " + e); } assertEquals(e.getMessage(), errResult); return; diff --git a/src/test/java/seedu/address/logic/commands/ListEventCommandTest.java b/src/test/java/seedu/address/logic/commands/ListEventCommandTest.java index c9666ea38c7..0d364a6299f 100644 --- a/src/test/java/seedu/address/logic/commands/ListEventCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/ListEventCommandTest.java @@ -10,14 +10,15 @@ import seedu.address.model.ModelManager; import seedu.address.model.UserPrefs; import seedu.address.model.event.Event; +import seedu.address.model.event.EventTime; import seedu.address.model.person.ContactID; public class ListEventCommandTest { private static final Event VALID_EVENT_0 = new Event("Have a meeting", "02:00", "04:00", "COM1", "Discuss project"); - private static final Event VALID_EVENT_1 = new Event("Midterm Exam", "02:00", "04:00", + private static final Event VALID_EVENT_1 = new Event("Midterm Exam", "04:01", "06:00", "MPSH1", "Seat number is xxx."); - private static final Event VALID_EVENT_2 = new Event("Another Midterm Exam", "04:00", "06:00", + private static final Event VALID_EVENT_2 = new Event("Another Midterm Exam", "06:01", "08:00", "MPSH2", "Seat number is xxx."); private Model model; @@ -28,11 +29,21 @@ public void setUp() { } @Test - public void execute_correctCommand_success() { + public void execute_correctCommandFullList_success() { model.findPersonByUserFriendlyId(ContactID.fromInt(1)).addEvent(VALID_EVENT_0); model.findPersonByUserFriendlyId(ContactID.fromInt(2)).addEvent(VALID_EVENT_1); model.findPersonByUserFriendlyId(ContactID.fromInt(2)).addEvent(VALID_EVENT_2); - assertCommandSuccess(() -> new ListEventCommand().execute(model)); + assertCommandSuccess(() -> new ListEventCommand(EventTime.fromString(""), + EventTime.fromString(""), true).execute(model)); + } + + @Test + public void execute_correctCommandFiltered_success() { + model.addEvent(VALID_EVENT_0, model.findPersonByUserFriendlyId(ContactID.fromInt(1))); + model.addEvent(VALID_EVENT_1, model.findPersonByUserFriendlyId(ContactID.fromInt(2))); + model.addEvent(VALID_EVENT_2, model.findPersonByUserFriendlyId(ContactID.fromInt(2))); + assertCommandSuccess(() -> new ListEventCommand(EventTime.fromString("03:00"), + EventTime.fromString("05:00"), true).execute(model)); } private void assertCommandSuccess(ThrowingSupplier function) { diff --git a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddEventCommandParserTest.java similarity index 87% rename from src/test/java/seedu/address/logic/parser/AddCommandParserTest.java rename to src/test/java/seedu/address/logic/parser/AddEventCommandParserTest.java index 077bd4696ea..54289b27d4e 100644 --- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/AddEventCommandParserTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.Messages.MESSAGE_START_TIME_AFTER_END_TIME; import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND; import org.junit.jupiter.api.Test; @@ -16,7 +17,7 @@ import seedu.address.model.event.EventName; import seedu.address.model.event.EventTime; -public class AddCommandParserTest { +public class AddEventCommandParserTest { private AddCommandParser parser = new AddCommandParser(); @@ -59,6 +60,14 @@ public void execute_emptyStringArguments_fails() { EventInformation.MESSAGE_CONSTRAINTS); } + @Test + public void execute_startTimeAfterEndTime_fails() { + assertParseFailedWithError(() -> parser.parse(" " + + AddEventCommand.SECONDARY_COMMAND_WORD + + " -id 1 -en 2 -st 2023-11-02 12:00 -et 2023-11-01 23:00"), + String.format(MESSAGE_START_TIME_AFTER_END_TIME, "2023-11-02 12:00:00", "2023-11-01 23:00:00")); + } + private void assertParseSuccessWithCommand(ThrowingSupplier function, String commandClassName) { try { Command command = function.get(); diff --git a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteEventCommandParserTest.java similarity index 98% rename from src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java rename to src/test/java/seedu/address/logic/parser/DeleteEventCommandParserTest.java index 3a74a31e33d..d7cdeb107c9 100644 --- a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/DeleteEventCommandParserTest.java @@ -12,7 +12,7 @@ import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; -public class DeleteCommandParserTest { +public class DeleteEventCommandParserTest { private DeleteCommandParser parser = new DeleteCommandParser(); diff --git a/src/test/java/seedu/address/logic/parser/ListCommandParserTest.java b/src/test/java/seedu/address/logic/parser/ListCommandParserTest.java index c3091465360..dc2a1c4866c 100644 --- a/src/test/java/seedu/address/logic/parser/ListCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/ListCommandParserTest.java @@ -8,7 +8,6 @@ import seedu.address.logic.commands.Command; import seedu.address.logic.commands.ListEventCommand; -import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; public class ListCommandParserTest { @@ -17,13 +16,13 @@ public class ListCommandParserTest { @Test - public void execute_correctCommand_success() throws CommandException { + public void execute_correctCommand_success() { assertParseSuccessWithCommand(() -> parser.parse(" " + ListEventCommand.SECONDARY_COMMAND_WORD), ListEventCommand.class.getName()); } @Test - public void execute_commandNotFound_fails() throws CommandException { + public void execute_commandNotFound_fails() { assertParseFailedWithError(() -> parser.parse(" " + " unknown_command 1 2 3"), MESSAGE_UNKNOWN_COMMAND); } diff --git a/src/test/java/seedu/address/logic/parser/ListEventCommandParserTest.java b/src/test/java/seedu/address/logic/parser/ListEventCommandParserTest.java new file mode 100644 index 00000000000..ddbcb766fab --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/ListEventCommandParserTest.java @@ -0,0 +1,95 @@ +package seedu.address.logic.parser; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.Messages.MESSAGE_START_TIME_AFTER_END_TIME; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.ThrowingSupplier; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.ListEventCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.event.EventTime; + +public class ListEventCommandParserTest { + + private ListEventCommandParser parser = new ListEventCommandParser(); + + + @Test + public void execute_correctCommandNoFilter_success() { + assertParseSuccessWithCommand(() -> parser.parse(" "), ListEventCommand.class.getName()); + } + + @Test + public void execute_correctCommandNoFilterDescending_success() { + assertParseSuccessWithCommand(() -> parser.parse(" -descending"), ListEventCommand.class.getName()); + } + + @Test + public void execute_correctCommandUseFilter_success() { + assertParseSuccessWithCommand(() -> parser.parse(" -st 2023-11-02 -et 2023-11-03"), + ListEventCommand.class.getName()); + } + + @Test + public void execute_correctCommandUseFilterDescending_success() { + assertParseSuccessWithCommand(() -> parser.parse(" -st 2023-11-02 -et 2023-11-03 -descending"), + ListEventCommand.class.getName()); + } + + @Test + public void execute_wrongCommandUseFilterFail_startTimeAfterEndTime() { + assertParseFailedWithError(() -> parser.parse(" -st 2023-11-03 -et 2023-11-02"), + String.format(MESSAGE_START_TIME_AFTER_END_TIME, "2023-11-03 00:00:00", "2023-11-02 00:00:00")); + } + + @Test + public void execute_wrongCommandUseFilterFail_onlyHasStartTime() { + assertParseFailedWithError(() -> parser.parse(" -st 2023-11-03"), + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListEventCommand.MESSAGE_USAGE)); + } + + @Test + public void execute_wrongCommandUseFilterFail_onlyHasEndTime() { + assertParseFailedWithError(() -> parser.parse(" -et 2023-11-03"), + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListEventCommand.MESSAGE_USAGE)); + } + + @Test + public void execute_wrongCommandUseFilterFail_emptyArguments() { + assertParseFailedWithError(() -> parser.parse(" -st -et"), + EventTime.MESSAGE_NON_EMPTY); + } + + @Test + public void execute_wrongCommandUseFilterDescendingFail_extraContentAfterDescendingPrefix() { + assertParseFailedWithError(() -> parser.parse(" -descending xxx -st 2023-11-02 -et 2023-11-03"), + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListEventCommand.MESSAGE_USAGE)); + } + + + + private void assertParseSuccessWithCommand(ThrowingSupplier function, String commandClassName) { + try { + Command command = function.get(); + assertEquals(command.getClass().getName(), commandClassName); + } catch (Throwable e) { + throw new AssertionError("Execution of command should not fail.", e); + } + } + + private void assertParseFailedWithError(ThrowingSupplier function, String errResult) { + try { + function.get(); + } catch (Throwable e) { + if (!(e instanceof ParseException)) { + throw new AssertionError("Execution of parser failed but not due to ParseException."); + } + assertEquals(e.getMessage(), errResult); + return; + } + throw new AssertionError("Execution of command should fail."); + } +} diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/seedu/address/model/AddressBookTest.java index 86f320b458d..3f6de20cd60 100644 --- a/src/test/java/seedu/address/model/AddressBookTest.java +++ b/src/test/java/seedu/address/model/AddressBookTest.java @@ -18,6 +18,7 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import seedu.address.model.event.Event; import seedu.address.model.person.Person; import seedu.address.model.person.exceptions.DuplicatePersonException; import seedu.address.testutil.PersonBuilder; @@ -94,6 +95,7 @@ public void toStringMethod() { */ private static class AddressBookStub implements ReadOnlyAddressBook { private final ObservableList persons = FXCollections.observableArrayList(); + private final ObservableList events = FXCollections.observableArrayList(); AddressBookStub(Collection persons) { this.persons.setAll(persons); @@ -104,6 +106,11 @@ public ObservableList getPersonList() { return persons; } + @Override + public ObservableList getEventList() { + return events; + } + @Override public String eventListToString() { return ""; diff --git a/src/test/java/seedu/address/testutil/TypicalPersons.java b/src/test/java/seedu/address/testutil/TypicalPersons.java index fec76fb7129..b30733524cc 100644 --- a/src/test/java/seedu/address/testutil/TypicalPersons.java +++ b/src/test/java/seedu/address/testutil/TypicalPersons.java @@ -14,6 +14,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import seedu.address.model.AddressBook; import seedu.address.model.person.Person; @@ -65,11 +66,24 @@ private TypicalPersons() {} // prevents instantiation public static AddressBook getTypicalAddressBook() { AddressBook ab = new AddressBook(); for (Person person : getTypicalPersons()) { - ab.addPerson(person); + ab.addPerson(copyTypicalPerson(person)); } return ab; } + // Use this function to make sure that for every test cases, we use complete new Person instances + // This can prevent unintentionally sharing and reusing of the typical person instances between different test cases + private static Person copyTypicalPerson(Person typicalPerson) { + List tagList = typicalPerson.getTags().stream().map(tag -> tag.tagName).collect(Collectors.toList()); + return new PersonBuilder() + .withName(typicalPerson.getName().toString()) + .withEmail(typicalPerson.getEmail().toString()) + .withAddress(typicalPerson.getAddress().toString()) + .withPhone(typicalPerson.getPhone().toString()) + .withTags(tagList.toArray(new String[tagList.size()])) + .build(); + } + public static List getTypicalPersons() { return new ArrayList<>(Arrays.asList(ALICE, BENSON, CARL, DANIEL, ELLE, FIONA, GEORGE)); } From 65cffe8ec8db2180b58d80e18826f5ff31cc4eeb Mon Sep 17 00:00:00 2001 From: Wang Zihan Date: Thu, 2 Nov 2023 00:43:42 +0800 Subject: [PATCH 2/3] Add more test cases --- .../logic/commands/ListEventCommandTest.java | 9 +++++ .../seedu/address/model/event/EventTest.java | 20 +++++++++++ .../address/model/event/EventTimeTest.java | 35 +++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 src/test/java/seedu/address/model/event/EventTest.java create mode 100644 src/test/java/seedu/address/model/event/EventTimeTest.java diff --git a/src/test/java/seedu/address/logic/commands/ListEventCommandTest.java b/src/test/java/seedu/address/logic/commands/ListEventCommandTest.java index 0d364a6299f..961bb95bf3f 100644 --- a/src/test/java/seedu/address/logic/commands/ListEventCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/ListEventCommandTest.java @@ -46,6 +46,15 @@ public void execute_correctCommandFiltered_success() { EventTime.fromString("05:00"), true).execute(model)); } + @Test + public void execute_correctCommandFilteredDescending_success() { + model.addEvent(VALID_EVENT_0, model.findPersonByUserFriendlyId(ContactID.fromInt(1))); + model.addEvent(VALID_EVENT_1, model.findPersonByUserFriendlyId(ContactID.fromInt(2))); + model.addEvent(VALID_EVENT_2, model.findPersonByUserFriendlyId(ContactID.fromInt(2))); + assertCommandSuccess(() -> new ListEventCommand(EventTime.fromString("03:00"), + EventTime.fromString("05:00"), false).execute(model)); + } + private void assertCommandSuccess(ThrowingSupplier function) { try { function.get(); diff --git a/src/test/java/seedu/address/model/event/EventTest.java b/src/test/java/seedu/address/model/event/EventTest.java new file mode 100644 index 00000000000..742d6b05422 --- /dev/null +++ b/src/test/java/seedu/address/model/event/EventTest.java @@ -0,0 +1,20 @@ +package seedu.address.model.event; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +import org.junit.jupiter.api.Test; + +public class EventTest { + @Test + public void test_eventEquals_pass() { + assertEquals(new Event("Sample", "00:01", "00:02", "Location", "Information"), + new Event("Sample", "00:01", "00:02", "Location", "Information")); + assertNotEquals(new Event("Sample", "00:01", "00:02", "Location", "Information"), + new Event("Sample", "00:02", "00:03", "Location", "Information")); + } + +} diff --git a/src/test/java/seedu/address/model/event/EventTimeTest.java b/src/test/java/seedu/address/model/event/EventTimeTest.java new file mode 100644 index 00000000000..7c127fcf686 --- /dev/null +++ b/src/test/java/seedu/address/model/event/EventTimeTest.java @@ -0,0 +1,35 @@ +package seedu.address.model.event; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +import org.junit.jupiter.api.Test; + +public class EventTimeTest { + @Test + public void test_eventTimeEquals_pass() { + assertEquals(EventTime.fromString("2023-11-02 00:30:00"), + EventTime.fromString("2023-11-02 00:30:00")); + assertEquals(EventTime.fromString("00:30:00"), + EventTime.fromString( + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + " 00:30:00")); + assertNotEquals(EventTime.fromString("2023-11-02 10:00:00"), + EventTime.fromString("2023-11-02 10:00:01")); + assertNotEquals(EventTime.fromString("10:00:00"), + EventTime.fromString( + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + " 10:00:01")); + } + + @Test + public void test_getTime_pass() { + assertEquals(EventTime.fromString("2023-11-02 00:30:00").getTime(), + LocalDateTime.of(2023, 11, 2, 0, 30, 0)); + assertEquals(EventTime.fromString("00:30:00").getTime(), + LocalDateTime.now().withHour(0).withMinute(30).withSecond(0).withNano(0)); + } + + +} From 80c07e5860b23fbc2f677f62a0b8c0ca08c3c5a2 Mon Sep 17 00:00:00 2001 From: Wang Zihan Date: Thu, 2 Nov 2023 00:45:42 +0800 Subject: [PATCH 3/3] Fix checkstyle issue --- src/test/java/seedu/address/model/event/EventTest.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/test/java/seedu/address/model/event/EventTest.java b/src/test/java/seedu/address/model/event/EventTest.java index 742d6b05422..0e735ac356a 100644 --- a/src/test/java/seedu/address/model/event/EventTest.java +++ b/src/test/java/seedu/address/model/event/EventTest.java @@ -3,17 +3,16 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; - import org.junit.jupiter.api.Test; public class EventTest { @Test public void test_eventEquals_pass() { - assertEquals(new Event("Sample", "00:01", "00:02", "Location", "Information"), + assertEquals( + new Event("Sample", "00:01", "00:02", "Location", "Information"), new Event("Sample", "00:01", "00:02", "Location", "Information")); - assertNotEquals(new Event("Sample", "00:01", "00:02", "Location", "Information"), + assertNotEquals( + new Event("Sample", "00:01", "00:02", "Location", "Information"), new Event("Sample", "00:02", "00:03", "Location", "Information")); }