From 4c9de339074af301fac29224f68837825320b2fb Mon Sep 17 00:00:00 2001 From: ZiHawkEye <37863247+ZiHawkEye@users.noreply.github.com> Date: Wed, 23 Oct 2019 02:32:14 +0800 Subject: [PATCH] Added Event, hourly tracking (#88) * Added ability to mark attendance * Added stub tests for MarkAttendanceCommand and MarkAttendanceCommandParser * Added ability to add multiple pending commands * Added ability to mark attendance of all students in a tutorial * Added ability for user to input multiple commands * Added ability to export attendance * Fixed bug where attendance marking is cancelled after no command is inputted Throws CommandException when invalid week is entered to mark attendance * Added index functionality for MarkAttendanceCommand, ExportAttendanceCommand Added tests for MarkAttendanceCommand, MarkAttendanceCommandParser, ExportAttendanceCommand, ExportAttendanceCommandParser * Added ability to remove Student from Attendance * Added Assignment, ability to add/delete Assignments * Fixed model.setStudent(), EditCommandTest * Added ability to add/delete/edit Events, hourly tracking * Added ability to add/delete/edit Events, hourly tracking --- .../seedu/tarence/commons/core/Messages.java | 2 + .../logic/commands/AddModuleCommand.java | 10 -- .../logic/commands/AddStudentCommand.java | 10 -- .../logic/commands/AddTutorialCommand.java | 10 -- .../logic/commands/ChangeTabCommand.java | 10 -- .../seedu/tarence/logic/commands/Command.java | 8 +- .../logic/commands/ConfirmNoCommand.java | 10 -- .../logic/commands/ConfirmYesCommand.java | 10 -- .../logic/commands/DeleteModuleCommand.java | 10 -- .../logic/commands/DeleteStudentCommand.java | 10 -- .../logic/commands/DeleteTutorialCommand.java | 10 -- .../commands/DisplayAttendanceCommand.java | 10 -- .../logic/commands/DisplayCommand.java | 10 -- .../tarence/logic/commands/EditCommand.java | 10 -- .../tarence/logic/commands/ExitCommand.java | 10 -- .../commands/ExportAttendanceCommand.java | 14 +- .../tarence/logic/commands/FindCommand.java | 10 -- .../tarence/logic/commands/HelpCommand.java | 10 -- .../tarence/logic/commands/ListCommand.java | 10 -- .../logic/commands/MarkAttendanceCommand.java | 14 +- .../commands/SelectSuggestionCommand.java | 5 - .../logic/commands/SetSemStartCommand.java | 82 ++++++++++ .../assignment/AddAssignmentCommand.java | 6 +- .../assignment/AssignmentCommand.java | 11 +- .../assignment/DeleteAssignmentCommand.java | 6 +- .../logic/commands/event/AddEventCommand.java | 126 ++++++++++++++ .../commands/event/DeleteEventCommand.java | 137 ++++++++++++++++ .../commands/event/EditEventCommand.java | 142 ++++++++++++++++ .../logic/commands/event/EventCommand.java | 154 ++++++++++++++++++ .../commands/event/ListEventsCommand.java | 117 +++++++++++++ .../logic/parser/ApplicationParser.java | 21 +++ .../parser/SetSemStartCommandParser.java | 53 ++++++ .../AddAssignmentCommandParser.java | 16 +- .../DeleteAssignmentCommandParser.java | 15 +- .../parser/event/AddEventCommandParser.java | 110 +++++++++++++ .../event/DeleteEventCommandParser.java | 137 ++++++++++++++++ .../parser/event/EditEventCommandParser.java | 119 ++++++++++++++ .../parser/event/ListEventsCommandParser.java | 89 ++++++++++ .../tarence/model/builder/EventBuilder.java | 64 ++++++++ .../model/builder/TimeTableBuilder.java | 2 +- .../model/builder/TutorialBuilder.java | 2 +- .../seedu/tarence/model/module/Module.java | 14 ++ .../tarence/model/tutorial/Assignment.java | 3 +- .../seedu/tarence/model/tutorial/Event.java | 86 ++++++++++ .../tarence/model/tutorial/TimeTable.java | 23 ++- .../tarence/model/tutorial/Tutorial.java | 99 ++++++++++- .../exceptions/DuplicateEventException.java | 10 ++ 47 files changed, 1617 insertions(+), 230 deletions(-) create mode 100644 src/main/java/seedu/tarence/logic/commands/SetSemStartCommand.java create mode 100644 src/main/java/seedu/tarence/logic/commands/event/AddEventCommand.java create mode 100644 src/main/java/seedu/tarence/logic/commands/event/DeleteEventCommand.java create mode 100644 src/main/java/seedu/tarence/logic/commands/event/EditEventCommand.java create mode 100644 src/main/java/seedu/tarence/logic/commands/event/EventCommand.java create mode 100644 src/main/java/seedu/tarence/logic/commands/event/ListEventsCommand.java create mode 100644 src/main/java/seedu/tarence/logic/parser/SetSemStartCommandParser.java rename src/main/java/seedu/tarence/logic/parser/{ => assignment}/AddAssignmentCommandParser.java (89%) rename src/main/java/seedu/tarence/logic/parser/{ => assignment}/DeleteAssignmentCommandParser.java (93%) create mode 100644 src/main/java/seedu/tarence/logic/parser/event/AddEventCommandParser.java create mode 100644 src/main/java/seedu/tarence/logic/parser/event/DeleteEventCommandParser.java create mode 100644 src/main/java/seedu/tarence/logic/parser/event/EditEventCommandParser.java create mode 100644 src/main/java/seedu/tarence/logic/parser/event/ListEventsCommandParser.java create mode 100644 src/main/java/seedu/tarence/model/builder/EventBuilder.java create mode 100644 src/main/java/seedu/tarence/model/tutorial/Event.java create mode 100644 src/main/java/seedu/tarence/model/tutorial/exceptions/DuplicateEventException.java diff --git a/src/main/java/seedu/tarence/commons/core/Messages.java b/src/main/java/seedu/tarence/commons/core/Messages.java index 916b4d345ca..4e44612feff 100644 --- a/src/main/java/seedu/tarence/commons/core/Messages.java +++ b/src/main/java/seedu/tarence/commons/core/Messages.java @@ -8,6 +8,8 @@ public class Messages { public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; public static final String MESSAGE_INVALID_ASSIGNMENT_IN_TUTORIAL = "No such assignment exists!"; public static final String MESSAGE_INVALID_ASSIGNMENT_DISPLAYED_INDEX = "The assignment index provided is invalid"; + public static final String MESSAGE_INVALID_EVENT_IN_TUTORIAL = "No such event exists!"; + public static final String MESSAGE_INVALID_EVENT_DISPLAYED_INDEX = "The event index provided is invalid"; public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; public static final String MESSAGE_INVALID_FILE = "Invalid file"; public static final String MESSAGE_INVALID_MODULE_DISPLAYED_INDEX = "The module index provided is invalid"; diff --git a/src/main/java/seedu/tarence/logic/commands/AddModuleCommand.java b/src/main/java/seedu/tarence/logic/commands/AddModuleCommand.java index 8760a176894..c91337af096 100644 --- a/src/main/java/seedu/tarence/logic/commands/AddModuleCommand.java +++ b/src/main/java/seedu/tarence/logic/commands/AddModuleCommand.java @@ -48,16 +48,6 @@ public CommandResult execute(Model model) throws CommandException { } - @Override - public boolean needsInput() { - return false; - } - - @Override - public boolean needsCommand(Command command) { - return false; - } - /** * Returns true if user command matches command word or any defined synonyms, and false otherwise. * diff --git a/src/main/java/seedu/tarence/logic/commands/AddStudentCommand.java b/src/main/java/seedu/tarence/logic/commands/AddStudentCommand.java index 3b35980ee3c..487f91ef214 100644 --- a/src/main/java/seedu/tarence/logic/commands/AddStudentCommand.java +++ b/src/main/java/seedu/tarence/logic/commands/AddStudentCommand.java @@ -152,16 +152,6 @@ private String createSuggestedCommands(List similarModCodes, ModCode or return suggestedCorrections; } - @Override - public boolean needsInput() { - return false; - } - - @Override - public boolean needsCommand(Command command) { - return false; - } - /** * Returns true if user command matches command word or any defined synonyms, and false otherwise. * diff --git a/src/main/java/seedu/tarence/logic/commands/AddTutorialCommand.java b/src/main/java/seedu/tarence/logic/commands/AddTutorialCommand.java index f00bdde06ba..e3c5d0283ff 100644 --- a/src/main/java/seedu/tarence/logic/commands/AddTutorialCommand.java +++ b/src/main/java/seedu/tarence/logic/commands/AddTutorialCommand.java @@ -109,16 +109,6 @@ private String createSuggestedCommands(List similarModCodes, Model mode return suggestedCorrections; } - @Override - public boolean needsInput() { - return false; - } - - @Override - public boolean needsCommand(Command command) { - return false; - } - /** * Returns true if user command matches command word or any defined synonyms, and false otherwise. * diff --git a/src/main/java/seedu/tarence/logic/commands/ChangeTabCommand.java b/src/main/java/seedu/tarence/logic/commands/ChangeTabCommand.java index 218da64dc51..3bb508ba4c6 100644 --- a/src/main/java/seedu/tarence/logic/commands/ChangeTabCommand.java +++ b/src/main/java/seedu/tarence/logic/commands/ChangeTabCommand.java @@ -62,16 +62,6 @@ public TabNames getTabvalue() throws CommandException { throw new CommandException(MESSAGE_INVALID_TAB); } - @Override - public boolean needsInput() { - return false; - } - - @Override - public boolean needsCommand(Command command) { - return false; - } - /** * Returns true if user command matches command word or any defined synonyms, and false otherwise. * diff --git a/src/main/java/seedu/tarence/logic/commands/Command.java b/src/main/java/seedu/tarence/logic/commands/Command.java index 61aeae6f061..55e452718ac 100644 --- a/src/main/java/seedu/tarence/logic/commands/Command.java +++ b/src/main/java/seedu/tarence/logic/commands/Command.java @@ -27,13 +27,17 @@ public abstract class Command { /** * Returns true if command requires prior user input, else false. */ - public abstract boolean needsInput(); + public boolean needsInput() { + return false; + } /** * Returns true if prior command is required for execution, else false. * Only needed for commands that require prior user input. */ - public abstract boolean needsCommand(Command command); + public boolean needsCommand(Command command) { + return false; + } /** * Returns a list of {@code ModCode}s similar to the given one, and corresponding to a module containing a diff --git a/src/main/java/seedu/tarence/logic/commands/ConfirmNoCommand.java b/src/main/java/seedu/tarence/logic/commands/ConfirmNoCommand.java index a9f21d68332..032dcae4ccd 100644 --- a/src/main/java/seedu/tarence/logic/commands/ConfirmNoCommand.java +++ b/src/main/java/seedu/tarence/logic/commands/ConfirmNoCommand.java @@ -30,16 +30,6 @@ public CommandResult execute(Model model) throws CommandException { return new CommandResult(MESSAGE_CONFIRM_NO_SUCCESS); } - @Override - public boolean needsInput() { - return false; - } - - @Override - public boolean needsCommand(Command command) { - return false; - } - /** * Returns true if user command matches command word or any defined synonyms, and false otherwise. * diff --git a/src/main/java/seedu/tarence/logic/commands/ConfirmYesCommand.java b/src/main/java/seedu/tarence/logic/commands/ConfirmYesCommand.java index 8def425b6fb..f6da2ac5676 100644 --- a/src/main/java/seedu/tarence/logic/commands/ConfirmYesCommand.java +++ b/src/main/java/seedu/tarence/logic/commands/ConfirmYesCommand.java @@ -28,16 +28,6 @@ public CommandResult execute(Model model) throws CommandException { return model.getPendingCommand().execute(model); } - @Override - public boolean needsInput() { - return false; - } - - @Override - public boolean needsCommand(Command command) { - return false; - } - /** * Returns true if user command matches command word or any defined synonyms, and false otherwise. * diff --git a/src/main/java/seedu/tarence/logic/commands/DeleteModuleCommand.java b/src/main/java/seedu/tarence/logic/commands/DeleteModuleCommand.java index 7659ccdff0c..19d76de31e9 100644 --- a/src/main/java/seedu/tarence/logic/commands/DeleteModuleCommand.java +++ b/src/main/java/seedu/tarence/logic/commands/DeleteModuleCommand.java @@ -118,16 +118,6 @@ private String createSuggestedCommands(List similarModCodes, Model mode return suggestedCorrections; } - @Override - public boolean needsInput() { - return false; - } - - @Override - public boolean needsCommand(Command command) { - return false; - } - /** * Returns true if user command matches command word or any defined synonyms, and false otherwise. * diff --git a/src/main/java/seedu/tarence/logic/commands/DeleteStudentCommand.java b/src/main/java/seedu/tarence/logic/commands/DeleteStudentCommand.java index 9a5cae9b429..f2298cd9b3b 100644 --- a/src/main/java/seedu/tarence/logic/commands/DeleteStudentCommand.java +++ b/src/main/java/seedu/tarence/logic/commands/DeleteStudentCommand.java @@ -47,16 +47,6 @@ public CommandResult execute(Model model) throws CommandException { return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, studentToDelete)); } - @Override - public boolean needsInput() { - return false; - } - - @Override - public boolean needsCommand(Command command) { - return false; - } - /** * Returns true if user command matches command word or any defined synonyms, and false otherwise. * diff --git a/src/main/java/seedu/tarence/logic/commands/DeleteTutorialCommand.java b/src/main/java/seedu/tarence/logic/commands/DeleteTutorialCommand.java index 482b7e7cff6..dbcf4b2c637 100644 --- a/src/main/java/seedu/tarence/logic/commands/DeleteTutorialCommand.java +++ b/src/main/java/seedu/tarence/logic/commands/DeleteTutorialCommand.java @@ -156,16 +156,6 @@ private String createSuggestedCommands(List similarModCodes, ModCode or return suggestedCorrections; } - @Override - public boolean needsInput() { - return false; - } - - @Override - public boolean needsCommand(Command command) { - return false; - } - /** * Returns true if user command matches command word or any defined synonyms, and false otherwise. * diff --git a/src/main/java/seedu/tarence/logic/commands/DisplayAttendanceCommand.java b/src/main/java/seedu/tarence/logic/commands/DisplayAttendanceCommand.java index f6da3a98458..db657535e69 100644 --- a/src/main/java/seedu/tarence/logic/commands/DisplayAttendanceCommand.java +++ b/src/main/java/seedu/tarence/logic/commands/DisplayAttendanceCommand.java @@ -154,16 +154,6 @@ private String createSuggestedCommands(List similarModCodes, List lastShownList = model.getFilteredTutorialList(); + + // Removes outdated events when semStart changes + for (Tutorial tutorial : lastShownList) { + List tutEvents = tutorial.getTutorialasEvents(); + for (Event tutEvent : tutEvents) { + tutorial.deleteEvent(tutEvent); + } + } + + Module.setSemStart(semStart); + + DateFormat dateFormat = new SimpleDateFormat(Tutorial.DATE_FORMAT); + return new CommandResult( + String.format(MESSAGE_SET_SEM_START_SUCCESS + "\n", + dateFormat.format(semStart))); + } + + /** + * Returns true if user command matches command word or any defined synonyms, and false otherwise. + * + * @param userCommand command word from user. + * @return whether user command matches specified command word or synonyms. + */ + public static boolean isMatchingCommandWord(String userCommand) { + for (String synonym : COMMAND_SYNONYMS) { + if (synonym.equals(userCommand.toLowerCase())) { + return true; + } + } + return false; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof SetSemStartCommand // instanceof handles nulls + && super.equals(other)); // state check + } +} diff --git a/src/main/java/seedu/tarence/logic/commands/assignment/AddAssignmentCommand.java b/src/main/java/seedu/tarence/logic/commands/assignment/AddAssignmentCommand.java index d11c562ef49..7a54418b1fe 100644 --- a/src/main/java/seedu/tarence/logic/commands/assignment/AddAssignmentCommand.java +++ b/src/main/java/seedu/tarence/logic/commands/assignment/AddAssignmentCommand.java @@ -30,13 +30,13 @@ public class AddAssignmentCommand extends AssignmentCommand { public static final String MESSAGE_ADD_ASSIGNMENT_SUCCESS = "%1$s created successfully"; public static final String COMMAND_WORD = "addAssign"; - private static final String[] COMMAND_SYNONYMS = {COMMAND_WORD.toLowerCase(), "addA", "addAsm"}; + private static final String[] COMMAND_SYNONYMS = {COMMAND_WORD.toLowerCase(), "adda", "addasm", "addassn"}; // TODO: Update message to include index format public static final String MESSAGE_USAGE = COMMAND_WORD + ": Creates an assignment for a tutorial.\n" + "Parameters: " - + PREFIX_TUTORIAL_NAME + "TUTORIAL_NAME " - + PREFIX_MODULE + "MODULE_CODE " + + PREFIX_TUTORIAL_NAME + "TUTORIAL NAME " + + PREFIX_MODULE + "MODULE CODE " + PREFIX_NAME + "ASSIGNMENT NAME " + PREFIX_MAX_SCORE + "MAX SCORE " + PREFIX_START_DATE + "START DATE " diff --git a/src/main/java/seedu/tarence/logic/commands/assignment/AssignmentCommand.java b/src/main/java/seedu/tarence/logic/commands/assignment/AssignmentCommand.java index 1b959f17429..157b76ed471 100644 --- a/src/main/java/seedu/tarence/logic/commands/assignment/AssignmentCommand.java +++ b/src/main/java/seedu/tarence/logic/commands/assignment/AssignmentCommand.java @@ -145,16 +145,6 @@ protected String createSuggestedCommands(List similarModCodes, List lastShownList = model.getFilteredTutorialList(); + + // TODO: Consider cases with multiple matching tutorials, students? + Tutorial targetTutorial = null; + if (targetModCode.isPresent() && targetTutName.isPresent()) { + // format with modcode and tutname + targetTutorial = lastShownList.stream() + .filter(tut -> tut.getTutName().equals(targetTutName.get()) + && tut.getModCode().equals(targetModCode.get())) + .findFirst() + .orElse(null); + if (targetTutorial == null) { + return handleSuggestedCommands(model, new AddEventCommand()); + } + } else if (targetTutIndex.isPresent()) { + // format with tutorial index + try { + targetTutorial = lastShownList.get(targetTutIndex.get().getZeroBased()); + } catch (IndexOutOfBoundsException e) { + throw new CommandException( + String.format(Messages.MESSAGE_INVALID_TUTORIAL_DISPLAYED_INDEX)); + } + } + + try { + targetTutorial.addEvent(new Event( + eventName.get(), + startTime.get(), + endTime.get())); + } catch (IllegalArgumentException e) { + throw new CommandException(e.getMessage()); + } + + return new CommandResult( + String.format(MESSAGE_ADD_EVENT_SUCCESS, eventName.get())); + } + + /** + * Returns true if user command matches command word or any defined synonyms, and false otherwise. + * + * @param userCommand command word from user. + * @return whether user command matches specified command word or synonyms. + */ + public static boolean isMatchingCommandWord(String userCommand) { + for (String synonym : COMMAND_SYNONYMS) { + if (synonym.equals(userCommand.toLowerCase())) { + return true; + } + } + return false; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddEventCommand // instanceof handles nulls + && super.equals(other)); // state check + } +} diff --git a/src/main/java/seedu/tarence/logic/commands/event/DeleteEventCommand.java b/src/main/java/seedu/tarence/logic/commands/event/DeleteEventCommand.java new file mode 100644 index 00000000000..1d078476b45 --- /dev/null +++ b/src/main/java/seedu/tarence/logic/commands/event/DeleteEventCommand.java @@ -0,0 +1,137 @@ +package seedu.tarence.logic.commands.event; + +import static java.util.Objects.requireNonNull; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_END_DATE; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_MODULE; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_START_DATE; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_TUTORIAL_NAME; + +import java.util.Date; +import java.util.List; + +import seedu.tarence.commons.core.Messages; +import seedu.tarence.commons.core.index.Index; +import seedu.tarence.logic.commands.CommandResult; +import seedu.tarence.logic.commands.exceptions.CommandException; +import seedu.tarence.model.Model; +import seedu.tarence.model.module.ModCode; +import seedu.tarence.model.tutorial.Event; +import seedu.tarence.model.tutorial.TutName; +import seedu.tarence.model.tutorial.Tutorial; + +/** + * Deletes assignment in a specified tutorial. + * Keyword matching is case insensitive. + */ +public class DeleteEventCommand extends EventCommand { + + public static final String MESSAGE_DELETE_EVENT_SUCCESS = "%1$s deleted successfully"; + public static final String COMMAND_WORD = "deleteEvent"; + private static final String[] COMMAND_SYNONYMS = {COMMAND_WORD.toLowerCase(), "delevnt", "dele", "delev"}; + + // TODO: Update message to include index format + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes an event for a tutorial.\n" + + "Parameters: " + + PREFIX_TUTORIAL_NAME + "TUTORIAL NAME " + + PREFIX_MODULE + "MODULE CODE " + + PREFIX_NAME + "EVENT NAME " + + PREFIX_START_DATE + "START TIME " + + PREFIX_END_DATE + "END TIME\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_TUTORIAL_NAME + "Lab 1 " + + PREFIX_MODULE + "CS1010 " + + PREFIX_NAME + "Lab01 " + + PREFIX_START_DATE + "09-11-2001 0000 " + + PREFIX_END_DATE + "31-10-2019 2359"; + + public DeleteEventCommand(ModCode modCode, TutName tutName, Index tutIndex, Index eventIndex, + String eventName, Date startTime, Date endTime) { + super(modCode, tutName, tutIndex, eventIndex, eventName, startTime, endTime); + } + + public DeleteEventCommand() { + super(); + } + + @Override + public EventCommand build(ModCode modCode, TutName tutName, Index tutIndex, Index eventIndex, + String eventName, Date startTime, Date endTime) { + return new DeleteEventCommand(modCode, tutName, tutIndex, eventIndex, + eventName, startTime, endTime); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredTutorialList(); + + // TODO: Consider cases with multiple matching tutorials, students? + Tutorial targetTutorial = null; + if (targetModCode.isPresent() && targetTutName.isPresent()) { + // format with modcode and tutname + targetTutorial = lastShownList.stream() + .filter(tut -> tut.getTutName().equals(targetTutName.get()) + && tut.getModCode().equals(targetModCode.get())) + .findFirst() + .orElse(null); + if (targetTutorial == null) { + return handleSuggestedCommands(model, + new DeleteEventCommand()); + } + } else if (targetTutIndex.isPresent()) { + // format with tutorial index + try { + targetTutorial = lastShownList.get(targetTutIndex.get().getZeroBased()); + } catch (IndexOutOfBoundsException e) { + throw new CommandException( + String.format(Messages.MESSAGE_INVALID_TUTORIAL_DISPLAYED_INDEX)); + } + } + + Event targetEvent = null; + if (targetEventIndex.isEmpty()) { + // format with assignment details + targetEvent = new Event( + eventName.get(), + startTime.get(), + endTime.get()); + boolean isRemoved = targetTutorial.deleteEvent(targetEvent); + + if (!isRemoved) { + throw new CommandException(Messages.MESSAGE_INVALID_EVENT_IN_TUTORIAL); + } + } else { + // format with assignment index + try { + targetEvent = targetTutorial.deleteEvent(targetEventIndex.get().getZeroBased()); + } catch (IndexOutOfBoundsException e) { + throw new CommandException(Messages.MESSAGE_INVALID_EVENT_DISPLAYED_INDEX); + } + } + return new CommandResult( + String.format(MESSAGE_DELETE_EVENT_SUCCESS, targetEvent.eventName)); + } + + /** + * Returns true if user command matches command word or any defined synonyms, and false otherwise. + * + * @param userCommand command word from user. + * @return whether user command matches specified command word or synonyms. + */ + public static boolean isMatchingCommandWord(String userCommand) { + for (String synonym : COMMAND_SYNONYMS) { + if (synonym.equals(userCommand.toLowerCase())) { + return true; + } + } + return false; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteEventCommand // instanceof handles nulls + && super.equals(other)); // state check + } +} diff --git a/src/main/java/seedu/tarence/logic/commands/event/EditEventCommand.java b/src/main/java/seedu/tarence/logic/commands/event/EditEventCommand.java new file mode 100644 index 00000000000..71f414305bf --- /dev/null +++ b/src/main/java/seedu/tarence/logic/commands/event/EditEventCommand.java @@ -0,0 +1,142 @@ +package seedu.tarence.logic.commands.event; + +import static java.util.Objects.requireNonNull; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_END_DATE; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_INDEX; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_MODULE; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_START_DATE; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_TUTORIAL_NAME; + +import java.util.Date; +import java.util.List; + +import seedu.tarence.commons.core.Messages; +import seedu.tarence.commons.core.index.Index; +import seedu.tarence.logic.commands.CommandResult; +import seedu.tarence.logic.commands.exceptions.CommandException; +import seedu.tarence.model.Model; +import seedu.tarence.model.builder.EventBuilder; +import seedu.tarence.model.module.ModCode; +import seedu.tarence.model.tutorial.Event; +import seedu.tarence.model.tutorial.TutName; +import seedu.tarence.model.tutorial.Tutorial; + +/** + * Deletes assignment in a specified tutorial. + * Keyword matching is case insensitive. + */ +public class EditEventCommand extends EventCommand { + + public static final String MESSAGE_DELETE_EVENT_SUCCESS = "%1$s edited successfully"; + public static final String COMMAND_WORD = "editEvent"; + private static final String[] COMMAND_SYNONYMS = {COMMAND_WORD.toLowerCase(), "edev", "editev", "edite"}; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits an event for a tutorial.\n" + + "Parameters: " + + PREFIX_INDEX + "TARGET EVENT INDEX " + + PREFIX_TUTORIAL_NAME + "TUTORIAL NAME " + + PREFIX_MODULE + "MODULE CODE " + + PREFIX_NAME + "EDITED EVENT NAME(OPTIONAL) " + + PREFIX_START_DATE + "EDITED START TIME(OPTIONAL) " + + PREFIX_END_DATE + "EDITED END TIME(OPTIONAL)\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_INDEX + "1 " + + PREFIX_TUTORIAL_NAME + "Lab 1 " + + PREFIX_MODULE + "CS1010 " + + PREFIX_NAME + "Lab01 " + + PREFIX_START_DATE + "09-11-2001 0000 " + + PREFIX_END_DATE + "31-10-2019 2359"; + + public EditEventCommand(ModCode modCode, TutName tutName, Index tutIndex, Index eventIndex, + String eventName, Date startTime, Date endTime) { + super(modCode, tutName, tutIndex, eventIndex, eventName, startTime, endTime); + requireNonNull(eventIndex); + } + + public EditEventCommand() { + super(); + } + + @Override + public EventCommand build(ModCode modCode, TutName tutName, Index tutIndex, Index eventIndex, + String eventName, Date startTime, Date endTime) { + return new EditEventCommand(modCode, tutName, tutIndex, eventIndex, + eventName, startTime, endTime); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredTutorialList(); + + // TODO: Consider cases with multiple matching tutorials, students? + Tutorial targetTutorial = null; + if (targetModCode.isPresent() && targetTutName.isPresent()) { + // format with modcode and tutname + targetTutorial = lastShownList.stream() + .filter(tut -> tut.getTutName().equals(targetTutName.get()) + && tut.getModCode().equals(targetModCode.get())) + .findFirst() + .orElse(null); + if (targetTutorial == null) { + return handleSuggestedCommands(model, + new EditEventCommand()); + } + } else if (targetTutIndex.isPresent()) { + // format with tutorial index + try { + targetTutorial = lastShownList.get(targetTutIndex.get().getZeroBased()); + } catch (IndexOutOfBoundsException e) { + throw new CommandException( + String.format(Messages.MESSAGE_INVALID_TUTORIAL_DISPLAYED_INDEX)); + } + } + + Event targetEvent; + try { + targetEvent = targetTutorial.getEventLog().get(targetEventIndex.get().getZeroBased()); + } catch (IndexOutOfBoundsException e) { + throw new CommandException(Messages.MESSAGE_INVALID_EVENT_DISPLAYED_INDEX); + } + EventBuilder eventBuilder = new EventBuilder(targetEvent); + if (eventName.isPresent()) { + eventBuilder = eventBuilder.withEventName(eventName.get()); + } + if (startTime.isPresent()) { + eventBuilder = eventBuilder.withStartTime(startTime.get()); + } + if (endTime.isPresent()) { + eventBuilder = eventBuilder.withEndTime(endTime.get()); + } + Event editedEvent = eventBuilder.build(); + + targetTutorial.deleteEvent(targetEvent); + targetTutorial.addEvent(editedEvent); + + return new CommandResult( + String.format(MESSAGE_DELETE_EVENT_SUCCESS, editedEvent.eventName)); + } + + /** + * Returns true if user command matches command word or any defined synonyms, and false otherwise. + * + * @param userCommand command word from user. + * @return whether user command matches specified command word or synonyms. + */ + public static boolean isMatchingCommandWord(String userCommand) { + for (String synonym : COMMAND_SYNONYMS) { + if (synonym.equals(userCommand.toLowerCase())) { + return true; + } + } + return false; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EditEventCommand // instanceof handles nulls + && super.equals(other)); // state check + } +} diff --git a/src/main/java/seedu/tarence/logic/commands/event/EventCommand.java b/src/main/java/seedu/tarence/logic/commands/event/EventCommand.java new file mode 100644 index 00000000000..7c31922a7e0 --- /dev/null +++ b/src/main/java/seedu/tarence/logic/commands/event/EventCommand.java @@ -0,0 +1,154 @@ +package seedu.tarence.logic.commands.event; + +import static seedu.tarence.commons.core.Messages.MESSAGE_SUGGESTED_CORRECTIONS; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Optional; + +import seedu.tarence.commons.core.Messages; +import seedu.tarence.commons.core.index.Index; +import seedu.tarence.logic.commands.Command; +import seedu.tarence.logic.commands.CommandResult; +import seedu.tarence.logic.commands.SelectSuggestionCommand; +import seedu.tarence.logic.commands.exceptions.CommandException; +import seedu.tarence.model.Model; +import seedu.tarence.model.module.ModCode; +import seedu.tarence.model.tutorial.TutName; + +/** + * Modifies events in a specified tutorial. + * Keyword matching is case insensitive. + */ +public abstract class EventCommand extends Command { + + protected final Optional targetModCode; + protected final Optional targetTutName; + protected final Optional targetTutIndex; + protected final Optional targetEventIndex; + protected final Optional eventName; + protected final Optional startTime; + protected final Optional endTime; + + public EventCommand(ModCode modCode, TutName tutName, Index tutIndex, Index eventIndex, + String eventName, Date startTime, Date endTime) { + this.targetModCode = Optional.ofNullable(modCode); + this.targetTutName = Optional.ofNullable(tutName); + this.targetTutIndex = Optional.ofNullable(tutIndex); + this.targetEventIndex = Optional.ofNullable(eventIndex); + this.eventName = Optional.ofNullable(eventName); + this.startTime = Optional.ofNullable(startTime); + this.endTime = Optional.ofNullable(endTime); + } + + public EventCommand() { + this.targetModCode = Optional.empty(); + this.targetTutName = Optional.empty(); + this.targetTutIndex = Optional.empty(); + this.targetEventIndex = Optional.empty(); + this.eventName = Optional.empty(); + this.startTime = Optional.empty(); + this.endTime = Optional.empty(); + } + + public abstract EventCommand build(ModCode modCode, TutName tutName, Index tutIndex, Index eventIndex, + String eventName, Date startTime, Date endTime); + + /** + * Handles the creating and processing of suggested {@code EventCommand}s, if the user's input does not + * match any combination of modules and tutorials. + * + * @param model The model to search in. + * @param eventCommand The command used to build suggested commands. + * @return a string representation of the suggested alternative commands to the user's invalid input. + * @throws CommandException if no suggested commands can be found. + */ + protected CommandResult handleSuggestedCommands(Model model, + EventCommand eventCommand) throws CommandException { + ModCode modCode = targetModCode.get(); + TutName tutName = targetTutName.get(); + // find tutorials with same name and similar modcodes, and similar names and same modcode + List similarModCodes = getSimilarModCodesWithTutorial(modCode, + tutName, model); + List similarTutNames = getSimilarTutNamesWithModule(modCode, + tutName, model); + if (similarModCodes.size() == 0 && similarTutNames.size() == 0) { + throw new CommandException(Messages.MESSAGE_INVALID_TUTORIAL_IN_MODULE); + } + + String suggestedCorrections = createSuggestedCommands(similarModCodes, + similarTutNames, model, eventCommand); + model.storePendingCommand(new SelectSuggestionCommand()); + return new CommandResult(String.format(MESSAGE_SUGGESTED_CORRECTIONS, "Tutorial", + modCode.toString() + " " + tutName.toString()) + suggestedCorrections); + } + + /** + * Generates and stores {@code EventCommand}s from a list of {@code ModCode}s and {@code TutName}s. + * + * @param similarModCodes List of {@code ModCode}s similar to the user's input. + * @param similarTutNames List of {@code TutName}s similar to the user's input. + * @param model The {@code Model} in which to store the generated commands. + * @param eventCommand The command used to build suggested comands. + * @return string representing the generated suggestions and their corresponding indexes for user selection. + */ + protected String createSuggestedCommands(List similarModCodes, List similarTutNames, + Model model, EventCommand eventCommand) { + ModCode modCode = targetModCode.get(); + TutName tutName = targetTutName.get(); + Index tutIndex = null; + Index eventIndex = targetEventIndex.orElse(null); + String eventName = this.eventName.orElse(null); + Date startTime = this.startTime.orElse(null); + Date endTime = this.endTime.orElse(null); + List suggestedCommands = new ArrayList<>(); + StringBuilder s = new StringBuilder(); + int index = 1; + for (ModCode similarModCode : similarModCodes) { + suggestedCommands.add(eventCommand.build( + similarModCode, + tutName, + tutIndex, + eventIndex, + eventName, + startTime, + endTime)); + s.append(index).append(". ").append(similarModCode).append(", ").append(tutName).append("\n"); + index++; + } + for (TutName similarTutName: similarTutNames) { + EventCommand newCommand = eventCommand.build( + modCode, + similarTutName, + tutIndex, + eventIndex, + eventName, + startTime, + endTime); + if (suggestedCommands.stream() + .anyMatch(existingCommand -> existingCommand.equals(newCommand))) { + continue; + } + suggestedCommands.add(newCommand); + s.append(index).append(". ").append(modCode).append(", ").append(similarTutName).append("\n"); + index++; + } + String suggestedCorrections = s.toString(); + model.storeSuggestedCommands(suggestedCommands, suggestedCorrections); + return suggestedCorrections; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EventCommand // instanceof handles nulls + && targetModCode.equals(((EventCommand) other).targetModCode) + && targetTutName.equals(((EventCommand) other).targetTutName) + && targetTutIndex.equals(((EventCommand) other).targetTutIndex) + && targetEventIndex.equals(((EventCommand) other).targetEventIndex) + && eventName.equals(((EventCommand) other).eventName) + && startTime.equals(((EventCommand) other).startTime) + && endTime.equals(((EventCommand) other).endTime)); // state check + } +} diff --git a/src/main/java/seedu/tarence/logic/commands/event/ListEventsCommand.java b/src/main/java/seedu/tarence/logic/commands/event/ListEventsCommand.java new file mode 100644 index 00000000000..85abc2e7aa5 --- /dev/null +++ b/src/main/java/seedu/tarence/logic/commands/event/ListEventsCommand.java @@ -0,0 +1,117 @@ +package seedu.tarence.logic.commands.event; + +import static java.util.Objects.requireNonNull; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_MODULE; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_TUTORIAL_NAME; + +import java.util.Date; +import java.util.List; + +import seedu.tarence.commons.core.Messages; +import seedu.tarence.commons.core.index.Index; +import seedu.tarence.logic.commands.CommandResult; +import seedu.tarence.logic.commands.exceptions.CommandException; +import seedu.tarence.model.Model; +import seedu.tarence.model.module.ModCode; +import seedu.tarence.model.tutorial.Event; +import seedu.tarence.model.tutorial.TutName; +import seedu.tarence.model.tutorial.Tutorial; + +/** + * Lists Events in a Tutorial. + * Keyword matching is case insensitive. + */ +public class ListEventsCommand extends EventCommand { + + public static final String MESSAGE_LIST_EVENT_SUCCESS = "Hours logged: %d\nEvent log for %s %s:"; + public static final String COMMAND_WORD = "listEvents"; + private static final String[] COMMAND_SYNONYMS = {COMMAND_WORD.toLowerCase(), "liste", "listev", "listevnt"}; + + // TODO: Update message to include index format + public static final String MESSAGE_USAGE = COMMAND_WORD + ": List events in a tutorial.\n" + + "Parameters: " + + PREFIX_TUTORIAL_NAME + "TUTORIAL NAME " + + PREFIX_MODULE + "MODULE CODE\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_TUTORIAL_NAME + "Lab 1 " + + PREFIX_MODULE + "CS1010 "; + + public ListEventsCommand(ModCode modCode, TutName tutName, Index tutIndex) { + super(modCode, tutName, tutIndex, null, null, null, null); + } + + public ListEventsCommand() { + super(); + } + + @Override + public EventCommand build(ModCode modCode, TutName tutName, Index tutIndex, Index eventIndex, + String eventName, Date startTime, Date endTime) { + return new ListEventsCommand(modCode, tutName, tutIndex); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredTutorialList(); + + // TODO: Consider cases with multiple matching tutorials, students? + Tutorial targetTutorial = null; + if (targetModCode.isPresent() && targetTutName.isPresent()) { + // format with modcode and tutname + targetTutorial = lastShownList.stream() + .filter(tut -> tut.getTutName().equals(targetTutName.get()) + && tut.getModCode().equals(targetModCode.get())) + .findFirst() + .orElse(null); + if (targetTutorial == null) { + return handleSuggestedCommands(model, new ListEventsCommand()); + } + } else if (targetTutIndex.isPresent()) { + // format with tutorial index + try { + targetTutorial = lastShownList.get(targetTutIndex.get().getZeroBased()); + } catch (IndexOutOfBoundsException e) { + throw new CommandException( + String.format(Messages.MESSAGE_INVALID_TUTORIAL_DISPLAYED_INDEX)); + } + } + + List eventLog = targetTutorial.getEventLog(); + StringBuffer stringBuffer = new StringBuffer(); + int index = 1; + for (Event event : eventLog) { + stringBuffer.append(index + ". " + event + "\n"); + index++; + } + + return new CommandResult( + String.format(MESSAGE_LIST_EVENT_SUCCESS + "\n" + + stringBuffer.toString(), + targetTutorial.getHours(), + targetTutorial.getTutName(), + targetTutorial.getModCode())); + } + + /** + * Returns true if user command matches command word or any defined synonyms, and false otherwise. + * + * @param userCommand command word from user. + * @return whether user command matches specified command word or synonyms. + */ + public static boolean isMatchingCommandWord(String userCommand) { + for (String synonym : COMMAND_SYNONYMS) { + if (synonym.equals(userCommand.toLowerCase())) { + return true; + } + } + return false; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ListEventsCommand // instanceof handles nulls + && super.equals(other)); // state check + } +} diff --git a/src/main/java/seedu/tarence/logic/parser/ApplicationParser.java b/src/main/java/seedu/tarence/logic/parser/ApplicationParser.java index ffdeb31996e..5744171d6b6 100644 --- a/src/main/java/seedu/tarence/logic/parser/ApplicationParser.java +++ b/src/main/java/seedu/tarence/logic/parser/ApplicationParser.java @@ -25,8 +25,19 @@ import seedu.tarence.logic.commands.ListCommand; import seedu.tarence.logic.commands.MarkAttendanceCommand; import seedu.tarence.logic.commands.SelectSuggestionCommand; +import seedu.tarence.logic.commands.SetSemStartCommand; import seedu.tarence.logic.commands.assignment.AddAssignmentCommand; import seedu.tarence.logic.commands.assignment.DeleteAssignmentCommand; +import seedu.tarence.logic.commands.event.AddEventCommand; +import seedu.tarence.logic.commands.event.DeleteEventCommand; +import seedu.tarence.logic.commands.event.EditEventCommand; +import seedu.tarence.logic.commands.event.ListEventsCommand; +import seedu.tarence.logic.parser.assignment.AddAssignmentCommandParser; +import seedu.tarence.logic.parser.assignment.DeleteAssignmentCommandParser; +import seedu.tarence.logic.parser.event.AddEventCommandParser; +import seedu.tarence.logic.parser.event.DeleteEventCommandParser; +import seedu.tarence.logic.parser.event.EditEventCommandParser; +import seedu.tarence.logic.parser.event.ListEventsCommandParser; import seedu.tarence.logic.parser.exceptions.ParseException; import seedu.tarence.model.Model; @@ -94,6 +105,16 @@ public Command parseCommand(String userInput) throws ParseException { return new AddAssignmentCommandParser().parse(arguments); } else if (DeleteAssignmentCommand.isMatchingCommandWord(commandWord)) { return new DeleteAssignmentCommandParser().parse(arguments); + } else if (AddEventCommand.isMatchingCommandWord(commandWord)) { + return new AddEventCommandParser().parse(arguments); + } else if (DeleteEventCommand.isMatchingCommandWord(commandWord)) { + return new DeleteEventCommandParser().parse(arguments); + } else if (EditEventCommand.isMatchingCommandWord(commandWord)) { + return new EditEventCommandParser().parse(arguments); + } else if (ListEventsCommand.isMatchingCommandWord(commandWord)) { + return new ListEventsCommandParser().parse(arguments); + } else if (SetSemStartCommand.isMatchingCommandWord(commandWord)) { + return new SetSemStartCommandParser().parse(arguments); } else if (ChangeTabCommand.isMatchingCommandWord(commandWord)) { return new ChangeTabCommand(arguments); } else { diff --git a/src/main/java/seedu/tarence/logic/parser/SetSemStartCommandParser.java b/src/main/java/seedu/tarence/logic/parser/SetSemStartCommandParser.java new file mode 100644 index 00000000000..7703a2ed48e --- /dev/null +++ b/src/main/java/seedu/tarence/logic/parser/SetSemStartCommandParser.java @@ -0,0 +1,53 @@ +package seedu.tarence.logic.parser; + +import static seedu.tarence.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_START_DATE; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.stream.Stream; + +import seedu.tarence.logic.commands.SetSemStartCommand; +import seedu.tarence.logic.parser.exceptions.ParseException; +import seedu.tarence.model.tutorial.Tutorial; + +/** + * Parses input arguments and creates a new SetSemStartCommand object + */ +public class SetSemStartCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the SetSemStartCommand + * and returns an SetSemStartCommand object for execution. + * @throws ParseException if the user input does not match the expected formats for the module code. + */ + public SetSemStartCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, + PREFIX_START_DATE); + + if (!arePrefixesPresent(argMultimap, PREFIX_START_DATE)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + SetSemStartCommand.MESSAGE_USAGE)); + } + + argMultimap.getValue(PREFIX_START_DATE).get(); + SimpleDateFormat dateFormatter = new SimpleDateFormat(Tutorial.DATE_FORMAT); + Date startDate; + try { + startDate = dateFormatter.parse(argMultimap.getValue(PREFIX_START_DATE).get()); + } catch (java.text.ParseException e) { + throw new ParseException(String.format("Format should be %s", Tutorial.DATE_FORMAT)); + } + + return new SetSemStartCommand(startDate); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} + diff --git a/src/main/java/seedu/tarence/logic/parser/AddAssignmentCommandParser.java b/src/main/java/seedu/tarence/logic/parser/assignment/AddAssignmentCommandParser.java similarity index 89% rename from src/main/java/seedu/tarence/logic/parser/AddAssignmentCommandParser.java rename to src/main/java/seedu/tarence/logic/parser/assignment/AddAssignmentCommandParser.java index cd76f9d91f6..5a8955c9755 100644 --- a/src/main/java/seedu/tarence/logic/parser/AddAssignmentCommandParser.java +++ b/src/main/java/seedu/tarence/logic/parser/assignment/AddAssignmentCommandParser.java @@ -1,4 +1,4 @@ -package seedu.tarence.logic.parser; +package seedu.tarence.logic.parser.assignment; import static seedu.tarence.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import static seedu.tarence.logic.parser.CliSyntax.PREFIX_END_DATE; @@ -15,17 +15,21 @@ import seedu.tarence.commons.core.index.Index; import seedu.tarence.logic.commands.assignment.AddAssignmentCommand; -import seedu.tarence.logic.commands.assignment.AssignmentCommand; +import seedu.tarence.logic.parser.ArgumentMultimap; +import seedu.tarence.logic.parser.ArgumentTokenizer; +import seedu.tarence.logic.parser.Parser; +import seedu.tarence.logic.parser.ParserUtil; +import seedu.tarence.logic.parser.Prefix; import seedu.tarence.logic.parser.exceptions.ParseException; import seedu.tarence.model.module.ModCode; import seedu.tarence.model.tutorial.Assignment; import seedu.tarence.model.tutorial.TutName; - +import seedu.tarence.model.tutorial.Tutorial; /** - * Parses input arguments and creates a new AddModuleCommand object + * Parses input arguments and creates a new AddAssignmentCommand object */ -public class AddAssignmentCommandParser implements Parser { +public class AddAssignmentCommandParser implements Parser { /** * Parses the given {@code String} of arguments in the context of the AddAssignmentCommand @@ -57,7 +61,7 @@ public AddAssignmentCommand parse(String args) throws ParseException { } catch (NumberFormatException e) { throw new ParseException(Assignment.MESSAGE_CONSTRAINTS_MAX_SCORE); } - SimpleDateFormat dateFormatter = new SimpleDateFormat(Assignment.DATE_FORMAT); + SimpleDateFormat dateFormatter = new SimpleDateFormat(Tutorial.DATE_FORMAT); Date startDate; Date endDate; try { diff --git a/src/main/java/seedu/tarence/logic/parser/DeleteAssignmentCommandParser.java b/src/main/java/seedu/tarence/logic/parser/assignment/DeleteAssignmentCommandParser.java similarity index 93% rename from src/main/java/seedu/tarence/logic/parser/DeleteAssignmentCommandParser.java rename to src/main/java/seedu/tarence/logic/parser/assignment/DeleteAssignmentCommandParser.java index 7e03eec3c3a..9911e015222 100644 --- a/src/main/java/seedu/tarence/logic/parser/DeleteAssignmentCommandParser.java +++ b/src/main/java/seedu/tarence/logic/parser/assignment/DeleteAssignmentCommandParser.java @@ -1,4 +1,4 @@ -package seedu.tarence.logic.parser; +package seedu.tarence.logic.parser.assignment; import static seedu.tarence.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import static seedu.tarence.logic.parser.CliSyntax.PREFIX_END_DATE; @@ -14,18 +14,22 @@ import java.util.stream.Stream; import seedu.tarence.commons.core.index.Index; -import seedu.tarence.logic.commands.assignment.AssignmentCommand; import seedu.tarence.logic.commands.assignment.DeleteAssignmentCommand; +import seedu.tarence.logic.parser.ArgumentMultimap; +import seedu.tarence.logic.parser.ArgumentTokenizer; +import seedu.tarence.logic.parser.Parser; +import seedu.tarence.logic.parser.ParserUtil; +import seedu.tarence.logic.parser.Prefix; import seedu.tarence.logic.parser.exceptions.ParseException; import seedu.tarence.model.module.ModCode; import seedu.tarence.model.tutorial.Assignment; import seedu.tarence.model.tutorial.TutName; - +import seedu.tarence.model.tutorial.Tutorial; /** * Parses input arguments and creates a new DeleteAssignmentCommand object */ -public class DeleteAssignmentCommandParser implements Parser { +public class DeleteAssignmentCommandParser implements Parser { /** * Parses the given {@code String} of arguments in the context of the DeleteAssignmentCommand @@ -62,7 +66,7 @@ public DeleteAssignmentCommand parse(String args) throws ParseException { } catch (NumberFormatException e) { throw new ParseException(Assignment.MESSAGE_CONSTRAINTS_MAX_SCORE); } - SimpleDateFormat dateFormatter = new SimpleDateFormat(Assignment.DATE_FORMAT); + SimpleDateFormat dateFormatter = new SimpleDateFormat(Tutorial.DATE_FORMAT); try { startDate = dateFormatter.parse(argMultimap.getValue(PREFIX_START_DATE).get()); endDate = dateFormatter.parse(argMultimap.getValue(PREFIX_END_DATE).get()); @@ -86,7 +90,6 @@ public DeleteAssignmentCommand parse(String args) throws ParseException { * else false. */ public static boolean validateModCodeTutNameFormat(ArgumentMultimap argMultimap) { - // student name not checked since it is optional // modcode, tutorial name present without tutorial index - first format return (arePrefixesPresent(argMultimap, PREFIX_MODULE, PREFIX_TUTORIAL_NAME) && arePrefixesAbsent(argMultimap, PREFIX_INDEX)); diff --git a/src/main/java/seedu/tarence/logic/parser/event/AddEventCommandParser.java b/src/main/java/seedu/tarence/logic/parser/event/AddEventCommandParser.java new file mode 100644 index 00000000000..9f82f023760 --- /dev/null +++ b/src/main/java/seedu/tarence/logic/parser/event/AddEventCommandParser.java @@ -0,0 +1,110 @@ +package seedu.tarence.logic.parser.event; + +import static seedu.tarence.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_END_DATE; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_INDEX; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_MODULE; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_START_DATE; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_TUTORIAL_NAME; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.stream.Stream; + +import seedu.tarence.commons.core.index.Index; +import seedu.tarence.logic.commands.event.AddEventCommand; +import seedu.tarence.logic.parser.ArgumentMultimap; +import seedu.tarence.logic.parser.ArgumentTokenizer; +import seedu.tarence.logic.parser.Parser; +import seedu.tarence.logic.parser.ParserUtil; +import seedu.tarence.logic.parser.Prefix; +import seedu.tarence.logic.parser.exceptions.ParseException; +import seedu.tarence.model.module.ModCode; +import seedu.tarence.model.tutorial.Event; +import seedu.tarence.model.tutorial.TutName; +import seedu.tarence.model.tutorial.Tutorial; + +/** + * Parses input arguments and creates a new AddEventCommand object + */ +public class AddEventCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddEventCommand + * and returns an AddEventCommand object for execution. + * @throws ParseException if the user input does not match the expected formats for the module code. + */ + public AddEventCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, + PREFIX_MODULE, PREFIX_TUTORIAL_NAME, PREFIX_INDEX, + PREFIX_NAME, PREFIX_START_DATE, PREFIX_END_DATE); + + ModCode modCode = null; + TutName tutName = null; + Index tutIndex = null; + if (validateModCodeTutNameFormat(argMultimap)) { + modCode = ParserUtil.parseModCode(argMultimap.getValue(PREFIX_MODULE).get()); + tutName = ParserUtil.parseTutorialName(argMultimap.getValue(PREFIX_TUTORIAL_NAME).get()); + } else if (validateIndexFormat(argMultimap)) { + tutIndex = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_INDEX).get()); + } else { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AddEventCommand.MESSAGE_USAGE)); + } + + String eventName = argMultimap.getValue(PREFIX_NAME).get(); + SimpleDateFormat dateFormatter = new SimpleDateFormat(Tutorial.DATE_FORMAT); + Date startTime; + Date endTime; + try { + startTime = dateFormatter.parse(argMultimap.getValue(PREFIX_START_DATE).get()); + endTime = dateFormatter.parse(argMultimap.getValue(PREFIX_END_DATE).get()); + } catch (java.text.ParseException e) { + throw new ParseException(Event.MESSAGE_CONSTRAINTS_START_END_TIME); + } + + return new AddEventCommand(modCode, tutName, tutIndex, eventName, startTime, endTime); + } + + /** + * Checks the argument multimap if it contains the correct combination of arguments. + * @return True if argument multimap contains the combination for the first format with modcode and tutorial name, + * else false. + */ + public static boolean validateModCodeTutNameFormat(ArgumentMultimap argMultimap) { + // modcode, tutorial name present without tutorial index - first format + return (arePrefixesPresent(argMultimap, PREFIX_MODULE, PREFIX_TUTORIAL_NAME, + PREFIX_NAME, PREFIX_START_DATE, PREFIX_END_DATE) + && arePrefixesAbsent(argMultimap, PREFIX_INDEX)); + } + + /** + * Checks the argument multimap if it contains the correct combination of arguments. + * @return True if argument multimap contains the combination for the second format with tutorial index, + * else false. + */ + public static boolean validateIndexFormat(ArgumentMultimap argMultimap) { + // tutorial index present without modcode or tutorial name - second format + return (arePrefixesPresent(argMultimap, PREFIX_INDEX, + PREFIX_NAME, PREFIX_START_DATE, PREFIX_END_DATE) + && arePrefixesAbsent(argMultimap, PREFIX_MODULE, PREFIX_TUTORIAL_NAME)); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + + /** + * Returns true if all of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesAbsent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isEmpty()); + } +} + diff --git a/src/main/java/seedu/tarence/logic/parser/event/DeleteEventCommandParser.java b/src/main/java/seedu/tarence/logic/parser/event/DeleteEventCommandParser.java new file mode 100644 index 00000000000..b98bc042655 --- /dev/null +++ b/src/main/java/seedu/tarence/logic/parser/event/DeleteEventCommandParser.java @@ -0,0 +1,137 @@ +package seedu.tarence.logic.parser.event; + +import static seedu.tarence.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_END_DATE; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_INDEX; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_MODULE; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_START_DATE; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_TUTORIAL_NAME; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.stream.Stream; + +import seedu.tarence.commons.core.index.Index; +import seedu.tarence.logic.commands.event.DeleteEventCommand; +import seedu.tarence.logic.parser.ArgumentMultimap; +import seedu.tarence.logic.parser.ArgumentTokenizer; +import seedu.tarence.logic.parser.Parser; +import seedu.tarence.logic.parser.ParserUtil; +import seedu.tarence.logic.parser.Prefix; +import seedu.tarence.logic.parser.exceptions.ParseException; +import seedu.tarence.model.module.ModCode; +import seedu.tarence.model.tutorial.Event; +import seedu.tarence.model.tutorial.TutName; +import seedu.tarence.model.tutorial.Tutorial; + +/** + * Parses input arguments and creates a new DeleteEventCommand object + */ +public class DeleteEventCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteEventCommand + * and returns a DeleteEventCommand object for execution. + * @throws ParseException if the user input does not match the expected formats for the module code. + */ + public DeleteEventCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, + PREFIX_MODULE, PREFIX_TUTORIAL_NAME, PREFIX_INDEX, PREFIX_NAME, + PREFIX_START_DATE, PREFIX_END_DATE); + + ModCode modCode = null; + TutName tutName = null; + Index tutIndex = null; + Index eventIndex = null; + String assignName = null; + Date startDate = null; + Date endDate = null; + if (validateModCodeTutNameFormat(argMultimap)) { + modCode = ParserUtil.parseModCode(argMultimap.getValue(PREFIX_MODULE).get()); + tutName = ParserUtil.parseTutorialName(argMultimap.getValue(PREFIX_TUTORIAL_NAME).get()); + } else if (validateTutIndexFormat(argMultimap)) { + tutIndex = ParserUtil.parseIndex(argMultimap.getAllValues(PREFIX_INDEX).get(0)); + } else { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteEventCommand.MESSAGE_USAGE)); + } + + if (validateEventFormat(argMultimap)) { + assignName = argMultimap.getValue(PREFIX_NAME).get(); + SimpleDateFormat dateFormatter = new SimpleDateFormat(Tutorial.DATE_FORMAT); + try { + startDate = dateFormatter.parse(argMultimap.getValue(PREFIX_START_DATE).get()); + endDate = dateFormatter.parse(argMultimap.getValue(PREFIX_END_DATE).get()); + } catch (java.text.ParseException e) { + throw new ParseException(Event.MESSAGE_CONSTRAINTS_START_END_TIME); + } + } else if (validateEventIndexFormat(argMultimap)) { + eventIndex = ParserUtil.parseIndex(argMultimap.getAllValues(PREFIX_INDEX).get(1)); + } else { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteEventCommand.MESSAGE_USAGE)); + } + + return new DeleteEventCommand(modCode, tutName, tutIndex, eventIndex, + assignName, startDate, endDate); + } + + /** + * Checks the argument multimap if it contains the correct combination of arguments. + * @return True if argument multimap contains the combination for the first format with modcode and tutorial name, + * else false. + */ + public static boolean validateModCodeTutNameFormat(ArgumentMultimap argMultimap) { + // modcode, tutorial name present without tutorial index - first format + return (arePrefixesPresent(argMultimap, PREFIX_MODULE, PREFIX_TUTORIAL_NAME) + && arePrefixesAbsent(argMultimap, PREFIX_INDEX)); + } + + /** + * Checks the argument multimap if it contains the correct combination of arguments. + * @return True if argument multimap contains the combination for the second format with tutorial index, + * else false. + */ + public static boolean validateTutIndexFormat(ArgumentMultimap argMultimap) { + return (arePrefixesPresent(argMultimap, PREFIX_INDEX) + && arePrefixesAbsent(argMultimap, PREFIX_MODULE, PREFIX_TUTORIAL_NAME)); + } + + /** + * Checks the argument multimap if it contains the correct combination of arguments. + * @return True if argument multimap contains the combination for the format with event name, + * max score, start and end date, else false. + */ + public static boolean validateEventFormat(ArgumentMultimap argMultimap) { + return (arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_START_DATE, PREFIX_END_DATE) + && argMultimap.getAllValues(PREFIX_INDEX).size() <= 1); + } + + /** + * Checks the argument multimap if it contains the correct combination of arguments. + * @return True if argument multimap contains the combination for the format with event index, + * else false. + */ + public static boolean validateEventIndexFormat(ArgumentMultimap argMultimap) { + return (argMultimap.getAllValues(PREFIX_INDEX).size() == 2 + && arePrefixesAbsent(argMultimap, PREFIX_NAME, PREFIX_START_DATE, PREFIX_END_DATE)); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + + /** + * Returns true if all of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesAbsent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isEmpty()); + } +} + diff --git a/src/main/java/seedu/tarence/logic/parser/event/EditEventCommandParser.java b/src/main/java/seedu/tarence/logic/parser/event/EditEventCommandParser.java new file mode 100644 index 00000000000..fd432590731 --- /dev/null +++ b/src/main/java/seedu/tarence/logic/parser/event/EditEventCommandParser.java @@ -0,0 +1,119 @@ +package seedu.tarence.logic.parser.event; + +import static seedu.tarence.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_END_DATE; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_INDEX; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_MODULE; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_START_DATE; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_TUTORIAL_NAME; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Optional; +import java.util.stream.Stream; + +import seedu.tarence.commons.core.index.Index; +import seedu.tarence.logic.commands.event.EditEventCommand; +import seedu.tarence.logic.parser.ArgumentMultimap; +import seedu.tarence.logic.parser.ArgumentTokenizer; +import seedu.tarence.logic.parser.Parser; +import seedu.tarence.logic.parser.ParserUtil; +import seedu.tarence.logic.parser.Prefix; +import seedu.tarence.logic.parser.exceptions.ParseException; +import seedu.tarence.model.module.ModCode; +import seedu.tarence.model.tutorial.Event; +import seedu.tarence.model.tutorial.TutName; +import seedu.tarence.model.tutorial.Tutorial; + +/** + * Parses input arguments and creates a new EditEventCommand object + */ +public class EditEventCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditEventCommand + * and returns a EditEventCommand object for execution. + * @throws ParseException if the user input does not match the expected formats for the module code. + */ + public EditEventCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, + PREFIX_MODULE, PREFIX_TUTORIAL_NAME, PREFIX_INDEX, PREFIX_NAME, + PREFIX_START_DATE, PREFIX_END_DATE); + + ModCode modCode = null; + TutName tutName = null; + Index tutIndex = null; + Index eventIndex = null; + String assignName = null; + Date startDate = null; + Date endDate = null; + if (validateModCodeTutNameFormat(argMultimap)) { + modCode = ParserUtil.parseModCode(argMultimap.getValue(PREFIX_MODULE).get()); + tutName = ParserUtil.parseTutorialName(argMultimap.getValue(PREFIX_TUTORIAL_NAME).get()); + eventIndex = ParserUtil.parseIndex(argMultimap.getAllValues(PREFIX_INDEX).get(1)); + } else if (validateTutIndexFormat(argMultimap)) { + tutIndex = ParserUtil.parseIndex(argMultimap.getAllValues(PREFIX_INDEX).get(0)); + eventIndex = ParserUtil.parseIndex(argMultimap.getAllValues(PREFIX_INDEX).get(0)); + } else { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EditEventCommand.MESSAGE_USAGE)); + } + + assignName = argMultimap.getValue(PREFIX_NAME).orElse(null); + SimpleDateFormat dateFormatter = new SimpleDateFormat(Tutorial.DATE_FORMAT); + try { + Optional startDateArg = argMultimap.getValue(PREFIX_START_DATE); + Optional endDateArg = argMultimap.getValue(PREFIX_END_DATE); + if (startDateArg.isPresent()) { + startDate = dateFormatter.parse(argMultimap.getValue(PREFIX_START_DATE).get()); + } + if (endDateArg.isPresent()) { + endDate = dateFormatter.parse(argMultimap.getValue(PREFIX_END_DATE).get()); + } + } catch (java.text.ParseException e) { + throw new ParseException(Event.MESSAGE_CONSTRAINTS_START_END_TIME); + } + + return new EditEventCommand(modCode, tutName, tutIndex, eventIndex, + assignName, startDate, endDate); + } + + /** + * Checks the argument multimap if it contains the correct combination of arguments. + * @return True if argument multimap contains the combination for the first format with modcode + * and tutorial name and event index, else false. + */ + public static boolean validateModCodeTutNameFormat(ArgumentMultimap argMultimap) { + // modcode, tutorial name present without tutorial index - first format + return (arePrefixesPresent(argMultimap, PREFIX_MODULE, PREFIX_TUTORIAL_NAME) + && argMultimap.getAllValues(PREFIX_INDEX).size() == 1); + } + + /** + * Checks the argument multimap if it contains the correct combination of arguments. + * @return True if argument multimap contains the combination for the second format with tutorial + * and event index, else false. + */ + public static boolean validateTutIndexFormat(ArgumentMultimap argMultimap) { + return (argMultimap.getAllValues(PREFIX_INDEX).size() == 2 + && arePrefixesAbsent(argMultimap, PREFIX_MODULE, PREFIX_TUTORIAL_NAME)); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + + /** + * Returns true if all of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesAbsent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isEmpty()); + } +} + diff --git a/src/main/java/seedu/tarence/logic/parser/event/ListEventsCommandParser.java b/src/main/java/seedu/tarence/logic/parser/event/ListEventsCommandParser.java new file mode 100644 index 00000000000..a3e8ac7d8c1 --- /dev/null +++ b/src/main/java/seedu/tarence/logic/parser/event/ListEventsCommandParser.java @@ -0,0 +1,89 @@ +package seedu.tarence.logic.parser.event; + +import static seedu.tarence.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_INDEX; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_MODULE; +import static seedu.tarence.logic.parser.CliSyntax.PREFIX_TUTORIAL_NAME; + +import java.util.stream.Stream; + +import seedu.tarence.commons.core.index.Index; +import seedu.tarence.logic.commands.event.ListEventsCommand; +import seedu.tarence.logic.parser.ArgumentMultimap; +import seedu.tarence.logic.parser.ArgumentTokenizer; +import seedu.tarence.logic.parser.Parser; +import seedu.tarence.logic.parser.ParserUtil; +import seedu.tarence.logic.parser.Prefix; +import seedu.tarence.logic.parser.exceptions.ParseException; +import seedu.tarence.model.module.ModCode; +import seedu.tarence.model.tutorial.TutName; + +/** + * Parses input arguments and creates a new ListEventsCommand object + */ +public class ListEventsCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ListEventsCommand + * and returns an ListEventsCommand object for execution. + * @throws ParseException if the user input does not match the expected formats for the module code. + */ + public ListEventsCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, + PREFIX_MODULE, PREFIX_TUTORIAL_NAME, PREFIX_INDEX); + + ModCode modCode = null; + TutName tutName = null; + Index tutIndex = null; + if (validateModCodeTutNameFormat(argMultimap)) { + modCode = ParserUtil.parseModCode(argMultimap.getValue(PREFIX_MODULE).get()); + tutName = ParserUtil.parseTutorialName(argMultimap.getValue(PREFIX_TUTORIAL_NAME).get()); + } else if (validateIndexFormat(argMultimap)) { + tutIndex = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_INDEX).get()); + } else { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ListEventsCommand.MESSAGE_USAGE)); + } + + return new ListEventsCommand(modCode, tutName, tutIndex); + } + + /** + * Checks the argument multimap if it contains the correct combination of arguments. + * @return True if argument multimap contains the combination for the first format with modcode and tutorial name, + * else false. + */ + public static boolean validateModCodeTutNameFormat(ArgumentMultimap argMultimap) { + // modcode, tutorial name present without tutorial index - first format + return (arePrefixesPresent(argMultimap, PREFIX_MODULE, PREFIX_TUTORIAL_NAME) + && arePrefixesAbsent(argMultimap, PREFIX_INDEX)); + } + + /** + * Checks the argument multimap if it contains the correct combination of arguments. + * @return True if argument multimap contains the combination for the second format with tutorial index, + * else false. + */ + public static boolean validateIndexFormat(ArgumentMultimap argMultimap) { + // tutorial index present without modcode or tutorial name - second format + return (arePrefixesPresent(argMultimap, PREFIX_INDEX) + && arePrefixesAbsent(argMultimap, PREFIX_MODULE, PREFIX_TUTORIAL_NAME)); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + + /** + * Returns true if all of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesAbsent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isEmpty()); + } +} + diff --git a/src/main/java/seedu/tarence/model/builder/EventBuilder.java b/src/main/java/seedu/tarence/model/builder/EventBuilder.java new file mode 100644 index 00000000000..2b4e848e12d --- /dev/null +++ b/src/main/java/seedu/tarence/model/builder/EventBuilder.java @@ -0,0 +1,64 @@ +package seedu.tarence.model.builder; + +import java.util.Date; + +import seedu.tarence.model.tutorial.Event; + + +/** + * A utility class to help with building Event objects. + */ +public class EventBuilder { + + public static final String DEFAULT_EVENT_NAME = "Event Name"; + public static final Date DEFAULT_START_TIME = new Date(0); + public static final Date DEFAULT_END_TIME = new Date(); + + private String eventName; + private Date startTime; + private Date endTime; + + public EventBuilder() { + this.eventName = DEFAULT_EVENT_NAME; + this.startTime = DEFAULT_START_TIME; + this.endTime = DEFAULT_END_TIME; + } + + /** + * Initializes the EventBuilder with the data of {@code eventToCopy}. + */ + public EventBuilder(Event eventToCopy) { + eventName = eventToCopy.eventName; + startTime = eventToCopy.startTime; + endTime = eventToCopy.endTime; + } + + /** + * Sets the {@code EventName} of the {@code Event} that we are building. + */ + public EventBuilder withEventName(String eventName) { + this.eventName = eventName; + return this; + } + + /** + * Sets the {@code StartTime} of the {@code Event} that we are building. + */ + public EventBuilder withStartTime(Date startTime) { + this.startTime = startTime; + return this; + } + + /** + * Sets the {@code EndTime} of the {@code Event} that we are building. + */ + public EventBuilder withEndTime(Date endTime) { + this.endTime = endTime; + return this; + } + + public Event build() { + return new Event(eventName, startTime, endTime); + } + +} diff --git a/src/main/java/seedu/tarence/model/builder/TimeTableBuilder.java b/src/main/java/seedu/tarence/model/builder/TimeTableBuilder.java index f96550c96c0..3088ea00551 100644 --- a/src/main/java/seedu/tarence/model/builder/TimeTableBuilder.java +++ b/src/main/java/seedu/tarence/model/builder/TimeTableBuilder.java @@ -12,7 +12,7 @@ /** - * A utility class to help with building Module objects. + * A utility class to help with building TimeTable objects. */ public class TimeTableBuilder { diff --git a/src/main/java/seedu/tarence/model/builder/TutorialBuilder.java b/src/main/java/seedu/tarence/model/builder/TutorialBuilder.java index d5993cd95c3..5929181328c 100644 --- a/src/main/java/seedu/tarence/model/builder/TutorialBuilder.java +++ b/src/main/java/seedu/tarence/model/builder/TutorialBuilder.java @@ -18,7 +18,7 @@ /** - * A utility class to help with building Module objects. + * A utility class to help with building Tutorial objects. */ public class TutorialBuilder { diff --git a/src/main/java/seedu/tarence/model/module/Module.java b/src/main/java/seedu/tarence/model/module/Module.java index 6461845dba9..b679541c5b6 100644 --- a/src/main/java/seedu/tarence/model/module/Module.java +++ b/src/main/java/seedu/tarence/model/module/Module.java @@ -2,6 +2,7 @@ import static seedu.tarence.commons.util.CollectionUtil.requireAllNonNull; +import java.util.Date; import java.util.List; import java.util.Objects; @@ -14,6 +15,8 @@ * Guarantees: details are present and not null, field values are validated, immutable. */ public class Module { + // TODO: Add to storage + private static Date semStart = null; // Identity fields protected final ModCode modCode; @@ -28,6 +31,17 @@ public Module(ModCode modCode, List tutorials) { this.tutorials = tutorials; } + public static Date getSemStart() { + return semStart; + } + + /** + * Sets the start of semester date. Does not modify event log of tutorials. + */ + public static void setSemStart(Date semStart) { + Module.semStart = semStart; + } + public ModCode getModCode() { return modCode; } diff --git a/src/main/java/seedu/tarence/model/tutorial/Assignment.java b/src/main/java/seedu/tarence/model/tutorial/Assignment.java index 50393c20c26..3d23d406b12 100644 --- a/src/main/java/seedu/tarence/model/tutorial/Assignment.java +++ b/src/main/java/seedu/tarence/model/tutorial/Assignment.java @@ -16,10 +16,9 @@ public class Assignment implements Comparable { "Assignment name should not be blank nor start/end with whitespace."; public static final String MESSAGE_CONSTRAINTS_MAX_SCORE = "Max score should be a non-negative integer"; - public static final String DATE_FORMAT = "dd-MM-yyyy HHmm"; public static final String MESSAGE_CONSTRAINTS_START_END_DATE = String.format("Dates should be of the format %s. " - + "Start date should be earlier than end date.", DATE_FORMAT); + + "Start date should be earlier than end date.", Tutorial.DATE_FORMAT); /* * The first character of the assignment name must not be a whitespace, diff --git a/src/main/java/seedu/tarence/model/tutorial/Event.java b/src/main/java/seedu/tarence/model/tutorial/Event.java new file mode 100644 index 00000000000..e54cf157d05 --- /dev/null +++ b/src/main/java/seedu/tarence/model/tutorial/Event.java @@ -0,0 +1,86 @@ +package seedu.tarence.model.tutorial; + +import static seedu.tarence.commons.util.AppUtil.checkArgument; +import static seedu.tarence.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Date; +import java.util.Objects; + +/** + * Represents an Event in a Tutorial. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Event implements Comparable { + + public static final String MESSAGE_CONSTRAINTS_EVENT_NAME = + "Name should be non-empty and should not start or end with white space."; + public static final String MESSAGE_CONSTRAINTS_START_END_TIME = + String.format("Time should be of the format %s. " + + "Start time should be earlier than end time.", Tutorial.DATE_FORMAT); + public static final String VALIDATION_REGEX = "^\\S+.*\\S+$"; + + public final String eventName; + public final Date startTime; + public final Date endTime; + + /** + * Every field must be present and not null. + */ + public Event(String eventName, Date startTime, Date endTime) { + requireAllNonNull(startTime, endTime); + checkArgument(isValidEventName(eventName), MESSAGE_CONSTRAINTS_EVENT_NAME); + checkArgument(isValidStartEndTime(startTime, endTime), MESSAGE_CONSTRAINTS_START_END_TIME); + this.eventName = eventName; + this.startTime = startTime; + this.endTime = endTime; + } + + /** + * Returns true if event name is valid. + */ + public static boolean isValidEventName(String name) { + return name.matches(VALIDATION_REGEX); + } + + /** + * Returns true if given parameters are valid. + */ + public static boolean isValidStartEndTime(Date startTime, Date endTime) { + return startTime.before(endTime); + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(eventName) + .append(" Start Time: ") + .append(startTime) + .append(" End Time: ") + .append(endTime); + + return stringBuilder.toString(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Event // instanceof handles nulls + && eventName.equals(((Event) other).eventName) + && startTime.equals(((Event) other).startTime) + && endTime.equals(((Event) other).endTime)); // state check + } + + @Override + public int compareTo(Event event1) { + return startTime.compareTo(event1.startTime) != 0 + ? startTime.compareTo(event1.startTime) + : endTime.compareTo(event1.endTime) != 0 + ? endTime.compareTo(event1.endTime) + : eventName.compareTo(event1.eventName); + } + + @Override + public int hashCode() { + return Objects.hash(eventName, startTime, endTime); + } +} diff --git a/src/main/java/seedu/tarence/model/tutorial/TimeTable.java b/src/main/java/seedu/tarence/model/tutorial/TimeTable.java index 1a34c198eac..f1ae00ecd61 100644 --- a/src/main/java/seedu/tarence/model/tutorial/TimeTable.java +++ b/src/main/java/seedu/tarence/model/tutorial/TimeTable.java @@ -5,10 +5,12 @@ import java.time.DayOfWeek; import java.time.Duration; import java.time.LocalTime; - +import java.util.Calendar; import java.util.Objects; import java.util.Set; +import seedu.tarence.model.module.Module; + /** * Represents a TimeTable for a Tutorial. Guarantees: details are present and not null, field * values are validated, immutable. @@ -50,6 +52,25 @@ public Duration getDuration() { return duration; } + /** + * Returns the current week. Assumes start of semester is present. + */ + public Week getCurrWeek() { + Calendar currCalendar = Calendar.getInstance(); + Calendar tempCalender = Calendar.getInstance(); + tempCalender.setTime(Module.getSemStart()); + tempCalender.add(Calendar.DAY_OF_MONTH, 7); + + for (int week = 2; week <= 14; week++) { + if (tempCalender.compareTo(currCalendar) <= 0) { + tempCalender.add(Calendar.DAY_OF_MONTH, 7); + } else { + return new Week(week - 1); + } + } + return new Week(13); + } + /** * Returns true if both timetables have the same identity or data fields. */ diff --git a/src/main/java/seedu/tarence/model/tutorial/Tutorial.java b/src/main/java/seedu/tarence/model/tutorial/Tutorial.java index ae3d2a16aa3..fefd42c0404 100644 --- a/src/main/java/seedu/tarence/model/tutorial/Tutorial.java +++ b/src/main/java/seedu/tarence/model/tutorial/Tutorial.java @@ -5,16 +5,22 @@ import java.time.DayOfWeek; import java.time.Duration; import java.time.LocalTime; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.TreeMap; +import java.util.concurrent.TimeUnit; import seedu.tarence.commons.core.index.Index; import seedu.tarence.model.module.ModCode; +import seedu.tarence.model.module.Module; import seedu.tarence.model.student.Student; +import seedu.tarence.model.tutorial.exceptions.DuplicateEventException; /** * Represents a Tutorial. @@ -22,6 +28,7 @@ */ public class Tutorial { public static final int NOT_SUBMITTED = -1; + public static final String DATE_FORMAT = "dd-MM-yyyy HHmm"; // Identity fields protected final TutName tutName; @@ -31,6 +38,8 @@ public class Tutorial { protected Attendance attendance; // TODO: Add assignments to storage protected Map> assignments; + // TODO: Add to storage + protected List eventLog; /** * Every field must be present and not null. @@ -38,13 +47,14 @@ public class Tutorial { public Tutorial(TutName tutName, DayOfWeek day, LocalTime startTime, Set weeks, Duration duration, List students, ModCode modCode) { - requireAllNonNull(tutName, day, startTime, weeks, students, modCode); + requireAllNonNull(tutName, day, startTime, weeks, duration, students, modCode); this.tutName = tutName; this.timeTable = new TimeTable(day, startTime, weeks, duration); this.students = students; this.modCode = modCode; this.attendance = new Attendance(weeks, students); this.assignments = new TreeMap<>(); + this.eventLog = new ArrayList<>(); } /** @@ -53,11 +63,7 @@ public Tutorial(TutName tutName, DayOfWeek day, LocalTime startTime, public Tutorial(TutName tutName, DayOfWeek day, LocalTime startTime, Set weeks, Duration duration, List students, ModCode modCode, Attendance attendance) { - requireAllNonNull(tutName, day, startTime, weeks, students, modCode); - this.tutName = tutName; - this.timeTable = new TimeTable(day, startTime, weeks, duration); - this.students = students; - this.modCode = modCode; + this(tutName, day, startTime, weeks, duration, students, modCode); this.attendance = attendance; } @@ -94,6 +100,61 @@ public Assignment getAssignment(Index assignIndex) throws IndexOutOfBoundsExcept throw new IndexOutOfBoundsException(); } + public Integer getHours() { + Integer hours = 0; + for (Event event : eventLog) { + hours += (int) TimeUnit.HOURS.convert( + event.endTime.getTime() - event.startTime.getTime(), + TimeUnit.MILLISECONDS); + } + return hours; + } + + /** + * Returns tutorials up to the current date as a list of events. + * If semester start date is specified, returns empty list. + */ + public List getTutorialasEvents() { + List tutorialEvents = new ArrayList<>(); + if (Module.getSemStart() == null) { + return tutorialEvents; + } + Week currWeek = timeTable.getCurrWeek(); + Calendar startEvent = new Calendar.Builder().setInstant(Module.getSemStart()).build(); + startEvent.set(Calendar.DAY_OF_WEEK, timeTable.getDay().getValue()); + startEvent.set(Calendar.HOUR, timeTable.getStartTime().getHour()); + startEvent.set(Calendar.MINUTE, timeTable.getStartTime().getMinute()); + Calendar endEvent = (Calendar) startEvent.clone(); + endEvent = new Calendar.Builder() + .setInstant(Date.from(endEvent.getTime().toInstant().plus(timeTable.getDuration()))) + .build(); + for (Week week : timeTable.getWeeks()) { + if (week.compareTo(currWeek) <= 0) { + Event tutEvent = new Event(tutName.tutName + " " + modCode.modCode, + startEvent.getTime(), + endEvent.getTime()); + tutorialEvents.add(tutEvent); + } + startEvent.add(Calendar.DAY_OF_MONTH, 7); + endEvent.add(Calendar.DAY_OF_MONTH, 7); + } + return tutorialEvents; + } + + /** + * Returns event log. If semester start date is specified, + * auto-adds completed tutorials into event log. + */ + public List getEventLog() { + if (Module.getSemStart() != null) { + List tutorialEvents = getTutorialasEvents(); + for (Event tutorialEvent : tutorialEvents) { + addEvent(tutorialEvent); + } + } + return eventLog; + } + /** * Adds a Student to a Tutorial. */ @@ -155,6 +216,32 @@ public void setAssignment(Assignment target, Assignment assignment) { deleteAssignment(target); } + /** + * Add an event. Checks for duplicates. + */ + public void addEvent(Event event) { + if (eventLog.contains(event)) { + throw new DuplicateEventException(); + } + eventLog.add(event); + eventLog.sort(Event::compareTo); + } + + + /** + * Remove an event based on its index in eventLog. + */ + public boolean deleteEvent(Event event) { + return eventLog.remove(event); + } + + /** + * Remove an event based on its index in eventLog. + */ + public Event deleteEvent(int index) { + return eventLog.remove(index); + } + /** * Returns true if both tutorials have the same identity or data fields. diff --git a/src/main/java/seedu/tarence/model/tutorial/exceptions/DuplicateEventException.java b/src/main/java/seedu/tarence/model/tutorial/exceptions/DuplicateEventException.java new file mode 100644 index 00000000000..8f9984e2bda --- /dev/null +++ b/src/main/java/seedu/tarence/model/tutorial/exceptions/DuplicateEventException.java @@ -0,0 +1,10 @@ +package seedu.tarence.model.tutorial.exceptions; + +/** + * Signals that the operation will result in duplicate Event. + */ +public class DuplicateEventException extends RuntimeException { + public DuplicateEventException() { + super("Operation would result in duplicate Events"); + } +}