From 30d84fd5cefe5be13126971b6dd6f3769617331d Mon Sep 17 00:00:00 2001 From: Wang Zihan Date: Wed, 25 Oct 2023 19:38:50 +0800 Subject: [PATCH 1/9] Fix the bug that accepts empty string as command argument in event commands - Fix the bug that accepts empty string as command argument in event commands. So now the program will notify the user if their command has empty argumnts - For example: - `add event -id 1 -en -st 12:00` - `add event -id 1 -en Sample -st 12:00 -loc` - Will result in a user-friend error message - Corrected UG about `add contact` command (no optional arguments) --- docs/UserGuide.md | 5 +- .../java/seedu/address/logic/Messages.java | 1 - .../logic/commands/AddEventCommand.java | 9 +- .../logic/commands/AddNoteCommand.java | 3 +- .../logic/commands/DeleteEventCommand.java | 15 ++- .../logic/commands/DeleteNoteCommand.java | 3 +- .../logic/parser/AddEventCommandParser.java | 36 +++-- .../parser/DeleteEventCommandParser.java | 13 +- .../address/logic/parser/ParserUtil.java | 123 ++++++++++++++++++ src/main/java/seedu/address/model/Model.java | 3 +- .../seedu/address/model/ModelManager.java | 5 +- .../java/seedu/address/model/event/Event.java | 30 ++++- .../seedu/address/model/event/EventID.java | 47 +++++++ .../address/model/event/EventInformation.java | 12 ++ .../address/model/event/EventLocation.java | 12 ++ .../seedu/address/model/event/EventName.java | 44 +++++++ .../seedu/address/model/event/EventTime.java | 3 + .../seedu/address/model/person/ContactID.java | 45 +++++++ .../seedu/address/model/person/Person.java | 15 ++- .../address/model/util/SampleDataUtil.java | 8 +- .../logic/commands/AddEventCommandTest.java | 11 +- .../logic/commands/AddPersonCommandTest.java | 3 +- .../commands/DeleteEventCommandTest.java | 28 ++-- .../logic/commands/DeleteNoteCommandTest.java | 13 +- .../logic/commands/ListEventCommandTest.java | 7 +- 25 files changed, 400 insertions(+), 94 deletions(-) create mode 100644 src/main/java/seedu/address/model/event/EventID.java create mode 100644 src/main/java/seedu/address/model/event/EventName.java create mode 100644 src/main/java/seedu/address/model/person/ContactID.java diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3f53a690115..318d0da9ae5 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -91,7 +91,7 @@ Format: `help` Adds a person to the contact list. -Format: `add contact -n NAME [-p PHONE_NUMBER] [-a ADDRESS] [-e EMAIL]` +Format: `add contact -n NAME -p PHONE_NUMBER -a ADDRESS -e EMAIL` @@ -99,7 +99,6 @@ Format: `add contact -n NAME [-p PHONE_NUMBER] [-a ADDRESS] [-e EMAIL]` Examples: * `add contact -n Aaron -p 12345678 -a Baker Street 12 -e aaron123@gmail.com` -* `add contact -n Daniel -p 87654321 -e daniel0109@gmail.com` ### Listing all persons : `list contact` @@ -219,7 +218,7 @@ _No known issues at the moment_ Action | Format, Examples -------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------- -**Add Contact** | `add contact -n NAME [-p PHONE_NUMBER] [-a ADDRESS] [-e EMAIL]`
e.g., `add contact -n Aaron -p 12345678 -a Baker Street 12 -e aaron123@gmail.com` +**Add Contact** | `add contact -n NAME -p PHONE_NUMBER -a ADDRESS -e EMAIL`
e.g., `add contact -n Aaron -p 12345678 -a Baker Street 12 -e aaron123@gmail.com` **Delete Contact** | `delete contact NAME`
e.g., `delete contact Aaron` **List Contact** | `list contact` **Add Note** | `add note -n NAME -t NOTE_TITLE -c NOTE_CONTENT`
e.g., `add note -n Daniel -t Open Position -e Applications for SWE full-time positions will open soon` diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java index af5a9ce5106..6a38ade6d61 100644 --- a/src/main/java/seedu/address/logic/Messages.java +++ b/src/main/java/seedu/address/logic/Messages.java @@ -32,7 +32,6 @@ 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_DATETIME_FORMAT = "Invalid date time format! \n%1$s"; 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 34eae6aa8ff..c4c3ddd0dca 100644 --- a/src/main/java/seedu/address/logic/commands/AddEventCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddEventCommand.java @@ -5,6 +5,7 @@ import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; import seedu.address.model.event.Event; +import seedu.address.model.person.ContactID; import seedu.address.model.person.Person; /** @@ -14,7 +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_PERSON_NOT_FOUND = "Can not find the target contact with ID: "; + 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 + ": Adds an event to a contact.\n" @@ -22,12 +23,12 @@ public class AddEventCommand extends AddCommand { + "START_TIME [-et END_TIME] [-loc LOCATION] [-i INFORMATION]"; private final Event toAdd; - private final int contactId; + private final ContactID contactId; /** * Creates an AddEventCommand to add the specified {@code Event} */ - public AddEventCommand(int contactId, Event event) { + public AddEventCommand(ContactID contactId, Event event) { requireNonNull(event); this.contactId = contactId; this.toAdd = event; @@ -38,7 +39,7 @@ public CommandResult execute(Model model) throws CommandException { requireNonNull(model); Person person = model.findPersonByUserFriendlyId(this.contactId); if (person == null) { - throw new CommandException(MESSAGE_PERSON_NOT_FOUND + this.contactId); + throw new CommandException(MESSAGE_CONTACT_NOT_FOUND + this.contactId.getId()); } person.addEvent(this.toAdd); diff --git a/src/main/java/seedu/address/logic/commands/AddNoteCommand.java b/src/main/java/seedu/address/logic/commands/AddNoteCommand.java index 9f78791a71f..6633802f4ec 100644 --- a/src/main/java/seedu/address/logic/commands/AddNoteCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddNoteCommand.java @@ -6,6 +6,7 @@ import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; import seedu.address.model.note.Note; +import seedu.address.model.person.ContactID; import seedu.address.model.person.Person; /** @@ -35,7 +36,7 @@ public AddNoteCommand(int contactId, Note note) { @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - Person person = model.findPersonByUserFriendlyId(this.contactId); + Person person = model.findPersonByUserFriendlyId(ContactID.fromInt(this.contactId)); if (person == null) { throw new CommandException(MESSAGE_PERSON_NOT_FOUND + this.contactId); } diff --git a/src/main/java/seedu/address/logic/commands/DeleteEventCommand.java b/src/main/java/seedu/address/logic/commands/DeleteEventCommand.java index 936b40fcb18..163c2f46af3 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteEventCommand.java +++ b/src/main/java/seedu/address/logic/commands/DeleteEventCommand.java @@ -4,6 +4,9 @@ import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; +import seedu.address.model.event.Event; +import seedu.address.model.event.EventID; +import seedu.address.model.person.ContactID; import seedu.address.model.person.Person; /** @@ -19,12 +22,12 @@ public class DeleteEventCommand extends DeleteCommand { public static final String MESSAGE_SUCCESS = "Successfully deleted event: "; public static final String MESSAGE_EVENT_NOT_FOUND = "Event not found: ID = "; - private final int eventIdToDelete; - private final int contactId; + private final EventID eventIdToDelete; + private final ContactID contactId; /** * Creates an DeleteEventCommand to delete the specified {@code Event} */ - public DeleteEventCommand(int contactId, int eventIdToDelete) { + public DeleteEventCommand(ContactID contactId, EventID eventIdToDelete) { this.contactId = contactId; this.eventIdToDelete = eventIdToDelete; } @@ -36,11 +39,11 @@ public CommandResult execute(Model model) throws CommandException { if (person == null) { throw new CommandException(MESSAGE_PERSON_NOT_FOUND + this.contactId); } - boolean success = person.removeEventByUserFriendlyId(this.eventIdToDelete); - if (!success) { + Event deletedEvent = person.removeEventByUserFriendlyId(this.eventIdToDelete); + if (deletedEvent == null) { throw new CommandException(MESSAGE_EVENT_NOT_FOUND + this.eventIdToDelete); } - return new CommandResult(MESSAGE_SUCCESS + this.eventIdToDelete); + return new CommandResult(MESSAGE_SUCCESS + this.eventIdToDelete + ". " + deletedEvent.getName()); } } diff --git a/src/main/java/seedu/address/logic/commands/DeleteNoteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteNoteCommand.java index 09a5a69d0f6..e826692b48d 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteNoteCommand.java +++ b/src/main/java/seedu/address/logic/commands/DeleteNoteCommand.java @@ -5,6 +5,7 @@ import seedu.address.commons.util.ToStringBuilder; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; +import seedu.address.model.person.ContactID; import seedu.address.model.person.Person; /** @@ -32,7 +33,7 @@ public DeleteNoteCommand(int contactId, int noteIdToDelete) { @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - Person person = model.findPersonByUserFriendlyId(this.contactId); + Person person = model.findPersonByUserFriendlyId(ContactID.fromInt(this.contactId)); if (person == null) { throw new CommandException(MESSAGE_PERSON_NOT_FOUND + this.contactId); } diff --git a/src/main/java/seedu/address/logic/parser/AddEventCommandParser.java b/src/main/java/seedu/address/logic/parser/AddEventCommandParser.java index 637d0412e07..4a7fa12ece7 100644 --- a/src/main/java/seedu/address/logic/parser/AddEventCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddEventCommandParser.java @@ -1,8 +1,6 @@ package seedu.address.logic.parser; import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.Messages.MESSAGE_INVALID_DATETIME_FORMAT; -import static seedu.address.logic.Messages.MESSAGE_INVALID_INTEGER_ARGUMENT; import static seedu.address.logic.parser.CliSyntax.PREFIX_EVENT_END_TIME; import static seedu.address.logic.parser.CliSyntax.PREFIX_EVENT_INFORMATION; import static seedu.address.logic.parser.CliSyntax.PREFIX_EVENT_LOCATION; @@ -10,11 +8,14 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_EVENT_START_TIME; import static seedu.address.logic.parser.CliSyntax.PREFIX_PERSON_ID; -import java.time.format.DateTimeParseException; - import seedu.address.logic.commands.AddEventCommand; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.event.Event; +import seedu.address.model.event.EventInformation; +import seedu.address.model.event.EventLocation; +import seedu.address.model.event.EventName; +import seedu.address.model.event.EventTime; +import seedu.address.model.person.ContactID; /** * Parses input arguments and creates a new AddEventCommand object @@ -37,23 +38,16 @@ public AddEventCommand parse(String args) throws ParseException { PREFIX_EVENT_NAME, PREFIX_EVENT_START_TIME, PREFIX_EVENT_END_TIME, PREFIX_EVENT_LOCATION, PREFIX_EVENT_INFORMATION); - int contactId = -1; - String eventName = argMultimap.getValue(PREFIX_EVENT_NAME).get(); - String startTime = argMultimap.getValue(PREFIX_EVENT_START_TIME).get(); - String endTime = argMultimap.getValue(PREFIX_EVENT_END_TIME).orElseGet(()->""); - String location = argMultimap.getValue(PREFIX_EVENT_LOCATION).orElseGet(()->""); - String information = argMultimap.getValue(PREFIX_EVENT_INFORMATION).orElseGet(()->""); - Event newEvent = null; - try { - newEvent = new Event(eventName, startTime, endTime, location, information); - } catch (DateTimeParseException e) { - throw new ParseException(String.format(MESSAGE_INVALID_DATETIME_FORMAT, e.getMessage())); - } - try { - contactId = Integer.parseInt(argMultimap.getValue(PREFIX_PERSON_ID).get()); - } catch (NumberFormatException e) { - throw new ParseException(String.format(MESSAGE_INVALID_INTEGER_ARGUMENT, e.getMessage())); - } + 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)); + 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()); + Event newEvent = new Event(eventName, startTime, endTime, location, information); + return new AddEventCommand(contactId, newEvent); } } diff --git a/src/main/java/seedu/address/logic/parser/DeleteEventCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteEventCommandParser.java index 3f722f37267..101f649a919 100644 --- a/src/main/java/seedu/address/logic/parser/DeleteEventCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/DeleteEventCommandParser.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.Messages.MESSAGE_INVALID_INTEGER_ARGUMENT; import static seedu.address.logic.parser.CliSyntax.PREFIX_EVENT_ID; import static seedu.address.logic.parser.CliSyntax.PREFIX_PERSON_ID; import seedu.address.logic.commands.DeleteEventCommand; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.event.EventID; +import seedu.address.model.person.ContactID; /** * Parses input arguments and creates a new DeleteEventCommand object @@ -22,14 +23,8 @@ public DeleteEventCommand parse(String args) throws ParseException { } argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_PERSON_ID, PREFIX_EVENT_ID); - int contactId = -1; - int eventId = -1; - try { - contactId = Integer.parseInt(argMultimap.getValue(PREFIX_PERSON_ID).get()); - eventId = Integer.parseInt(argMultimap.getValue(PREFIX_EVENT_ID).get()); - } catch (NumberFormatException e) { - throw new ParseException(String.format(MESSAGE_INVALID_INTEGER_ARGUMENT, e.getMessage())); - } + ContactID contactId = ParserUtil.parseContactID(argMultimap.getValue(PREFIX_PERSON_ID).get()); + EventID eventId = ParserUtil.parseEventID(argMultimap.getValue(PREFIX_EVENT_ID).get()); return new DeleteEventCommand(contactId, eventId); } diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java index 290c662f712..36a6c303130 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java @@ -1,7 +1,9 @@ package seedu.address.logic.parser; import static java.util.Objects.requireNonNull; +import static seedu.address.logic.Messages.MESSAGE_INVALID_INTEGER_ARGUMENT; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -12,7 +14,13 @@ import seedu.address.commons.core.index.Index; import seedu.address.commons.util.StringUtil; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.event.EventID; +import seedu.address.model.event.EventInformation; +import seedu.address.model.event.EventLocation; +import seedu.address.model.event.EventName; +import seedu.address.model.event.EventTime; import seedu.address.model.person.Address; +import seedu.address.model.person.ContactID; import seedu.address.model.person.Email; import seedu.address.model.person.Name; import seedu.address.model.person.Phone; @@ -125,6 +133,121 @@ public static Set parseTags(Collection tags) throws ParseException return tagSet; } + /** + * Parses a {@code String eventName} into a {@code EventName}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code eventName} is invalid. + */ + public static ContactID parseContactID(String contactID) throws ParseException { + requireNonNull(contactID); + String trimmedContactID = contactID.trim(); + if (trimmedContactID.isEmpty()) { + throw new ParseException(ContactID.MESSAGE_NON_EMPTY); + } + ContactID result = null; + try { + result = ContactID.fromString(trimmedContactID); + } catch (NumberFormatException e) { + throw new ParseException(String.format(MESSAGE_INVALID_INTEGER_ARGUMENT, e.getMessage())); + } + return result; + } + + + /** + * Parses a {@code String eventName} into a {@code EventName}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code eventName} is invalid. + */ + public static EventName parseEventName(String eventName) throws ParseException { + requireNonNull(eventName); + String trimmedEventName = eventName.trim(); + if (!EventName.isValidEventName(trimmedEventName)) { + throw new ParseException(EventName.MESSAGE_CONSTRAINTS); + } + return EventName.fromString(trimmedEventName); + } + + /** + * Parses a {@code String eventTime} into a {@code EventTime} with the specified date-time format. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code eventTime} is invalid. + */ + public static EventTime parseEventTime(String eventTime) throws ParseException { + String trimmedEventTime = ""; + if (eventTime != null) { + trimmedEventTime = eventTime.trim(); + if (trimmedEventTime.isEmpty()) { + throw new ParseException(EventTime.MESSAGE_NON_EMPTY); + } + } + EventTime result = null; + try { + result = EventTime.fromString(trimmedEventTime); + } catch (DateTimeParseException e) { + throw new ParseException(EventTime.MESSAGE_INVALID_DATETIME_FORMAT + e.getMessage()); + } + return result; + } + + /** + * Parses a {@code String eventLocation} into a {@code EventLocation}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code eventLocation} is invalid. + */ + public static EventLocation parseEventLocation(String eventLocation) throws ParseException { + if (eventLocation == null) { + return EventLocation.fromString(""); + } + String trimmedEventLocation = eventLocation.trim(); + if (!EventLocation.isValidEventLocation(trimmedEventLocation)) { + throw new ParseException(EventLocation.MESSAGE_CONSTRAINTS); + } + return EventLocation.fromString(trimmedEventLocation); + } + + /** + * Parses a {@code String eventInformation} into a {@code EventInformation}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code eventInformation} is invalid. + */ + public static EventInformation parseEventInformation(String eventInformation) throws ParseException { + if (eventInformation == null) { + return EventInformation.fromString(""); + } + String trimmedEventInformation = eventInformation.trim(); + if (!EventInformation.isValidEventInformation(trimmedEventInformation)) { + throw new ParseException(EventInformation.MESSAGE_CONSTRAINTS); + } + return EventInformation.fromString(trimmedEventInformation); + } + + /** + * Parses a {@code String eventID} into a {@code EventID}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code eventID} is invalid. + */ + public static EventID parseEventID(String eventID) throws ParseException { + requireNonNull(eventID); + String trimmedEventID = eventID.trim(); + if (trimmedEventID.isEmpty()) { + throw new ParseException(EventID.MESSAGE_NON_EMPTY); + } + EventID result = null; + try { + result = EventID.fromString(trimmedEventID); + } catch (NumberFormatException e) { + throw new ParseException(String.format(MESSAGE_INVALID_INTEGER_ARGUMENT, e.getMessage())); + } + return result; + } + /** * Returns true if none of the prefixes contains empty {@code Optional} values * in the given diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index 510e98b7a67..c95a414a0f2 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -5,6 +5,7 @@ import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; +import seedu.address.model.person.ContactID; import seedu.address.model.person.Person; /** @@ -95,5 +96,5 @@ public interface Model { * Find a person by the user-friendly ID displayed in the person card on the UI. * If the ID is invalid, returns null. */ - Person findPersonByUserFriendlyId(int id); + Person findPersonByUserFriendlyId(ContactID id); } diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index b828c7f6967..b365f200412 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -12,6 +12,7 @@ import javafx.collections.transformation.FilteredList; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; +import seedu.address.model.person.ContactID; import seedu.address.model.person.Person; /** @@ -139,8 +140,8 @@ public Person findPersonByIndex(int index) { } @Override - public Person findPersonByUserFriendlyId(int id) { - return this.findPersonByIndex(id - 1); + public Person findPersonByUserFriendlyId(ContactID id) { + return this.findPersonByIndex(id.getId() - 1); } @Override diff --git a/src/main/java/seedu/address/model/event/Event.java b/src/main/java/seedu/address/model/event/Event.java index 88397d9fb25..721ebd03f93 100644 --- a/src/main/java/seedu/address/model/event/Event.java +++ b/src/main/java/seedu/address/model/event/Event.java @@ -6,7 +6,7 @@ public class Event { private final EventTime start; private final EventTime end; - private final String name; + private final EventName name; private final EventLocation location; @@ -17,15 +17,31 @@ public class Event { * @param name The event name * @param start The start time of the event * @param end The end time of the event - * @param locationStr The location of the event - * @param informationStr The information of the event + * @param location The location of the event + * @param information The information of the event */ - public Event(String name, String start, String end, String locationStr, String informationStr) { + public Event(EventName name, EventTime start, EventTime end, EventLocation location, EventInformation information) { this.name = name; + this.start = start; + this.end = end; + this.location = location; + this.information = information; + } + + /** + * Constructor for the `Event` class, with Strings as parameters + * @param name The event name + * @param start The start time of the event + * @param end The end time of the event + * @param location The location of the event + * @param information The information of the event + */ + public Event(String name, String start, String end, String location, String information) { + this.name = EventName.fromString(name); this.start = EventTime.fromString(start); this.end = EventTime.fromString(end); - this.location = EventLocation.fromString(locationStr); - this.information = EventInformation.fromString(informationStr); + this.location = EventLocation.fromString(location); + this.information = EventInformation.fromString(information); } /** @@ -33,7 +49,7 @@ public Event(String name, String start, String end, String locationStr, String i * @return The name of the event */ public String getName() { - return name; + return name.toString(); } /** diff --git a/src/main/java/seedu/address/model/event/EventID.java b/src/main/java/seedu/address/model/event/EventID.java new file mode 100644 index 00000000000..3197abe3afb --- /dev/null +++ b/src/main/java/seedu/address/model/event/EventID.java @@ -0,0 +1,47 @@ +package seedu.address.model.event; + +/** + * The class for holding the event id for DeleteEventCommand + */ +public class EventID { + public static final String MESSAGE_NON_EMPTY = "Event ID can not be empty!"; + + private final int id; + private EventID(String id) { + this.id = Integer.parseInt(id); + } + private EventID(int id) { + this.id = id; + } + + /** + * Get the numerical id + * @return The numerical id + */ + public int getId() { + return this.id; + } + + /** + * Construct an {@code EventID} object from {@code String} + * @param id The id in {@code String} + * @return The {@code EventID} object + */ + public static EventID fromString(String id) { + return new EventID(id); + } + + /** + * Construct an {@code EventID} object from {@code int} + * @param id The id in {@code int} + * @return The {@code EventID} object + */ + public static EventID fromInt(int id) { + return new EventID(id); + } + + @Override + public String toString() { + return String.valueOf(this.id); + } +} diff --git a/src/main/java/seedu/address/model/event/EventInformation.java b/src/main/java/seedu/address/model/event/EventInformation.java index 29cab58c982..970d63c6ee1 100644 --- a/src/main/java/seedu/address/model/event/EventInformation.java +++ b/src/main/java/seedu/address/model/event/EventInformation.java @@ -4,6 +4,9 @@ * The class for holding the "information" part for an Event */ public class EventInformation { + + public static final String MESSAGE_CONSTRAINTS = "Event information can not be empty."; + private final String information; private EventInformation() { @@ -30,4 +33,13 @@ public static EventInformation fromString(String information) { public String toString() { return this.information; } + + /** + * Returns whether the given information in string is valid event information + * @param str The information in string + * @return True if the name is valid event information, false otherwise + */ + public static boolean isValidEventInformation(String str) { + return !str.isEmpty(); + } } diff --git a/src/main/java/seedu/address/model/event/EventLocation.java b/src/main/java/seedu/address/model/event/EventLocation.java index a0db0a246e0..b3989776a68 100644 --- a/src/main/java/seedu/address/model/event/EventLocation.java +++ b/src/main/java/seedu/address/model/event/EventLocation.java @@ -4,6 +4,9 @@ * The class for holding the "location" part for an Event */ public class EventLocation { + + public static final String MESSAGE_CONSTRAINTS = "Event location can not be empty."; + private final String location; private EventLocation() { @@ -33,4 +36,13 @@ public static EventLocation fromString(String location) { public String toString() { return this.location; } + + /** + * Returns whether the given location string in string is a valid event location + * @param str The location in string + * @return True if the location string is a valid event location, false otherwise + */ + public static boolean isValidEventLocation(String str) { + return !str.isEmpty(); + } } diff --git a/src/main/java/seedu/address/model/event/EventName.java b/src/main/java/seedu/address/model/event/EventName.java new file mode 100644 index 00000000000..6fbbdad0d68 --- /dev/null +++ b/src/main/java/seedu/address/model/event/EventName.java @@ -0,0 +1,44 @@ +package seedu.address.model.event; + +/** + * The class for holding the "name" part for an Event + */ +public class EventName { + + public static final String MESSAGE_CONSTRAINTS = "Event name can not be empty."; + + private final String name; + + private EventName(String name) { + this.name = name; + } + + /** + * Construct an {@code EventName} object from {@code String} + * + * @param name The name in String + * @return The {@code EventName} object + */ + public static EventName fromString(String name) { + return new EventName(name); + } + + /** + * Get the String representation of this {@code EventName} object + * + * @return The String representation of this {@code EventName} object + */ + @Override + public String toString() { + return this.name; + } + + /** + * Returns whether the given name in string is a valid event name + * @param str The name in string + * @return True if the name is a valid event name, false otherwise + */ + public static boolean isValidEventName(String str) { + return !str.isEmpty(); + } +} diff --git a/src/main/java/seedu/address/model/event/EventTime.java b/src/main/java/seedu/address/model/event/EventTime.java index 5f7a70cbe6e..a7d77b91efc 100644 --- a/src/main/java/seedu/address/model/event/EventTime.java +++ b/src/main/java/seedu/address/model/event/EventTime.java @@ -9,6 +9,9 @@ * The class for holding the "start" and "end" part for an Event */ 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!"; + private final LocalDateTime time; private EventTime() { diff --git a/src/main/java/seedu/address/model/person/ContactID.java b/src/main/java/seedu/address/model/person/ContactID.java new file mode 100644 index 00000000000..1cb0f25f987 --- /dev/null +++ b/src/main/java/seedu/address/model/person/ContactID.java @@ -0,0 +1,45 @@ +package seedu.address.model.person; + +/** + * The class for holding the event id for various commands + */ +public class ContactID { + public static final String MESSAGE_NON_EMPTY = "Contact ID can not be empty!"; + + private final int id; + + private ContactID(String id) { + this.id = Integer.parseInt(id); + } + + private ContactID(int id) { + this.id = id; + } + + public int getId() { + return this.id; + } + + /** + * Construct an {@code ContactID} object from {@code String} + * @param id The id in {@code String} + * @return The {@code ContactID} object + */ + public static ContactID fromString(String id) { + return new ContactID(id); + } + + /** + * Construct an {@code ContactID} object from {@code int} + * @param id The id in {@code int} + * @return The {@code ContactID} object + */ + public static ContactID fromInt(int id) { + return new ContactID(id); + } + + @Override + public String toString() { + return String.valueOf(this.id); + } +} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java index 3f56f0a21d2..ccfe72440a4 100644 --- a/src/main/java/seedu/address/model/person/Person.java +++ b/src/main/java/seedu/address/model/person/Person.java @@ -12,6 +12,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.note.Note; import seedu.address.model.tag.Tag; @@ -127,18 +128,18 @@ public void addEvent(Event event) { /** * Remove an event by its user-friendly id * @param id The id of the event you want to remove - * @return {@code true} if the operation is successful and {@code false} if the event with this name does not exist + * @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 */ - public boolean removeEventByUserFriendlyId(int id) { - return this.removeEventByIndex(id - 1); + public Event removeEventByUserFriendlyId(EventID id) { + return this.removeEventByIndex(id.getId() - 1); } - private boolean removeEventByIndex(int index) { + private Event removeEventByIndex(int index) { if (index < 0 || index >= this.events.size()) { - return false; + return null; } - this.events.remove(index); - return true; + 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 92d1e04fd14..3695246538f 100644 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java @@ -28,10 +28,10 @@ public static Person[] getSamplePersons() { sampleNotes.add(new Note("Hello", "Sample content")); ArrayList sampleEvents = new ArrayList(); - sampleEvents.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", + sampleEvents.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")); return new Person[] { diff --git a/src/test/java/seedu/address/logic/commands/AddEventCommandTest.java b/src/test/java/seedu/address/logic/commands/AddEventCommandTest.java index bd41f0f60e0..8b344083814 100644 --- a/src/test/java/seedu/address/logic/commands/AddEventCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/AddEventCommandTest.java @@ -12,6 +12,7 @@ import seedu.address.model.ModelManager; import seedu.address.model.UserPrefs; import seedu.address.model.event.Event; +import seedu.address.model.person.ContactID; public class AddEventCommandTest { @@ -29,16 +30,16 @@ public void setUp() { @Test public void execute_correctCommand_success() throws CommandException { - int personId = 1; - assertCommandSuccessWithFeedback(() -> new AddEventCommand(personId, VALID_EVENT_0) + ContactID contactID = ContactID.fromInt(1); + assertCommandSuccessWithFeedback(() -> new AddEventCommand(contactID, VALID_EVENT_0) .execute(model), AddEventCommand.MESSAGE_SUCCESS + VALID_EVENT_0.getName()); } @Test public void execute_personNotExist_fails() throws CommandException { - int personId = 99999; - assertCommandFailWithFeedback(() -> new AddEventCommand(personId, VALID_EVENT_SAME_NAME_0) - .execute(model), AddEventCommand.MESSAGE_PERSON_NOT_FOUND + personId); + ContactID contactID = ContactID.fromInt(99999); + assertCommandFailWithFeedback(() -> new AddEventCommand(contactID, VALID_EVENT_SAME_NAME_0) + .execute(model), AddEventCommand.MESSAGE_CONTACT_NOT_FOUND + contactID); } diff --git a/src/test/java/seedu/address/logic/commands/AddPersonCommandTest.java b/src/test/java/seedu/address/logic/commands/AddPersonCommandTest.java index 8c82bfd496b..23b988479fe 100644 --- a/src/test/java/seedu/address/logic/commands/AddPersonCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/AddPersonCommandTest.java @@ -22,6 +22,7 @@ import seedu.address.model.Model; import seedu.address.model.ReadOnlyAddressBook; import seedu.address.model.ReadOnlyUserPrefs; +import seedu.address.model.person.ContactID; import seedu.address.model.person.Person; import seedu.address.testutil.PersonBuilder; @@ -165,7 +166,7 @@ public Person findPersonByIndex(int index) { } @Override - public Person findPersonByUserFriendlyId(int id) { + public Person findPersonByUserFriendlyId(ContactID id) { throw new AssertionError("This method should not be called."); } } diff --git a/src/test/java/seedu/address/logic/commands/DeleteEventCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteEventCommandTest.java index 709f282ab26..020095f5a65 100644 --- a/src/test/java/seedu/address/logic/commands/DeleteEventCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/DeleteEventCommandTest.java @@ -12,6 +12,8 @@ import seedu.address.model.ModelManager; import seedu.address.model.UserPrefs; import seedu.address.model.event.Event; +import seedu.address.model.event.EventID; +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", @@ -28,25 +30,27 @@ public void setUp() { @Test public void execute_correctCommand_success() throws CommandException { - int personId = 1; - model.findPersonByUserFriendlyId(personId).addEvent(VALID_EVENT_0); - assertCommandSuccessWithFeedback(() -> new DeleteEventCommand(personId, 1) - .execute(model), DeleteEventCommand.MESSAGE_SUCCESS + "1"); + ContactID contactId = ContactID.fromInt(1); + EventID eventID = EventID.fromInt(1); + model.findPersonByUserFriendlyId(contactId).addEvent(VALID_EVENT_0); + assertCommandSuccessWithFeedback(() -> new DeleteEventCommand(contactId, eventID) + .execute(model), DeleteEventCommand.MESSAGE_SUCCESS + eventID + ". " + VALID_EVENT_0.getName()); } @Test - public void execute_personNotFound_fails() throws CommandException { - int personId = 999; - assertCommandFailWithFeedback(() -> new DeleteEventCommand(personId, 1) - .execute(model), DeleteEventCommand.MESSAGE_PERSON_NOT_FOUND + personId); + public void execute_contactNotFound_fails() throws CommandException { + ContactID contactId = ContactID.fromInt(999); + EventID eventId = EventID.fromInt(1); + assertCommandFailWithFeedback(() -> new DeleteEventCommand(contactId, eventId) + .execute(model), DeleteEventCommand.MESSAGE_PERSON_NOT_FOUND + contactId.getId()); } @Test public void execute_eventNotFound_fails() throws CommandException { - int personId = 1; - int invalidEventId = 99999; - model.findPersonByUserFriendlyId(personId).addEvent(VALID_EVENT_0); - assertCommandFailWithFeedback(() -> new DeleteEventCommand(personId, invalidEventId) + ContactID contactId = ContactID.fromInt(1); + EventID invalidEventId = EventID.fromInt(99999); + model.findPersonByUserFriendlyId(contactId).addEvent(VALID_EVENT_0); + assertCommandFailWithFeedback(() -> new DeleteEventCommand(contactId, invalidEventId) .execute(model), DeleteEventCommand.MESSAGE_EVENT_NOT_FOUND + invalidEventId); } diff --git a/src/test/java/seedu/address/logic/commands/DeleteNoteCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteNoteCommandTest.java index eac3628bd4b..17f0560079e 100644 --- a/src/test/java/seedu/address/logic/commands/DeleteNoteCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/DeleteNoteCommandTest.java @@ -18,6 +18,7 @@ import seedu.address.model.ModelManager; import seedu.address.model.UserPrefs; import seedu.address.model.note.Note; +import seedu.address.model.person.ContactID; public class DeleteNoteCommandTest { private static final Note VALID_NOTE_0 = new Note("Meeting Topics", @@ -32,9 +33,9 @@ public void setUp() { @Test public void execute_correctCommand_success() throws CommandException { - int personId = 1; - model.findPersonByUserFriendlyId(personId).addNote(VALID_NOTE_0); - assertCommandSuccessWithFeedback(() -> new DeleteNoteCommand(personId, 1) + ContactID contactId = ContactID.fromInt(1); + model.findPersonByUserFriendlyId(contactId).addNote(VALID_NOTE_0); + assertCommandSuccessWithFeedback(() -> new DeleteNoteCommand(contactId.getId(), 1) .execute(model), DeleteNoteCommand.MESSAGE_SUCCESS + "1"); } @@ -47,10 +48,10 @@ public void execute_personNotFound_fails() throws CommandException { @Test public void execute_noteNotFound_fails() throws CommandException { - int personId = 1; + ContactID contactId = ContactID.fromInt(1); int invalidNoteId = 99999; - model.findPersonByUserFriendlyId(personId).addNote(VALID_NOTE_0); - assertCommandFailWithFeedback(() -> new DeleteNoteCommand(personId, invalidNoteId) + model.findPersonByUserFriendlyId(contactId).addNote(VALID_NOTE_0); + assertCommandFailWithFeedback(() -> new DeleteNoteCommand(contactId.getId(), invalidNoteId) .execute(model), DeleteNoteCommand.MESSAGE_NOTE_NOT_FOUND + invalidNoteId); } diff --git a/src/test/java/seedu/address/logic/commands/ListEventCommandTest.java b/src/test/java/seedu/address/logic/commands/ListEventCommandTest.java index 74da5f5c0e5..c9666ea38c7 100644 --- a/src/test/java/seedu/address/logic/commands/ListEventCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/ListEventCommandTest.java @@ -10,6 +10,7 @@ import seedu.address.model.ModelManager; import seedu.address.model.UserPrefs; import seedu.address.model.event.Event; +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", @@ -28,9 +29,9 @@ public void setUp() { @Test public void execute_correctCommand_success() { - model.findPersonByUserFriendlyId(1).addEvent(VALID_EVENT_0); - model.findPersonByUserFriendlyId(2).addEvent(VALID_EVENT_1); - model.findPersonByUserFriendlyId(2).addEvent(VALID_EVENT_2); + 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)); } From 6c9ca92034ff8ef7edb70df87169685b88d15f9f Mon Sep 17 00:00:00 2001 From: Wang Zihan Date: Wed, 25 Oct 2023 19:55:07 +0800 Subject: [PATCH 2/9] Add contents about parsing logic in DG and corrected a small mistake in a notification string --- docs/DeveloperGuide.md | 5 +++++ .../seedu/address/logic/commands/DeleteEventCommand.java | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 5cb72f42fb4..faa592a10a3 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -113,6 +113,11 @@ Here are the other classes in `Logic` (omitted from the class diagram above) tha How the parsing works: * When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object. * All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing. +* Some commands contains secondary command, like `add contact`, `add note` and `add event`. +* - In this case, the primary command parser (in the example it is `AddCommand`) will check the secondary command word and use the correspond secondary command parser (like `AddPersonCommandParser`, `AddEventCommandParser` and `AddNoteCommandParser`) to continue parsing the command. +* The parser will turn the arguments in the command from raw `String` into corresponding Object. During this process, the parser also needs to check whether the arguments are valid or not. +* - The parsing method for each types of arguments are mainly in `ParserUtil.java` +* If the command is correct in format, the parser will then return a Command Object for the execution of the command. ### Model component **API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) diff --git a/src/main/java/seedu/address/logic/commands/DeleteEventCommand.java b/src/main/java/seedu/address/logic/commands/DeleteEventCommand.java index 163c2f46af3..ad4efeb5561 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteEventCommand.java +++ b/src/main/java/seedu/address/logic/commands/DeleteEventCommand.java @@ -17,7 +17,7 @@ public class DeleteEventCommand extends DeleteCommand { public static final String SECONDARY_COMMAND_WORD = "event"; public static final String MESSAGE_USAGE = COMMAND_WORD + " " + SECONDARY_COMMAND_WORD + ": Deletes an event from a contact.\n" - + "Usage: delete event -n CONTACT_NAME -en EVENT_NAME"; + + "Usage: delete event -id CONTACT_ID -eid EVENT_ID"; public static final String MESSAGE_PERSON_NOT_FOUND = "Can not find the target contact with ID: "; public static final String MESSAGE_SUCCESS = "Successfully deleted event: "; public static final String MESSAGE_EVENT_NOT_FOUND = "Event not found: ID = "; From 1bb41990e5b23875abf9a9be4b26a13e07f1b03c Mon Sep 17 00:00:00 2001 From: Wang Zihan Date: Wed, 25 Oct 2023 20:14:51 +0800 Subject: [PATCH 3/9] Add JUnit Test Cases for empty string arguments in event commands --- .../logic/commands/AddEventCommandTest.java | 4 ++-- .../logic/parser/AddCommandParserTest.java | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/test/java/seedu/address/logic/commands/AddEventCommandTest.java b/src/test/java/seedu/address/logic/commands/AddEventCommandTest.java index 8b344083814..81b34c207db 100644 --- a/src/test/java/seedu/address/logic/commands/AddEventCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/AddEventCommandTest.java @@ -29,14 +29,14 @@ public void setUp() { } @Test - public void execute_correctCommand_success() throws CommandException { + public void execute_correctCommand_success() { ContactID contactID = ContactID.fromInt(1); assertCommandSuccessWithFeedback(() -> new AddEventCommand(contactID, VALID_EVENT_0) .execute(model), AddEventCommand.MESSAGE_SUCCESS + VALID_EVENT_0.getName()); } @Test - public void execute_personNotExist_fails() throws CommandException { + public void execute_personNotExist_fails() { ContactID contactID = ContactID.fromInt(99999); assertCommandFailWithFeedback(() -> new AddEventCommand(contactID, VALID_EVENT_SAME_NAME_0) .execute(model), AddEventCommand.MESSAGE_CONTACT_NOT_FOUND + contactID); diff --git a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java index 6a33b4c5628..077bd4696ea 100644 --- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java @@ -11,6 +11,10 @@ import seedu.address.logic.commands.Command; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.event.EventInformation; +import seedu.address.model.event.EventLocation; +import seedu.address.model.event.EventName; +import seedu.address.model.event.EventTime; public class AddCommandParserTest { @@ -36,6 +40,25 @@ public void execute_commandFormatError_fails() throws CommandException { String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddEventCommand.MESSAGE_USAGE)); } + @Test + public void execute_emptyStringArguments_fails() { + assertParseFailedWithError(() -> parser.parse(" " + + AddEventCommand.SECONDARY_COMMAND_WORD + " -id 1 -en -st 12:00"), + EventName.MESSAGE_CONSTRAINTS); + assertParseFailedWithError(() -> parser.parse(" " + + AddEventCommand.SECONDARY_COMMAND_WORD + " -id 1 -en Sample -st"), + EventTime.MESSAGE_NON_EMPTY); + assertParseFailedWithError(() -> parser.parse(" " + + AddEventCommand.SECONDARY_COMMAND_WORD + " -id 1 -en Sample -st 12:00 -et"), + EventTime.MESSAGE_NON_EMPTY); + assertParseFailedWithError(() -> parser.parse(" " + + AddEventCommand.SECONDARY_COMMAND_WORD + " -id 1 -en Sample -st 12:00 -loc"), + EventLocation.MESSAGE_CONSTRAINTS); + assertParseFailedWithError(() -> parser.parse(" " + + AddEventCommand.SECONDARY_COMMAND_WORD + " -id 1 -en Sample -st 12:00 -info"), + EventInformation.MESSAGE_CONSTRAINTS); + } + private void assertParseSuccessWithCommand(ThrowingSupplier function, String commandClassName) { try { Command command = function.get(); From 6754f47bfae8072486a0eb7c94e48fcf5e7e80c1 Mon Sep 17 00:00:00 2001 From: Wang Zihan Date: Wed, 25 Oct 2023 20:41:15 +0800 Subject: [PATCH 4/9] Add test cases for `ParserUtil` --- .../address/logic/parser/ParserUtilTest.java | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java index 4256788b1a7..a32f7d001f9 100644 --- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java +++ b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java @@ -6,6 +6,8 @@ import static seedu.address.testutil.Assert.assertThrows; import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -13,8 +15,15 @@ import org.junit.jupiter.api.Test; +import seedu.address.logic.Messages; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.event.EventID; +import seedu.address.model.event.EventInformation; +import seedu.address.model.event.EventLocation; +import seedu.address.model.event.EventName; +import seedu.address.model.event.EventTime; import seedu.address.model.person.Address; +import seedu.address.model.person.ContactID; import seedu.address.model.person.Email; import seedu.address.model.person.Name; import seedu.address.model.person.Phone; @@ -193,4 +202,104 @@ public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception { assertEquals(expectedTagSet, actualTagSet); } + + @Test + public void parseContactID_success() throws Exception { + assertEquals("1", ParserUtil.parseContactID("1").toString()); + } + + @Test + public void parseContactID_fail_emptyString() { + assertThrows(ParseException.class, ContactID.MESSAGE_NON_EMPTY, () -> ParserUtil.parseContactID("").toString()); + } + + @Test + public void parseContactID_fail_invalidInteger() { + assertThrows(ParseException.class, String.format(Messages.MESSAGE_INVALID_INTEGER_ARGUMENT, + "For input string: \"abc\""), () -> ParserUtil.parseContactID("abc").toString()); + assertThrows(ParseException.class, String.format(Messages.MESSAGE_INVALID_INTEGER_ARGUMENT, + "For input string: \"1.23\""), () -> ParserUtil.parseContactID("1.23").toString()); + } + + @Test + public void parseEventID_success() throws Exception { + assertEquals("1", ParserUtil.parseEventID("1").toString()); + } + + @Test + public void parseEventID_fail_emptyString() { + assertThrows(ParseException.class, EventID.MESSAGE_NON_EMPTY, () -> ParserUtil.parseEventID("").toString()); + } + + @Test + public void parseEventID_fail_invalidInteger() { + assertThrows(ParseException.class, String.format(Messages.MESSAGE_INVALID_INTEGER_ARGUMENT, + "For input string: \"abc\""), () -> ParserUtil.parseEventID("abc").toString()); + assertThrows(ParseException.class, String.format(Messages.MESSAGE_INVALID_INTEGER_ARGUMENT, + "For input string: \"1.23\""), () -> ParserUtil.parseEventID("1.23").toString()); + } + + @Test + public void parseEventName_success() throws Exception { + assertEquals("SomeName", ParserUtil.parseEventName("SomeName").toString()); + } + + @Test + public void parseEventName_fail_emptyString() throws Exception { + assertThrows(ParseException.class, EventName.MESSAGE_CONSTRAINTS, () -> + ParserUtil.parseEventName("").toString()); + } + + @Test + public void parseEventTime_success() throws Exception { + assertEquals(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + " 12:00:00", + ParserUtil.parseEventTime("12:00").toString()); + assertEquals("2023-12-01 00:00:00", + ParserUtil.parseEventTime("2023-12-01").toString()); + assertEquals("2023-12-01 10:02:03", + ParserUtil.parseEventTime("2023-12-01 10:02:03").toString()); + assertEquals("", + ParserUtil.parseEventTime(null).toString()); + } + + @Test + public void parseEventTime_fail_emptyString() { + assertThrows(ParseException.class, EventTime.MESSAGE_NON_EMPTY, () -> ParserUtil.parseEventTime("")); + } + + @Test + public void parseEventTime_fail_wrongFormat() { + assertThrows(ParseException.class, EventTime.MESSAGE_NON_EMPTY, () -> ParserUtil.parseEventTime("")); + String dateNow = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + assertThrows(ParseException.class, + EventTime.MESSAGE_INVALID_DATETIME_FORMAT + "Text '" + dateNow + + " 1' could not be parsed at index 11", () -> + ParserUtil.parseEventTime("1")); + assertThrows(ParseException.class, + EventTime.MESSAGE_INVALID_DATETIME_FORMAT + "Text '" + dateNow + + " 1,2,3,4' could not be parsed at index 11", () -> + ParserUtil.parseEventTime("1,2,3,4")); + } + + @Test + public void parseEventLocation_success() throws Exception { + assertEquals("SomeLocation", ParserUtil.parseEventLocation("SomeLocation").toString()); + } + + @Test + public void parseEventLocation_fail_emptyString() throws Exception { + assertThrows(ParseException.class, EventLocation.MESSAGE_CONSTRAINTS, () -> + ParserUtil.parseEventLocation("").toString()); + } + + @Test + public void parseEventInformation_success() throws Exception { + assertEquals("SomeInformation", ParserUtil.parseEventInformation("SomeInformation").toString()); + } + + @Test + public void parseEventInformation_fail_emptyString() throws Exception { + assertThrows(ParseException.class, EventInformation.MESSAGE_CONSTRAINTS, () -> + ParserUtil.parseEventInformation("").toString()); + } } From 898f2bc63f49dbc03b020678a331b9aab6d0fd91 Mon Sep 17 00:00:00 2001 From: zekone Date: Thu, 26 Oct 2023 15:14:35 +0800 Subject: [PATCH 5/9] DG: Add user story and use case for add tag feature --- docs/DeveloperGuide.md | 54 +++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 5cb72f42fb4..9332672569d 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -4,7 +4,7 @@ pageNav: 3 --- -# AB-3 Developer Guide +# KeepInTouch Developer Guide @@ -294,6 +294,7 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli | `* *` | user | delete notes to a contact | remove additional information that are no longer needed about that contact in the notes | | `* *` | user who has some event to do | add an event | record an event with start time and also end time, location and any additional information like what to do during the event | | `* *` | user who has/had some event to do | delete an event | remove an event after it is obsolete, cancelled or no longer needed to be recorded | +| `* *` | tidy user | tag a contact with a label | keep my contacts oraganised and categorised | | `* * *` | user who finishes using the application | exit the program | exit the program normally while ensuring all my data is currectly saved | *{More to be added}* @@ -342,8 +343,7 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli * 1a. User inputs a contact that does not exist. - * 1a1. KeepInTouch shows a message indicating the non-existent contact. - + * 1a1. KeepInTouch shows a message indicating that the contact cannot be found. Use case ends. * 2a. The contact list is empty. @@ -386,12 +386,12 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli Use case ends. -**Use case: UC06 - Add notes to a contact** +**Use case: UC06 - Add a note to a contact** **MSS** -1. User requests to add notes to a contact. -2. KeepInTouch adds the notes to the contact. +1. User requests to add a note to a contact. +2. KeepInTouch adds the note to the contact. Use case ends. @@ -405,16 +405,16 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli * 1b. User inputs a contact that does not exist. - * 1b1. KeepInTouch shows a message indicating the non-existent contact. + * 1b1. KeepInTouch shows a message indicating that the contact cannot be found. Use case resumes at step 1. -**Use case: UC07 - Delete notes from a contact** +**Use case: UC07 - Delete a note from a contact** **MSS** -1. User requests to delete notes from a contact. -2. KeepInTouch deletes the notes from the contact. +1. User requests to delete a note from a contact. +2. KeepInTouch deletes the note from the contact. Use case ends. @@ -428,14 +428,13 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli * 1b. User inputs a contact that does not exist. - * 1b1. KeepInTouch shows a message indicating the non-existent contact. + * 1b1. KeepInTouch shows a message indicating that the contact cannot be found. Use case ends. -* 1c. User inputs notes that does not exist. - - * 1c1. KeepInTouch shows a message indicating the non-existent notes. +* 1c. User inputs a note that does not exist. + * 1c1. KeepInTouch shows a message indicating that the note cannnot be found Use case ends. **Use case: UC08 - Add an event** @@ -474,11 +473,34 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli * 1b. User inputs an event that does not exist. - * 1b1. KeepInTouch shows a message indicating the non-existent event. + * 1b1. KeepInTouch shows a message indicating that the event cannot be found. Use case ends. -**Use case: UC10 - Exit the program** +**Use case: UC10 - Adding tags to a contact** + +**MSS** + +1. User requests add tags to a contact. +2. KeepInTouch appends that tags to the specified contact. + + Use case ends. + +**Extensions** + +* 1a. User inputs a non-alphanumeric tag. + + * 1a1. KeepInTouch shows a message indicating that tags should be alphanumeric. + + Use case resumes at step 1. + +* 1b. User inputs a contact that does not exist + + * 1b1. KeepInTouch shows a message indicating the contact cannot be found. + + Use case resumes at step 1. + +**Use case: UC11 - Exit the program** **MSS** From e08c7abf9151c34f9afbce5b178a0235cc47b701 Mon Sep 17 00:00:00 2001 From: josepholim Date: Thu, 26 Oct 2023 22:50:57 +0800 Subject: [PATCH 6/9] Add DG for Delete Tags --- docs/DeveloperGuide.md | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 9de3472dc51..df55cf16b7c 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -439,7 +439,8 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli * 1c. User inputs a note that does not exist. - * 1c1. KeepInTouch shows a message indicating that the note cannnot be found + * 1c1. KeepInTouch shows a message indicating that the note cannot be found. + Use case ends. **Use case: UC08 - Add an event** @@ -486,7 +487,7 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli **MSS** -1. User requests add tags to a contact. +1. User requests to add tags to a contact. 2. KeepInTouch appends that tags to the specified contact. Use case ends. @@ -499,13 +500,42 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli Use case resumes at step 1. -* 1b. User inputs a contact that does not exist +* 1b. User inputs a contact that does not exist. * 1b1. KeepInTouch shows a message indicating the contact cannot be found. Use case resumes at step 1. -**Use case: UC11 - Exit the program** +**Use case: UC11 - Delete tags from a contact** + +**MSS** + +1. User requests to delete tags from a contact. +2. KeepInTouch deletes the tags from the specified contact. + + Use case ends. + +**Extensions** + +* 1a. User inputs incomplete data. + + * 1a1. KeepInTouch shows a message indicating incomplete data. + + Use case ends. + +* 1b. User inputs a contact that does not exist. + + * 1b1. KeepInTouch shows a message indicating that the contact cannot be found. + + Use case ends. + +* 1c. User inputs a tags that does not exist. + + * 1c1. KeepInTouch shows a message indicating that the tags cannot be found. + + Use case ends. + +**Use case: UC12 - Exit the program** **MSS** From 0df9146e57715dd0eb644fba20f13a4e311a6406 Mon Sep 17 00:00:00 2001 From: josepholim Date: Thu, 26 Oct 2023 22:55:00 +0800 Subject: [PATCH 7/9] Add User Stories for Delete Tags --- docs/DeveloperGuide.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index df55cf16b7c..751536aa571 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -299,8 +299,9 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli | `* *` | user | delete notes to a contact | remove additional information that are no longer needed about that contact in the notes | | `* *` | user who has some event to do | add an event | record an event with start time and also end time, location and any additional information like what to do during the event | | `* *` | user who has/had some event to do | delete an event | remove an event after it is obsolete, cancelled or no longer needed to be recorded | -| `* *` | tidy user | tag a contact with a label | keep my contacts oraganised and categorised | -| `* * *` | user who finishes using the application | exit the program | exit the program normally while ensuring all my data is currectly saved | +| `* *` | tidy user | tag a contact with a label | keep my contacts oraganised and categorised | +| `* *` | tidy user | delete a tag from a contact | remove tags that are no longer relevant | +| `* * *` | user who finishes using the application | exit the program | exit the program normally while ensuring all my data is currectly saved | *{More to be added}* From 690c273881a64c09ac35702f068deb2776f288fe Mon Sep 17 00:00:00 2001 From: Andrew Janong Date: Fri, 27 Oct 2023 01:43:42 +0800 Subject: [PATCH 8/9] Add use case for edit tag in DG --- docs/DeveloperGuide.md | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 751536aa571..7abf7f5c6ae 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -530,13 +530,42 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli Use case ends. -* 1c. User inputs a tags that does not exist. +* 1c. User inputs a tag that does not exist. * 1c1. KeepInTouch shows a message indicating that the tags cannot be found. Use case ends. -**Use case: UC12 - Exit the program** +**Use case: UC12 - Edit tags in a contact** + +**MSS** + +1. User requests to edit tags in a contact. +2. KeepInTouch edits the tags from the specified contact. + + Use case ends. + +**Extensions** + +* 1a. User inputs incomplete data. + + * 1a1. KeepInTouch shows a message indicating incomplete data. + + Use case ends. + +* 1b. User inputs a contact that does not exist. + + * 1b1. KeepInTouch shows a message indicating that the contact cannot be found. + + Use case ends. + +* 1c. User inputs a tag that does not exist. + + * 1c1. KeepInTouch shows a message indicating that the tags cannot be found. + + Use case ends. + +**Use case: UC13 - Exit the program** **MSS** From a33e41bc0f7cf527fe1f3d4c4a57132d8e0e31a9 Mon Sep 17 00:00:00 2001 From: Andrew Janong Date: Fri, 27 Oct 2023 01:46:38 +0800 Subject: [PATCH 9/9] Add user stories for edit tags --- docs/DeveloperGuide.md | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 7abf7f5c6ae..ccffbc1dd49 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -288,20 +288,21 @@ _{Explain here how the data archiving feature will be implemented}_ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` -| Priority | As a …​ | I want to …​ | So that I can…​ | -|----------|--------------------------------------------|------------------------------|------------------------------------------------------------------------| -| `* * *` | new user | get a list of the commands | know how to use the commands and their parameters | -| `* * *` | user | add a new contact | record one person's phone number and email address | -| `* * *` | user | delete a contact | remove a contact (by name) that I do not need | -| `* * *` | user | view all contact | easily see and know what contacts are currently stored in the application in one place | -| `* *` | user | view all notes | easily see and know what notes are currently stored in the application in one place | -| `* *` | user | add notes to a contact | record additional information about that contact in the notes | -| `* *` | user | delete notes to a contact | remove additional information that are no longer needed about that contact in the notes | -| `* *` | user who has some event to do | add an event | record an event with start time and also end time, location and any additional information like what to do during the event | -| `* *` | user who has/had some event to do | delete an event | remove an event after it is obsolete, cancelled or no longer needed to be recorded | -| `* *` | tidy user | tag a contact with a label | keep my contacts oraganised and categorised | -| `* *` | tidy user | delete a tag from a contact | remove tags that are no longer relevant | -| `* * *` | user who finishes using the application | exit the program | exit the program normally while ensuring all my data is currectly saved | +| Priority | As a …​ | I want to …​ | So that I can…​ | +|----------|--------------------------------------------|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------| +| `* * *` | new user | get a list of the commands | know how to use the commands and their parameters | +| `* * *` | user | add a new contact | record one person's phone number and email address | +| `* * *` | user | delete a contact | remove a contact (by name) that I do not need | +| `* * *` | user | view all contact | easily see and know what contacts are currently stored in the application in one place | +| `* *` | user | view all notes | easily see and know what notes are currently stored in the application in one place | +| `* *` | user | add notes to a contact | record additional information about that contact in the notes | +| `* *` | user | delete notes to a contact | remove additional information that are no longer needed about that contact in the notes | +| `* *` | user who has some event to do | add an event | record an event with start time and also end time, location and any additional information like what to do during the event | +| `* *` | user who has/had some event to do | delete an event | remove an event after it is obsolete, cancelled or no longer needed to be recorded | +| `* *` | tidy user | tag a contact with a label | keep my contacts oraganised and categorised | +| `* *` | tidy user | delete a tag from a contact | remove tags that are no longer relevant | +| `* *` | tidy user | edit a tag from a contact | edit tags in a contact that needs to be changed | +| `* * *` | user who finishes using the application | exit the program | exit the program normally while ensuring all my data is currectly saved | *{More to be added}*