From 6fa9241c071a20cf4c35fc2235344489bdf46b3d Mon Sep 17 00:00:00 2001 From: WayneKoo Date: Wed, 4 Apr 2018 13:20:30 +0800 Subject: [PATCH] upload collate (#158) --- collated/functional/SunBangjie.md | 1299 ++++++++++++++++++++ collated/functional/hzxcaryn.md | 841 +++++++++++++ collated/functional/koo1993.md | 1139 ++++++++++++++++++ collated/functional/shanwpf.md | 1231 +++++++++++++++++++ collated/test/SunBangjie.md | 50 + collated/test/hzxcaryn.md | 613 ++++++++++ collated/test/koo1993.md | 831 +++++++++++++ collated/test/shanwpf.md | 1839 +++++++++++++++++++++++++++++ 8 files changed, 7843 insertions(+) create mode 100644 collated/functional/SunBangjie.md create mode 100644 collated/functional/hzxcaryn.md create mode 100644 collated/functional/koo1993.md create mode 100644 collated/functional/shanwpf.md create mode 100644 collated/test/SunBangjie.md create mode 100644 collated/test/hzxcaryn.md create mode 100644 collated/test/koo1993.md create mode 100644 collated/test/shanwpf.md diff --git a/collated/functional/SunBangjie.md b/collated/functional/SunBangjie.md new file mode 100644 index 000000000000..69793eac7b6a --- /dev/null +++ b/collated/functional/SunBangjie.md @@ -0,0 +1,1299 @@ +# SunBangjie +###### \java\seedu\ptman\commons\encrypter\DataEncrypter.java +``` java +/** + * Provides tools to encrypts data + */ +public class DataEncrypter { + private static final String ALGO = "AES"; + private static final byte[] keyValue = + new byte[]{'T', 'h', 'e', 'B', 'e', 's', 't', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y'}; + + /** + * Encrypt a string with AES algorithm. + * + * @param data is a string + * @return the encrypted string + */ + public static String encrypt(String data) throws Exception { + if (data == null) { + return null; + } + Key key = generateKey(); + Cipher c = Cipher.getInstance(ALGO); + c.init(Cipher.ENCRYPT_MODE, key); + byte[] encVal = c.doFinal(data.getBytes()); + return Base64.getEncoder().encodeToString(encVal); + } + + /** + * Decrypt a string with AES algorithm. + * + * @param encryptedData is a string + * @return the decrypted string + */ + public static String decrypt(String encryptedData) throws Exception { + if (encryptedData == null) { + return null; + } + Key key = generateKey(); + Cipher c = Cipher.getInstance(ALGO); + c.init(Cipher.DECRYPT_MODE, key); + byte[] decordedValue = Base64.getDecoder().decode(encryptedData); + byte[] decValue = c.doFinal(decordedValue); + return new String(decValue); + } + + /** + * Generate a new encryption key. + */ + private static Key generateKey() throws Exception { + return new SecretKeySpec(keyValue, ALGO); + } +} +``` +###### \java\seedu\ptman\commons\events\model\OutletDataChangedEvent.java +``` java +/** Indicates the OutletInformation in the model has changed*/ +public class OutletDataChangedEvent extends BaseEvent { + public final OutletInformation data; + + public OutletDataChangedEvent(OutletInformation data) { + this.data = data; + } + + @Override + public String toString() { + return OutletDataChangedEvent.class.getSimpleName(); + } +} +``` +###### \java\seedu\ptman\commons\events\ui\AnnouncementChangedEvent.java +``` java +/** + * Represents an announcement change in the Outlet Information + */ +public class AnnouncementChangedEvent extends BaseEvent { + public final String information; + + public AnnouncementChangedEvent(String information) { + this.information = information; + } + + @Override + public String toString() { + return this.getClass().getSimpleName(); + } +} +``` +###### \java\seedu\ptman\logic\commands\AnnouncementCommand.java +``` java +/** + * Edits the announcement of outlet in the ptman. + */ +public class AnnouncementCommand extends UndoableCommand { + public static final String COMMAND_WORD = "announcement"; + public static final String COMMAND_ALIAS = "announce"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the announcement of the outlet " + + "in admin mode. Existing values will be overwritten by the input values.\n" + + "Example: " + COMMAND_WORD + " " + + "This is a new announcement."; + public static final String MESSAGE_EDIT_OUTLET_SUCCESS = "Announcement successfully updated."; + public static final String MESSAGE_EDIT_OUTLET_FAILURE = "New announcement cannot be empty." + + MESSAGE_USAGE; + + private Announcement announcement; + + public AnnouncementCommand(Announcement announcement) { + this.announcement = announcement; + } + + @Override + public CommandResult executeUndoableCommand() throws CommandException { + if (!model.isAdminMode()) { + throw new CommandException(MESSAGE_ACCESS_DENIED); + } + try { + OutletInformation editedOutlet = new OutletInformation(model.getOutletInformation()); + editedOutlet.setAnnouncement(announcement); + model.updateOutlet(editedOutlet); + EventsCenter.getInstance().post(new AnnouncementChangedEvent(editedOutlet.getAnnouncement().value)); + } catch (NoOutletInformationFieldChangeException e) { + throw new CommandException(MESSAGE_EDIT_OUTLET_FAILURE); + } + return new CommandResult(MESSAGE_EDIT_OUTLET_SUCCESS); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AnnouncementCommand)) { + return false; + } + + // state check + return announcement.equals(((AnnouncementCommand) other).announcement); + } +} +``` +###### \java\seedu\ptman\logic\commands\EditOutletCommand.java +``` java +/** + * Edits the details of outlet in the ptman. + */ +public class EditOutletCommand extends UndoableCommand { + + public static final String COMMAND_WORD = "editoutlet"; + public static final String COMMAND_ALIAS = "eo"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the outlet in admin " + + "mode. Existing values will be overwritten by the input values.\n" + + "Parameters: " + + "[" + PREFIX_OUTLET_NAME + "OUTLETNAME] " + + "[" + PREFIX_OPERATING_HOURS + "OPERATINGHOURS] " + + "[" + PREFIX_OUTLET_CONTACT + "CONTACT] " + + "[" + PREFIX_OUTLET_EMAIL + "EMAIL] " + + "Example: " + COMMAND_WORD + " " + + PREFIX_OUTLET_NAME + "AwesomeOutlet " + + PREFIX_OPERATING_HOURS + "09:00-22:00 " + + PREFIX_OUTLET_CONTACT + "91234567 " + + PREFIX_OUTLET_EMAIL + "AwesomeOutlet@gmail.com"; + + public static final String MESSAGE_EDIT_OUTLET_SUCCESS = "Outlet Information Edited."; + public static final String MESSAGE_EDIT_OUTLET_FAILURE = "At least one field must be specified.\n" + + MESSAGE_USAGE; + + private final OutletName name; + private final OperatingHours operatingHours; + private final OutletContact outletContact; + private final OutletEmail outletEmail; + + /** + * Constructor of EditOutletCommand + */ + public EditOutletCommand(OutletName name, OperatingHours operatingHours, OutletContact outletContact, + OutletEmail outletEmail) { + this.name = name; + this.operatingHours = operatingHours; + this.outletContact = outletContact; + this.outletEmail = outletEmail; + } + + @Override + public CommandResult executeUndoableCommand() throws CommandException { + if (!model.isAdminMode()) { + throw new CommandException(MESSAGE_ACCESS_DENIED); + } + try { + OutletInformation editedOutlet = new OutletInformation(model.getOutletInformation()); + editedOutlet.setOutletInformation(name, operatingHours, outletContact, outletEmail); + model.updateOutlet(editedOutlet); + EventsCenter.getInstance().post(new OutletInformationChangedEvent( + editedOutlet.getOperatingHours().toString(), + editedOutlet.getOutletContact().toString(), + editedOutlet.getOutletEmail().toString())); + EventsCenter.getInstance().post(new OutletNameChangedEvent(editedOutlet.getName().toString())); + } catch (NoOutletInformationFieldChangeException e) { + throw new CommandException(MESSAGE_EDIT_OUTLET_FAILURE); + } + return new CommandResult(MESSAGE_EDIT_OUTLET_SUCCESS); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditOutletCommand)) { + return false; + } + + // state check + EditOutletCommand e = (EditOutletCommand) other; + return outletContact.equals(e.outletContact) + && name.equals(e.name) + && operatingHours.equals(e.operatingHours) + && outletEmail.equals(e.outletEmail); + } +} +``` +###### \java\seedu\ptman\logic\commands\ViewOutletCommand.java +``` java +/** + * Displays the details of outlet in the ptman. + */ +public class ViewOutletCommand extends Command { + + public static final String COMMAND_WORD = "viewoutlet"; + public static final String COMMAND_ALIAS = "vo"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": display basic outlet information\n" + + "Example: " + COMMAND_WORD; + + @Override + public CommandResult execute() { + String messageToDisplay = "Outlet Name: " + model.getOutletInformation().getName() + " " + + model.getOutletInformationMessage() + " Announcement: " + + model.getOutletInformation().getAnnouncement(); + return new CommandResult(messageToDisplay); + } +} +``` +###### \java\seedu\ptman\logic\parser\AnnouncementCommandParser.java +``` java +/** + * Parses input arguments and creates a new AnnouncementCommand object + */ +public class AnnouncementCommandParser implements Parser { + + /** + * Constructs an AnnouncementCommand parser + * @param args + * @return + * @throws ParseException + */ + public AnnouncementCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException(AnnouncementCommand.MESSAGE_EDIT_OUTLET_FAILURE); + } + + return new AnnouncementCommand(new Announcement(trimmedArgs)); + } +} +``` +###### \java\seedu\ptman\logic\parser\EditOutletCommandParser.java +``` java +/** + * Parses input arguments and creates a new EditOutletCommand object + */ +public class EditOutletCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the EditOutletCommand + * and returns an EditOutletCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditOutletCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_OUTLET_NAME, PREFIX_OPERATING_HOURS, + PREFIX_OUTLET_CONTACT, PREFIX_OUTLET_EMAIL); + + OutletName outletName; + OperatingHours operatingHours; + OutletContact outletContact; + OutletEmail outletEmail; + + try { + outletName = ParserUtil.parseOutletName(argMultimap.getValue(PREFIX_OUTLET_NAME)).isPresent() + ? ParserUtil.parseOutletName(argMultimap.getValue(PREFIX_OUTLET_NAME)).get() + : null; + operatingHours = ParserUtil.parseOperatingHours(argMultimap.getValue(PREFIX_OPERATING_HOURS)).isPresent() + ? ParserUtil.parseOperatingHours(argMultimap.getValue(PREFIX_OPERATING_HOURS)).get() + : null; + outletContact = ParserUtil.parseOutletContact(argMultimap.getValue(PREFIX_OUTLET_CONTACT)).isPresent() + ? ParserUtil.parseOutletContact(argMultimap.getValue(PREFIX_OUTLET_CONTACT)).get() + : null; + outletEmail = ParserUtil.parseOutletEmail(argMultimap.getValue(PREFIX_OUTLET_EMAIL)).isPresent() + ? ParserUtil.parseOutletEmail(argMultimap.getValue(PREFIX_OUTLET_EMAIL)).get() + : null; + } catch (IllegalValueException ive) { + throw new ParseException(ive.getMessage(), ive); + } + + return new EditOutletCommand(outletName, operatingHours, outletContact, outletEmail); + } +} +``` +###### \java\seedu\ptman\model\ModelManager.java +``` java + @Override + public void updateOutlet(OutletInformation editedOutlet) throws NoOutletInformationFieldChangeException { + partTimeManager.updateOutlet(editedOutlet); + indicatePartTimeManagerChanged(); + } + + @Override + public String getOutletInformationMessage() { + return partTimeManager.getOutletInformationMessage(); + } + + @Override + public OutletInformation getOutletInformation() { + return partTimeManager.getOutletInformation(); + } + + //=========== Filtered Employee List Accessors ============================================================= + @Override + public void deleteTagFromAllEmployee(Tag tag) { + partTimeManager.removeTagFromAllEmployees(tag); + } + + /** + * Returns an unmodifiable view of the list of {@code Employee} backed by the internal list of + * {@code partTimeManager} + */ + @Override + public ObservableList getFilteredEmployeeList() { + return FXCollections.unmodifiableObservableList(filteredEmployees); + } + + @Override + public void updateFilteredEmployeeList(Predicate predicate) { + requireNonNull(predicate); + filteredEmployees.setPredicate(predicate); + } + + @Override + public boolean equals(Object obj) { + // short circuit if same object + if (obj == this) { + return true; + } + + // instanceof handles nulls + if (!(obj instanceof ModelManager)) { + return false; + } + + // state check + ModelManager other = (ModelManager) obj; + return partTimeManager.equals(other.partTimeManager) + && filteredEmployees.equals(other.filteredEmployees) + && filteredShifts.equals(other.filteredShifts); + } + +} +``` +###### \java\seedu\ptman\model\outlet\Announcement.java +``` java +/** + * Represents an announcement in PTMan. + * Guarantees: immutable; + */ +public class Announcement { + + public final String value; + + public Announcement(String announcement) { + requireNonNull(announcement); + this.value = announcement; + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Announcement // instanceof handles nulls + && this.value.equals(((Announcement) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} +``` +###### \java\seedu\ptman\model\outlet\exceptions\NoOutletInformationFieldChangeException.java +``` java +/** + * Signals that the set outlet information operation has all null fields + */ +public class NoOutletInformationFieldChangeException extends Exception { +} +``` +###### \java\seedu\ptman\model\outlet\OperatingHours.java +``` java +/** + * Represents operating hours in an outlet. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class OperatingHours { + + public static final String MESSAGE_OPERATING_HOUR_CONSTRAINTS = + "Operating hours must be in the format of START-END where START and END must be in " + + "the format of hh:mm and in terms of 24 hours. For example, 09:00-22:00"; + public static final String TIME24HOURS_PATTERN = "([01]?[0-9]|2[0-3]):[0-5][0-9]"; + public static final String OPERATING_HOUR_VALIDATION_REGEX = TIME24HOURS_PATTERN + "-" + + TIME24HOURS_PATTERN; + + public final String value; + private final LocalTime startTime; + private final LocalTime endTime; + + /** + * Constructs an {@code OperatingHours}. + * + * @param operatingHours A valid string of operating hours. + */ + public OperatingHours(String operatingHours) { + requireNonNull(operatingHours); + checkArgument(isValidOperatingHours(operatingHours), MESSAGE_OPERATING_HOUR_CONSTRAINTS); + String[] splitedTime = operatingHours.split("-"); + this.startTime = convertStringToLocalTime(splitedTime[0]); + this.endTime = convertStringToLocalTime(splitedTime[1]); + this.value = operatingHours; + } + + /** + * Converts a valid string of time to Local Time + */ + public static LocalTime convertStringToLocalTime(String time) { + String[] splitedTime = time.split(":"); + int hour = Integer.parseInt(splitedTime[0]); + int minute = Integer.parseInt(splitedTime[1]); + return LocalTime.of(hour, minute); + } + + public LocalTime getStartTime() { + return startTime; + } + + public LocalTime getEndTime() { + return endTime; + } + + /** + * Returns true if a given string is a valid operating hours of an outlet. + */ + public static boolean isValidOperatingHours(String test) { + return test.matches(OPERATING_HOUR_VALIDATION_REGEX); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getStartTime()) + .append("-") + .append(getEndTime()); + return builder.toString(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof OperatingHours // instanceof handles nulls + && this.startTime.equals(((OperatingHours) other).startTime) + && this.endTime.equals(((OperatingHours) other).endTime)); // state check + } + + @Override + public int hashCode() { + return Objects.hash(startTime, endTime); + } + +} +``` +###### \java\seedu\ptman\model\outlet\OutletContact.java +``` java +/** + * Represents an outlet's contact number in PTMan. + * Guarantees: immutable; is valid as declared in {@link #isValidOutletContact(String)} + */ +public class OutletContact { + + public static final String MESSAGE_OUTLET_CONTACT_CONSTRAINTS = + "Outlet contact numbers can only contain numbers, and should be at least 3 digits long"; + public static final String OUTLET_CONTACT_VALIDATION_REGEX = "\\d{3,}"; + public final String value; + + /** + * Constructs a {@code OutletContact}. + * + * @param phone A valid phone number. + */ + public OutletContact(String phone) { + requireNonNull(phone); + checkArgument(isValidOutletContact(phone), MESSAGE_OUTLET_CONTACT_CONSTRAINTS); + this.value = phone; + } + + /** + * Returns true if a given string is a valid employee phone number. + */ + public static boolean isValidOutletContact(String test) { + return test.matches(OUTLET_CONTACT_VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof OutletContact // instanceof handles nulls + && this.value.equals(((OutletContact) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} +``` +###### \java\seedu\ptman\model\outlet\OutletEmail.java +``` java +/** + * Represents an email of outlet in PTMan. + * Guarantees: immutable; is valid as declared in {@link #isValidOutletEmail(String)} + */ +public class OutletEmail { + + private static final String SPECIAL_CHARACTERS = "!#$%&'*+/=?`{|}~^.-"; + public static final String MESSAGE_OUTLET_EMAIL_CONSTRAINTS = "Outlet emails should be of the format " + + "local-part@domain and adhere to the following constraints:\n" + + "1. The local-part should only contain alphanumeric characters and these special " + + "characters, excluding the parentheses, (" + SPECIAL_CHARACTERS + ") .\n" + + "2. This is followed by a '@' and then a domain name. " + + "The domain name must:\n" + + " - be at least 2 characters long\n" + + " - start and end with alphanumeric characters\n" + + " - consist of alphanumeric characters, a period or a hyphen for the " + + "characters in between, if any."; + // alphanumeric and special characters + private static final String LOCAL_PART_REGEX = "^[\\w" + SPECIAL_CHARACTERS + "]+"; + // alphanumeric characters except underscore + private static final String DOMAIN_FIRST_CHARACTER_REGEX = "[^\\W_]"; + // alphanumeric, period and hyphen + private static final String DOMAIN_MIDDLE_REGEX = "[a-zA-Z0-9.-]*"; + private static final String DOMAIN_LAST_CHARACTER_REGEX = "[^\\W_]$"; + public static final String OUTLET_EMAIL_VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + + DOMAIN_FIRST_CHARACTER_REGEX + DOMAIN_MIDDLE_REGEX + DOMAIN_LAST_CHARACTER_REGEX; + + public final String value; + + /** + * Constructs an {@code OutletEmail}. + * + * @param outletEmail A valid outletEmail ptman. + */ + public OutletEmail(String outletEmail) { + requireNonNull(outletEmail); + checkArgument(isValidOutletEmail(outletEmail), MESSAGE_OUTLET_EMAIL_CONSTRAINTS); + this.value = outletEmail; + } + + /** + * Returns if a given string is a valid outlet email. + */ + public static boolean isValidOutletEmail(String test) { + return test.matches(OUTLET_EMAIL_VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof OutletEmail // instanceof handles nulls + && this.value.equals(((OutletEmail) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} +``` +###### \java\seedu\ptman\model\outlet\OutletInformation.java +``` java +/** + * Represents an outlet in PTMan. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class OutletInformation { + + public static final String DEFAULT_OUTLET_NAME = "DefaultOutlet"; + public static final String DEFAULT_OPERATING_HOURS = "09:00-22:00"; + public static final String DEFAULT_OUTLET_CONTACT = "91234567"; + public static final String DEFAULT_OUTLET_EMAIL = "DefaultOutlet@gmail.com"; + public static final String DEFAULT_ANNOUNCEMENT_MESSAGE = "No announcement. " + + "Please add new announcement with announcement command."; + + private OutletName name; + private Password masterPassword; + private OperatingHours operatingHours; + private OutletContact outletContact; + private OutletEmail outletEmail; + private Announcement announcement; + + /** + * Constructs an {@code OutletInformation}. + * + * @param name a valid outlet name + * @param operatingHours a valid operating hours + */ + public OutletInformation(OutletName name, OperatingHours operatingHours, OutletContact outletContact, + OutletEmail outletEmail, Password masterPassword, Announcement announcement) { + requireAllNonNull(name, operatingHours, outletContact, outletEmail, masterPassword, announcement); + this.name = name; + this.operatingHours = operatingHours; + this.outletContact = outletContact; + this.outletEmail = outletEmail; + this.masterPassword = masterPassword; + this.announcement = announcement; + } + + public OutletInformation(OutletInformation outlet) { + this.name = new OutletName(outlet.getName().toString()); + this.masterPassword = new Password(outlet.getMasterPassword()); + this.outletContact = new OutletContact(outlet.getOutletContact().toString()); + this.operatingHours = new OperatingHours(outlet.getOperatingHours().toString()); + this.outletEmail = new OutletEmail(outlet.getOutletEmail().toString()); + this.announcement = new Announcement(outlet.getAnnouncement().toString()); + } + + /** + * Default constructor with default values + */ + public OutletInformation() { + this.name = new OutletName(DEFAULT_OUTLET_NAME); + this.masterPassword = new Password(); + this.operatingHours = new OperatingHours(DEFAULT_OPERATING_HOURS); + this.outletContact = new OutletContact(DEFAULT_OUTLET_CONTACT); + this.outletEmail = new OutletEmail(DEFAULT_OUTLET_EMAIL); + this.announcement = new Announcement(DEFAULT_ANNOUNCEMENT_MESSAGE); + } + + public OutletName getName() { + return name; + } + + public Password getMasterPassword() { + return masterPassword; + } + + public OperatingHours getOperatingHours() { + return operatingHours; + } + + public OutletContact getOutletContact() { + return outletContact; + } + + public OutletEmail getOutletEmail() { + return outletEmail; + } + + /** + * Set the outlet password. + * only set after checking against outlet password. + * @param password + */ + public void setOutletPassword (Password password) { + requireNonNull(password); + this.masterPassword = password; + + } + public Announcement getAnnouncement() { + return announcement; + } + + public void setOutletInformation(OutletName name, OperatingHours operatingHours, OutletContact outletContact, + OutletEmail outletEmail) + throws NoOutletInformationFieldChangeException { + if (name == null && operatingHours == null && outletContact == null && outletEmail == null) { + throw new NoOutletInformationFieldChangeException(); + } + if (name != null) { + this.name = name; + } + if (operatingHours != null) { + this.operatingHours = operatingHours; + } + if (outletContact != null) { + this.outletContact = outletContact; + } + if (outletEmail != null) { + this.outletEmail = outletEmail; + } + } + + + public void setOutletInformation(OutletInformation outlet) throws NoOutletInformationFieldChangeException { + try { + requireAllNonNull(outlet.getName(), outlet.getOperatingHours(), outlet.getMasterPassword(), + outlet.getOutletEmail(), outlet.getOutletContact(), outlet.getAnnouncement()); + } catch (NullPointerException e) { + throw new NoOutletInformationFieldChangeException(); + } + this.name = outlet.getName(); + this.operatingHours = outlet.getOperatingHours(); + this.outletContact = outlet.getOutletContact(); + this.outletEmail = outlet.getOutletEmail(); + this.masterPassword = outlet.getMasterPassword(); + this.announcement = outlet.getAnnouncement(); + } + + public void setAnnouncement(Announcement announcement) { + this.announcement = announcement; + } + + + @Override + public boolean equals(Object other) { + return this == other + || (other instanceof OutletInformation + && ((OutletInformation) other).getName().equals(this.getName()) + && ((OutletInformation) other).getMasterPassword().equals(this.getMasterPassword()) + && ((OutletInformation) other).getOperatingHours().equals(this.getOperatingHours()) + && ((OutletInformation) other).getOutletContact().equals(this.getOutletContact()) + && ((OutletInformation) other).getOutletEmail().equals(this.getOutletEmail()) + && ((OutletInformation) other).getAnnouncement().equals(this.getAnnouncement())); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(name, masterPassword, operatingHours, outletContact, outletEmail, announcement); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("Operating Hours: ") + .append(getOperatingHours()) + .append(" Contact: ") + .append(getOutletContact()) + .append(" Email: ") + .append(getOutletEmail()); + return builder.toString(); + } + +} +``` +###### \java\seedu\ptman\model\outlet\OutletName.java +``` java +/** + * Represents an outlet's name in PTMan. + * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} + */ +public class OutletName { + + public static final String MESSAGE_NAME_CONSTRAINTS = + "Outlet name should only contain alphanumeric characters and spaces, and it should not be blank"; + + /* + * The first character of the name must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String NAME_VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; + + public final String fullName; + + /** + * Constructs a {@code Name}. + * + * @param name A valid name. + */ + public OutletName(String name) { + requireNonNull(name); + checkArgument(isValidName(name), MESSAGE_NAME_CONSTRAINTS); + this.fullName = name; + } + + /** + * Returns true if a given string is a valid outlet name. + */ + public static boolean isValidName(String test) { + return test.matches(NAME_VALIDATION_REGEX); + } + + + @Override + public String toString() { + return fullName; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof OutletName // instanceof handles nulls + && this.fullName.equals(((OutletName) other).fullName)); // state check + } + + @Override + public int hashCode() { + return fullName.hashCode(); + } + +} + +``` +###### \java\seedu\ptman\model\PartTimeManager.java +``` java + public void updateOutlet(OutletInformation editedOutlet) throws NoOutletInformationFieldChangeException { + outlet.setOutletInformation(editedOutlet); + } + + public String getOutletInformationMessage() { + return outlet.toString(); + } + + @Override + public OutletInformation getOutletInformation() { + return outlet; + } + + /** + * Updates the master tag list to include tags in {@code employee} that are not in the list. + * @return a copy of this {@code employee} such that every tag in this employee points to a Tag + * object in the master list. + */ + private Employee syncWithMasterTagList(Employee employee) { + final UniqueTagList employeeTags = new UniqueTagList(employee.getTags()); + tags.mergeFrom(employeeTags); + + // Create map with values = tag object references in the master list + // used for checking employee tag references + final Map masterTagObjects = new HashMap<>(); + tags.forEach(tag -> masterTagObjects.put(tag, tag)); + + // Rebuild the list of employee tags to point to the relevant tags in the master tag list. + final Set correctTagReferences = new HashSet<>(); + employeeTags.forEach(tag -> correctTagReferences.add(masterTagObjects.get(tag))); + return new Employee( + employee.getName(), + employee.getPhone(), + employee.getEmail(), + employee.getAddress(), + employee.getSalary(), + employee.getPassword(), + correctTagReferences + ); + } + + /** + * Removes {@code key} from this {@code PartTimeManager}. + * @throws EmployeeNotFoundException if the {@code key} is not in this {@code PartTimeManager}. + */ + public boolean removeEmployee(Employee key) throws EmployeeNotFoundException { + if (employees.remove(key)) { + removeUnusedTag(); + return true; + } else { + throw new EmployeeNotFoundException(); + } + } + +``` +###### \java\seedu\ptman\storage\OutletInformationStorage.java +``` java +/** + * Represents a storage for {@link seedu.ptman.model.outlet.OutletInformation}. + */ +public interface OutletInformationStorage { + /** + * Returns the file path of the data file. + */ + String getOutletInformationFilePath(); + + Optional readOutletInformation() throws DataConversionException, IOException; + + Optional readOutletInformation(String filePath) throws DataConversionException, IOException; + + void saveOutletInformation(OutletInformation outletInformation) throws IOException; + + void saveOutletInformation(OutletInformation outletInformation, String filePath) throws IOException; +} +``` +###### \java\seedu\ptman\storage\StorageManager.java +``` java + @Override + public String getOutletInformationFilePath() { + return outletInformationStorage.getOutletInformationFilePath(); + } + + @Override + public Optional readOutletInformation() throws DataConversionException, IOException { + return readOutletInformation(outletInformationStorage.getOutletInformationFilePath()); + } + + @Override + public Optional readOutletInformation(String filePath) + throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return outletInformationStorage.readOutletInformation(filePath); + } + + @Override + public void saveOutletInformation(OutletInformation outletInformation) throws IOException { + saveOutletInformation(outletInformation, outletInformationStorage.getOutletInformationFilePath()); + } + + @Override + public void saveOutletInformation(OutletInformation outletInformation, String filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + outletInformationStorage.saveOutletInformation(outletInformation, filePath); + } + + @Override + @Subscribe + public void handleOutletDataChangedEvent(OutletDataChangedEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event, "Local data changed, saving to file")); + try { + saveOutletInformation(event.data); + } catch (IOException e) { + raise(new DataSavingExceptionEvent(e)); + } + } +} +``` +###### \java\seedu\ptman\storage\XmlAdaptedOutletInformation.java +``` java +/** + * JAXB-friendly version of the OutletInformation. + */ +@XmlRootElement(name = "outletinformation") +public class XmlAdaptedOutletInformation { + + public static final String FAIL_MESSAGE = "Outlet's %s field is missing!"; + public static final String DECRYPT_FAIL_MESSAGE = "Cannot decrypt %s"; + + @XmlElement(required = true) + private String outletName; + @XmlElement(required = true) + private String operatingHours; + @XmlElement(required = true) + private String outletContact; + @XmlElement(required = true) + private String outletEmail; + @XmlElement(required = true) + private String passwordHash; + @XmlElement(required = true) + private String announcement; + + public XmlAdaptedOutletInformation() { + this.outletName = null; + this.operatingHours = null; + this.outletContact = null; + this.outletEmail = null; + this.passwordHash = null; + this.announcement = null; + } + + public XmlAdaptedOutletInformation(String outletName, String operatingHours, String outletContact, + String outletEmail, String passwordHash, String announcement) { + try { + this.outletName = encrypt(outletName); + this.operatingHours = encrypt(operatingHours); + this.outletContact = encrypt(outletContact); + this.outletEmail = encrypt(outletEmail); + this.passwordHash = encrypt(passwordHash); + this.announcement = encrypt(announcement); + } catch (Exception e) { + setAttributesFromStrings(outletName, operatingHours, outletContact, outletEmail, + passwordHash, announcement); + } + + } + + /** + * Converts a given OutletInformation into this class for JAXB use. + */ + public XmlAdaptedOutletInformation(OutletInformation source) { + this(); + try { + outletName = encrypt(source.getName().fullName); + operatingHours = encrypt(source.getOperatingHours().value); + outletContact = encrypt(source.getOutletContact().value); + outletEmail = encrypt(source.getOutletEmail().value); + passwordHash = encrypt(source.getMasterPassword().getPasswordHash()); + announcement = encrypt(source.getAnnouncement().value); + } catch (Exception e) { + setAttributesFromSource(source); + } + + } + + public void setAttributesFromStrings(String outletName, String operatingHours, String outletContact, + String outletEmail, String passwordHash, String announcement) { + this.outletName = outletName; + this.operatingHours = operatingHours; + this.outletContact = outletContact; + this.outletEmail = outletEmail; + this.passwordHash = passwordHash; + this.announcement = announcement; + } + + public void setAttributesFromSource(OutletInformation source) { + outletName = source.getName().fullName; + operatingHours = source.getOperatingHours().value; + outletContact = source.getOutletContact().value; + outletEmail = source.getOutletEmail().value; + passwordHash = source.getMasterPassword().getPasswordHash(); + announcement = source.getAnnouncement().value; + } + + private OutletName setOutletName() throws IllegalValueException { + String decryptedOutletName; + try { + decryptedOutletName = decrypt(this.outletName); + } catch (Exception e) { + throw new IllegalValueException(String.format(DECRYPT_FAIL_MESSAGE, OutletName.class.getSimpleName())); + } + if (decryptedOutletName == null) { + throw new IllegalValueException(String.format(FAIL_MESSAGE, OutletName.class.getSimpleName())); + } + if (!OutletName.isValidName(decryptedOutletName)) { + throw new IllegalValueException(OutletName.MESSAGE_NAME_CONSTRAINTS); + } + OutletName outletName = new OutletName(decryptedOutletName); + return outletName; + } + + private OperatingHours setOperatingHours() throws IllegalValueException { + String decryptedOperatingHours; + try { + decryptedOperatingHours = decrypt(this.operatingHours); + } catch (Exception e) { + throw new IllegalValueException(String.format(DECRYPT_FAIL_MESSAGE, + OperatingHours.class.getSimpleName())); + } + if (decryptedOperatingHours == null) { + throw new IllegalValueException(String.format(FAIL_MESSAGE, OperatingHours.class.getSimpleName())); + } + if (!OperatingHours.isValidOperatingHours(decryptedOperatingHours)) { + throw new IllegalValueException(OperatingHours.MESSAGE_OPERATING_HOUR_CONSTRAINTS); + } + OperatingHours operatingHours = new OperatingHours(decryptedOperatingHours); + return operatingHours; + } + + private OutletContact setOutletContact() throws IllegalValueException { + String decryptedOutletContact; + try { + decryptedOutletContact = decrypt(this.outletContact); + } catch (Exception e) { + throw new IllegalValueException(String.format(DECRYPT_FAIL_MESSAGE, + OutletContact.class.getSimpleName())); + } + if (decryptedOutletContact == null) { + throw new IllegalValueException(String.format(FAIL_MESSAGE, OutletContact.class.getSimpleName())); + } + if (!OutletContact.isValidOutletContact(decryptedOutletContact)) { + throw new IllegalValueException(OutletContact.MESSAGE_OUTLET_CONTACT_CONSTRAINTS); + } + OutletContact outletContact = new OutletContact(decryptedOutletContact); + return outletContact; + } + + private OutletEmail setOutletEmail() throws IllegalValueException { + String decryptedOutletEmail; + try { + decryptedOutletEmail = decrypt(this.outletEmail); + } catch (Exception e) { + throw new IllegalValueException(String.format(DECRYPT_FAIL_MESSAGE, + OutletEmail.class.getSimpleName())); + } + if (decryptedOutletEmail == null) { + throw new IllegalValueException(String.format(FAIL_MESSAGE, OutletEmail.class.getSimpleName())); + } + if (!OutletEmail.isValidOutletEmail(decryptedOutletEmail)) { + throw new IllegalValueException(OutletEmail.MESSAGE_OUTLET_EMAIL_CONSTRAINTS); + } + OutletEmail outletEmail = new OutletEmail(decryptedOutletEmail); + return outletEmail; + } + + private Password setPassword() throws IllegalValueException { + String decryptedPasswordHash; + try { + decryptedPasswordHash = decrypt(this.passwordHash); + } catch (Exception e) { + throw new IllegalValueException(String.format(DECRYPT_FAIL_MESSAGE, Password.class.getSimpleName())); + } + if (decryptedPasswordHash == null) { + throw new IllegalValueException(String.format(FAIL_MESSAGE, Password.class.getSimpleName())); + } + Password masterPassword = new Password(decryptedPasswordHash); + return masterPassword; + } + + private Announcement setAnnouncement() throws IllegalValueException { + String decryptedAnnouncement; + try { + decryptedAnnouncement = decrypt(this.announcement); + } catch (Exception e) { + throw new IllegalValueException(String.format(DECRYPT_FAIL_MESSAGE, + Announcement.class.getSimpleName())); + } + if (decryptedAnnouncement == null) { + throw new IllegalValueException(String.format(FAIL_MESSAGE, Announcement.class.getSimpleName())); + } + Announcement announcement = new Announcement(decryptedAnnouncement); + return announcement; + } + + /** + * Converts this jaxb-friendly adapted outlet object into the model's OutletInformation object + */ + public OutletInformation toModelType() throws IllegalValueException { + final OutletName outletName = setOutletName(); + final OperatingHours operatingHours = setOperatingHours(); + final OutletContact outletContact = setOutletContact(); + final OutletEmail outletEmail = setOutletEmail(); + final Password masterPassword = setPassword(); + final Announcement announcement = setAnnouncement(); + return new OutletInformation(outletName, operatingHours, outletContact, outletEmail, + masterPassword, announcement); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof XmlAdaptedOutletInformation)) { + return false; + } + + XmlAdaptedOutletInformation otherOutlet = (XmlAdaptedOutletInformation) other; + return Objects.equals(outletName, otherOutlet.outletName) + && Objects.equals(operatingHours, otherOutlet.operatingHours) + && Objects.equals(outletContact, otherOutlet.outletContact) + && Objects.equals(outletEmail, otherOutlet.outletEmail) + && Objects.equals(passwordHash, otherOutlet.passwordHash) + && Objects.equals(announcement, otherOutlet.announcement); + } +} +``` +###### \java\seedu\ptman\storage\XmlOutletFileStorage.java +``` java +/** + * Stores outlet information data in an XML file + */ +public class XmlOutletFileStorage { + + /** + * Saves the given parttimemanager data to the specified file. + */ + public static void saveDataToFile(File file, XmlAdaptedOutletInformation outletInformation) + throws FileNotFoundException { + try { + XmlUtil.saveDataToFile(file, outletInformation); + } catch (JAXBException e) { + throw new AssertionError("Unexpected exception " + e.getMessage()); + } + } + + /** + * Returns outlet information in the file or an empty outlet information + */ + public static XmlAdaptedOutletInformation loadDataFromSaveFile(File file) throws DataConversionException, + FileNotFoundException { + try { + return XmlUtil.getDataFromFile(file, XmlAdaptedOutletInformation.class); + } catch (JAXBException e) { + throw new DataConversionException(e); + } + } +} +``` +###### \java\seedu\ptman\storage\XmlOutletInformationStorage.java +``` java +/** + * A class to access OutletInformation data stored as an xml file on the hard disk. + */ +public class XmlOutletInformationStorage implements OutletInformationStorage { + + private static final Logger logger = LogsCenter.getLogger(XmlOutletInformationStorage.class); + + private String filePath; + + public XmlOutletInformationStorage(String filePath) { + this.filePath = filePath; + } + + public String getOutletInformationFilePath() { + return filePath; + } + + @Override + public Optional readOutletInformation() throws DataConversionException, IOException { + return readOutletInformation(filePath); + } + + /** + * Reads outlet information from storage + * @param filePath + * @return + * @throws DataConversionException + * @throws FileNotFoundException + */ + public Optional readOutletInformation(String filePath) throws DataConversionException, + FileNotFoundException { + requireNonNull(filePath); + + File outletInformationFile = new File(filePath); + + if (!outletInformationFile.exists()) { + logger.info("OutletInformation file " + outletInformationFile + " not found"); + return Optional.empty(); + } + + XmlAdaptedOutletInformation xmlAdaptedOutletInformation = XmlOutletFileStorage + .loadDataFromSaveFile(new File(filePath)); + try { + return Optional.of(xmlAdaptedOutletInformation.toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + outletInformationFile + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public void saveOutletInformation(OutletInformation outletInformation) throws IOException { + saveOutletInformation(outletInformation, filePath); + } + + /** + * Saves outlet information into storage + * @param outletInformation + * @param filePath + * @throws IOException + */ + public void saveOutletInformation(OutletInformation outletInformation, String filePath) throws IOException { + requireNonNull(outletInformation); + requireNonNull(filePath); + + File file = new File(filePath); + FileUtil.createIfMissing(file); + XmlOutletFileStorage.saveDataToFile(file, new XmlAdaptedOutletInformation(outletInformation)); + } +} +``` +###### \java\seedu\ptman\ui\OutletDetailsPanel.java +``` java + private void setAnnouncement(String text) { + announcement.setText(text); + } + +``` +###### \java\seedu\ptman\ui\OutletDetailsPanel.java +``` java + @Subscribe + public void handleAnnouncementChangedEvent(AnnouncementChangedEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event)); + Platform.runLater(() -> setAnnouncement(event.information)); + } +} +``` diff --git a/collated/functional/hzxcaryn.md b/collated/functional/hzxcaryn.md new file mode 100644 index 000000000000..748150804f3c --- /dev/null +++ b/collated/functional/hzxcaryn.md @@ -0,0 +1,841 @@ +# hzxcaryn +###### \java\seedu\ptman\commons\events\model\UserModeChangedEvent.java +``` java +/** + * Indicates that the user mode has changed. (Admin mode or not) + */ +public class UserModeChangedEvent extends BaseEvent { + + public final boolean isAdminMode; + + public UserModeChangedEvent(boolean isAdminMode) { + this.isAdminMode = isAdminMode; + } + + @Override + public String toString() { + return this.getClass().getSimpleName(); + } + +} +``` +###### \java\seedu\ptman\commons\events\ui\ExportTimetableAsImageAndEmailRequestEvent.java +``` java +/** + * An event requesting to export the timetable view as an image and email it to the given email + */ +public class ExportTimetableAsImageAndEmailRequestEvent extends BaseEvent { + + public final String filename; + public final Email email; + + public ExportTimetableAsImageAndEmailRequestEvent(String filename, Email email) { + this.filename = filename; + this.email = email; + } + + @Override + public String toString() { + return this.getClass().getSimpleName(); + } + +} +``` +###### \java\seedu\ptman\commons\events\ui\ExportTimetableAsImageRequestEvent.java +``` java +/** + * An event requesting to export the timetable view as an image locally + */ +public class ExportTimetableAsImageRequestEvent extends BaseEvent { + + public final String filename; + + public ExportTimetableAsImageRequestEvent(String filename) { + this.filename = filename; + } + + @Override + public String toString() { + return this.getClass().getSimpleName(); + } +} +``` +###### \java\seedu\ptman\commons\events\ui\OutletInformationChangedEvent.java +``` java +/** + * Represents an information change in the Outlet Information + */ +public class OutletInformationChangedEvent extends BaseEvent { + + public final String operatingHours; + public final String outletContact; + public final String outletEmail; + + public OutletInformationChangedEvent(String operatingHours, String outletContact, String outletEmail) { + this.operatingHours = operatingHours; + this.outletContact = outletContact; + this.outletEmail = outletEmail; + } + + @Override + public String toString() { + return this.getClass().getSimpleName(); + } + +} +``` +###### \java\seedu\ptman\commons\events\ui\OutletNameChangedEvent.java +``` java +/** + * Represents an information change in the Outlet Information + */ +public class OutletNameChangedEvent extends BaseEvent { + + public final String message; + + public OutletNameChangedEvent(String message) { + this.message = message; + } + + @Override + public String toString() { + return this.getClass().getSimpleName(); + } + +} +``` +###### \java\seedu\ptman\commons\services\EmailService.java +``` java + /** + * Send exported timetable image as an attachment to user + * @param email + * @param filename + * @throws MessagingException + */ + public void sendTimetableAttachment(String email, String filename) throws MessagingException { + Message message = new MimeMessage(session); + message.setFrom(new InternetAddress(senderEmailTimetable)); + message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(email)); + message.setSubject("[PTMan] My Timetable"); + + BodyPart messageBodyPart = new MimeBodyPart(); + messageBodyPart.setText("Dear Valued PTMan user,\n\nAttached is your exported timetable.\n" + + "Thank you for using PTMan and have a nice day!\n\nBest Regards,\nThe PTMan Team"); + + Multipart multipart = new MimeMultipart(); + multipart.addBodyPart(messageBodyPart); + + messageBodyPart = new MimeBodyPart(); + DataSource source = new FileDataSource(filename); + messageBodyPart.setDataHandler(new DataHandler(source)); + messageBodyPart.setFileName(filename); + + multipart.addBodyPart(messageBodyPart); + message.setContent(multipart); + + Transport.send(message); + } + +} +``` +###### \java\seedu\ptman\logic\commands\ExportCommand.java +``` java +/** + * Exports current timetable view as an image locally + */ +public class ExportCommand extends Command { + + public static final String COMMAND_WORD = "export"; + public static final String COMMAND_ALIAS = "exp"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Exports timetable as image. " + + "If email is stated, timetable image will be sent as an attachment to the stated email. " + + "Else, timetable image will be saved locally.\n" + + "Parameters: " + + "[" + PREFIX_EMAIL + "EMAIL]\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_EMAIL + "email@example.com"; + + public static final String MESSAGE_SAVE_SUCCESS = "Timetable is successfully exported!"; + public static final String MESSAGE_EMAIL_SUCCESS = "Timetable is successfully sent to your email!"; + + private final Email emailToSendImageTo; + + /** + * Creates an ExportCommand to save the timetable as image locally + */ + public ExportCommand() { + emailToSendImageTo = null; + } + + /** + * Creates an ExportCommand to send the timetable image to the user's email + * @param email + */ + public ExportCommand(Email email) { + requireNonNull(email); + emailToSendImageTo = email; + } + + @Override + public CommandResult execute() { + if (emailToSendImageTo != null) { + EventsCenter.getInstance().post(new ExportTimetableAsImageAndEmailRequestEvent( + TIMETABLE_IMAGE_FILE_NAME_DEFAULT + LocalDateTime.now().toString(), emailToSendImageTo)); + return new CommandResult(MESSAGE_EMAIL_SUCCESS); + } else { + EventsCenter.getInstance().post(new ExportTimetableAsImageRequestEvent(TIMETABLE_IMAGE_FILE_NAME_DEFAULT)); + return new CommandResult(MESSAGE_SAVE_SUCCESS); + } + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ExportCommand)) { + return false; + } + + // state check + ExportCommand e = (ExportCommand) other; + return emailToSendImageTo.equals(e.emailToSendImageTo); + } + +} +``` +###### \java\seedu\ptman\logic\commands\MainCommand.java +``` java +/** + * Returns back to main timetable view (of current week) in PTMan + */ +public class MainCommand extends Command { + + public static final String COMMAND_WORD = "main"; + + public static final String MESSAGE_SUCCESS = "Showing main timetable view."; + + @Override + public CommandResult execute() { + EventsCenter.getInstance().post(new EmployeePanelSelectionChangedEvent(null)); + return new CommandResult(MESSAGE_SUCCESS); + } +} +``` +###### \java\seedu\ptman\logic\parser\ExportCommandParser.java +``` java +/** + * Parses input arguments and creates a new ExportCommand object + */ +public class ExportCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ExportCommand + * and returns an ExportCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ExportCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_EMAIL); + + if (!arePrefixesPresent(argMultimap, PREFIX_EMAIL) && argMultimap.getPreamble().isEmpty()) { + return new ExportCommand(); + } else if (!argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ExportCommand.MESSAGE_USAGE)); + } + + try { + Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL)).get(); + return new ExportCommand(email); + } catch (IllegalValueException ive) { + throw new ParseException(ive.getMessage(), ive); + } + } +} +``` +###### \java\seedu\ptman\ui\AdminModeDisplay.java +``` java +/** + * Admin mode display of the app. Displays whether the user is in admin mode or not. + */ +public class AdminModeDisplay extends UiPart { + + public static final String LABEL_STYLE_CLASS_ADMIN = "label-admin-mode"; + public static final String LABEL_STYLE_CLASS_NON_ADMIN = "label-non-admin-mode"; + + private static final String adminModeText = "Admin Mode"; + private static final Logger logger = LogsCenter.getLogger(AdminModeDisplay.class); + private static final String FXML = "AdminModeDisplay.fxml"; + + @FXML + private Label adminModeDisplay; + + public AdminModeDisplay(boolean isAdminMode) { + super(FXML); + + adminModeDisplay.setText(adminModeText); + setLabelStyle(isAdminMode); + + registerAsAnEventHandler(this); + } + + private void setLabelStyle(boolean isAdminMode) { + ObservableList styleClass = this.adminModeDisplay.getStyleClass(); + if (isAdminMode) { + styleClass.remove(LABEL_STYLE_CLASS_NON_ADMIN); + styleClass.add(LABEL_STYLE_CLASS_ADMIN); + } else { + styleClass.remove(LABEL_STYLE_CLASS_ADMIN); + styleClass.add(LABEL_STYLE_CLASS_NON_ADMIN); + } + } + + @Subscribe + private void handleUserModeChangedEvent(UserModeChangedEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event)); + Platform.runLater(() -> setLabelStyle(event.isAdminMode)); + } + +} +``` +###### \java\seedu\ptman\ui\OutletDetailsPanel.java +``` java +/** + * The Outlet Panel of the App, which displays the Outlet name and details + */ +public class OutletDetailsPanel extends UiPart { + + private static final String FXML = "OutletDetailsPanel.fxml"; + + public final OutletInformation outlet; + + private final Logger logger = LogsCenter.getLogger(this.getClass()); + + @FXML + private Label outletNamePanelHeader; + + @FXML + private Label operatingHours; + + @FXML + private Label outletContact; + + @FXML + private Label outletEmail; + + @FXML + private Label announcement; + + + public OutletDetailsPanel(OutletInformation outlet) { + super(FXML); + this.outlet = outlet; + //outletInformation.setWrapText(true); + setOutletInformation(outlet.getOperatingHours().toString(), + outlet.getOutletContact().toString(), + outlet.getOutletEmail().toString()); + setOutletName(outlet.getName().toString()); + setAnnouncement(outlet.getAnnouncement().toString()); + + registerAsAnEventHandler(this); + } + + private void setOutletName(String name) { + outletNamePanelHeader.setText(name); + } + + private void setOutletInformation(String operatingHours, String outletContact, String outletEmail) { + this.operatingHours.setText(operatingHours + " "); + this.outletContact.setText(outletContact + " "); + this.outletEmail.setText(outletEmail); + } + +``` +###### \java\seedu\ptman\ui\OutletDetailsPanel.java +``` java + @Subscribe + private void handleOutletInformationChangedEvent(OutletInformationChangedEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event)); + Platform.runLater(() -> setOutletInformation(event.operatingHours, event.outletContact, event.outletEmail)); + } + + @Subscribe + private void handleOutletNameChangedEvent(OutletNameChangedEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event)); + Platform.runLater(() -> setOutletName(event.message)); + } + +``` +###### \java\seedu\ptman\ui\PtmanLogoDisplay.java +``` java +/** + * Displays the PTMan logo at the left side of the command box. + */ +public class PtmanLogoDisplay extends UiPart { + + private static final String imagePath = "/icons/ptman_logo_icon.png"; + private static final String FXML = "PtmanLogoDisplay.fxml"; + + @FXML + private ImageView ptmanLogoView; + + public PtmanLogoDisplay() { + super(FXML); + + Image ptmanLogo = new Image(imagePath); + ptmanLogoView.setImage(ptmanLogo); + setLogoSize(); + } + + /** + * Scale the logo image to the desired size + */ + private void setLogoSize() { + ptmanLogoView.setFitWidth(35); + ptmanLogoView.setPreserveRatio(true); + } +} +``` +###### \java\seedu\ptman\ui\TimetablePanel.java +``` java +/** + * Displays the GUI Timetable. + */ +public class TimetablePanel extends UiPart { + + public static final String TIMETABLE_IMAGE_FILE_NAME_DEFAULT = "MyTimetable"; + public static final String TIMETABLE_IMAGE_FILE_FORMAT = "png"; + + private static final int TIMETABLE_IMAGE_PIXEL_SCALE = 2; + private static final String FXML = "TimetableView.fxml"; + private static final int MAX_SLOTS_LEFT_RUNNING_OUT = 3; + + private static final Style ENTRY_GREEN_STYLE = Style.STYLE1; + private static final Style ENTRY_BLUE_STYLE = Style.STYLE2; + private static final Style ENTRY_YELLOW_STYLE = Style.STYLE3; + private static final Style ENTRY_RED_STYLE = Style.STYLE5; + private static final Style ENTRY_BROWN_STYLE = Style.STYLE7; + + private static Calendar timetableAvail; + private static Calendar timetableEmployee; + private static Calendar timetableFull; + private static Calendar timetableOthers; + private static Calendar timetableRunningOut; + + private final Logger logger = LogsCenter.getLogger(this.getClass()); + + @FXML + private CalendarView timetableView; + + private ObservableList shiftObservableList; + private OutletInformation outletInformation; + + + private Employee currentEmployee = null; + + protected TimetablePanel(ObservableList shiftObservableList, OutletInformation outletInformation) { + super(FXML); + + this.shiftObservableList = shiftObservableList; + this.outletInformation = outletInformation; + + timetableView = new CalendarView(); + setTimetableViewStyle(); + showRelevantViewsOnly(); + + // disable clicks on timetable view + timetableView.getWeekPage().setMouseTransparent(true); + + updateTimetableView(); + + registerAsAnEventHandler(this); + } + + public CalendarView getRoot() { + return this.timetableView; + } + + public static Calendar getTimetableAvail() { + return timetableAvail; + } + + public static Calendar getTimetableRunningOut() { + return timetableRunningOut; + } + + public static Calendar getTimetableFull() { + return timetableFull; + } + + public static Calendar getTimetableEmployee() { + return timetableEmployee; + } + + public static Calendar getTimetableOthers() { + return timetableOthers; + } + + /** + * Sets the style of the timetable view + */ + private void setTimetableViewStyle() { + ObservableList styleClass = timetableView.getStyleClass(); + styleClass.add("timetable-container"); + } + + /** + * Only show the parts of CalendarFX that we need. + */ + private void showRelevantViewsOnly() { + timetableView.showWeekPage(); + + timetableView.getWeekPage().setShowNavigation(false); + timetableView.getWeekPage().setShowDate(false); + timetableView.weekFieldsProperty().setValue(WeekFields.of(Locale.FRANCE)); // Start week from Monday + timetableView.setShowToday(true); + timetableView.setShowPrintButton(true); + timetableView.setShowAddCalendarButton(false); + timetableView.setShowSearchField(false); + timetableView.setShowToolBar(false); + timetableView.setShowPageSwitcher(false); + timetableView.setShowPageToolBarControls(false); + timetableView.setShowSearchResultsTray(false); + timetableView.setShowSourceTray(false); + timetableView.setShowSourceTrayButton(false); + timetableView.getWeekPage().getDetailedWeekView().setShowAllDayView(false); + } + + /** + * This ensures that the range of the times shown by the timetable view is constrained to the + * operating hours of the outlet. + * Also ensures that no scrolling is required to view the entire timetable. + */ + private void setTimetableRange() { + LocalTime startTime = outletInformation.getOperatingHours().getStartTime(); + LocalTime endTime = outletInformation.getOperatingHours().getEndTime(); + timetableView.setStartTime(startTime); + timetableView.setEndTime(endTime); + + DetailedWeekView detailedWeekView = timetableView.getWeekPage().getDetailedWeekView(); + detailedWeekView.setEarlyLateHoursStrategy(DayViewBase.EarlyLateHoursStrategy.HIDE); + detailedWeekView.setHoursLayoutStrategy(DayViewBase.HoursLayoutStrategy.FIXED_HOUR_COUNT); + detailedWeekView.setVisibleHours((int) ChronoUnit.HOURS.between(startTime, endTime)); + detailedWeekView.setShowScrollBar(false); + detailedWeekView.setEnableCurrentTimeMarker(false); + } + + private void setCurrentTime() { + timetableView.setToday(LocalDate.now()); + } + + /** + * Takes default outlet shifts and set timetable entries based on these shifts. + */ + private void setShifts() { + int index = 1; + for (Shift shift: shiftObservableList) { + LocalDate date = shift.getDate().getLocalDate(); + Interval timeInterval = new Interval(date, shift.getStartTime().getLocalTime(), + date, shift.getEndTime().getLocalTime()); + Entry shiftEntry = new Entry<>("SHIFT " + index + "\nSlots left: " + shift.getSlotsLeft() + + "/" + shift.getCapacity().getCapacity(), timeInterval); + setEntryType(shift, shiftEntry); + index++; + } + } + + /** + * Sets the entry type (aka the color) of the shift in the timetable + * @param shift + * @param shiftEntry + */ + private void setEntryType(Shift shift, Entry shiftEntry) { + Calendar entryType; + if (currentEmployee != null) { + entryType = getEntryTypeEmployee(shift); + } else { + entryType = getEntryTypeMain(shift); + } + entryType.addEntry(shiftEntry); + } + + /** + * Checks if currentEmployee is in input shift + * @param shift + * @return true if currentEmployee is in input shift, false if not. + */ + private boolean isCurrentEmployeeInShift(Shift shift) { + UniqueEmployeeList employees = shift.getUniqueEmployeeList(); + for (Employee employee : employees) { + if (employee.equals(currentEmployee)) { + return true; + } + } + return false; + } + + /** + * @return the entryType (a Calendar object) for the shift in the main timetable view, which reflects + * the color of the shift in the timetableView. + */ + private Calendar getEntryTypeMain(Shift shift) { + float ratio = (float) shift.getSlotsLeft() / (float) shift.getCapacity().getCapacity(); + if (ratio <= 0) { + return timetableFull; + } else if (ratio <= 0.5 || shift.getCapacity().getCapacity() < MAX_SLOTS_LEFT_RUNNING_OUT) { + return timetableRunningOut; + } else { + return timetableAvail; + } + } + + /** + * @return the entryType (a Calendar object) for the shift in the employee timetable view, which reflects + * the color of the shift in the timetableView. + */ + private Calendar getEntryTypeEmployee(Shift shift) { + if (isCurrentEmployeeInShift(shift)) { + return timetableEmployee; + } else { + return timetableOthers; + } + } + + /** + * Replaces the timetable view with a new timetable, with shifts taken by the employee being highlighted + * @param employee + */ + private void loadEmployeeTimetable(Employee employee) { + currentEmployee = employee; + updateTimetableView(); + } + + private void loadMainTimetable() { + currentEmployee = null; + updateTimetableView(); + } + + /** + * Replaces timetableView with a new timetable with updated shift and outlet information + */ + private void updateTimetableView() { + setCurrentTime(); + timetableView.getCalendarSources().clear(); + CalendarSource calendarSource = new CalendarSource("Shifts"); + addCalendars(calendarSource); + + setShifts(); + timetableView.getCalendarSources().add(calendarSource); + + setTimetableRange(); + } + + /** + * Initialises all the Calendar objects + */ + private void initialiseEntries() { + timetableAvail = new Calendar("Available"); + timetableRunningOut = new Calendar("Running Out"); + timetableFull = new Calendar("Full"); + timetableEmployee = new Calendar("Employee's shift"); + timetableOthers = new Calendar("Other shifts"); + } + + /** + * Sets the color styles of the entries + */ + private void setEntryStyles() { + timetableAvail.setStyle(ENTRY_GREEN_STYLE); + timetableRunningOut.setStyle(ENTRY_YELLOW_STYLE); + timetableFull.setStyle(ENTRY_RED_STYLE); + timetableEmployee.setStyle(ENTRY_BLUE_STYLE); + timetableOthers.setStyle(ENTRY_BROWN_STYLE); + } + + /** + * Adds all relevant Calendars (entryTypes) to its source + */ + private void addCalendars(CalendarSource calendarSource) { + initialiseEntries(); + setEntryStyles(); + calendarSource.getCalendars().addAll(timetableAvail, timetableRunningOut, timetableFull, + timetableEmployee, timetableOthers); + } + + /** + * Takes a snapshot of the timetable view + */ + private WritableImage takeSnapshot() { + WritableImage timetableWritableImage = new WritableImage( + (int) (TIMETABLE_IMAGE_PIXEL_SCALE * timetableView.getWidth()), + (int) (TIMETABLE_IMAGE_PIXEL_SCALE * timetableView.getHeight())); + SnapshotParameters spa = new SnapshotParameters(); + spa.setTransform(Transform.scale(TIMETABLE_IMAGE_PIXEL_SCALE, TIMETABLE_IMAGE_PIXEL_SCALE)); + WritableImage snapshot = timetableView.snapshot(spa, timetableWritableImage); + return snapshot; + } + + /** + * Exports timetable as image and save it locally + */ + private void exportTimetableAsImage(String filename) { + File imageFile = new File("." + File.separator + filename + "." + TIMETABLE_IMAGE_FILE_FORMAT); + try { + ImageIO.write(SwingFXUtils.fromFXImage(takeSnapshot(), null), TIMETABLE_IMAGE_FILE_FORMAT, imageFile); + } catch (IOException e) { + logger.warning("Error taking snapshot of timetable."); + } + } + + /** + * Exports timetable as image and email it + * @param email + */ + private void exportTimetableAsImageAndEmail(String filename, Email email) { + String pathName = "." + File.separator + filename + "." + TIMETABLE_IMAGE_FILE_FORMAT; + File imageFile = new File(pathName); + try { + ImageIO.write(SwingFXUtils.fromFXImage(takeSnapshot(), null), TIMETABLE_IMAGE_FILE_FORMAT, imageFile); + EmailService emailService = EmailService.getInstance(); + emailService.sendTimetableAttachment(email.toString(), pathName); + } catch (IOException e) { + logger.warning("Error taking snapshot of timetable."); + } catch (MessagingException e) { + logger.warning("Error sending timetable as email."); + } + + try { + Files.deleteIfExists(Paths.get(pathName)); + } catch (IOException e) { + logger.warning("Error deleting exported and emailed timetable image."); + } + } + + @Subscribe + private void handleShiftChangedEvent(PartTimeManagerChangedEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event) + ": Updating timetable view...."); + Platform.runLater(() -> updateTimetableView()); + } + + @Subscribe + private void handleEmployeePanelSelectionChangedEvent(EmployeePanelSelectionChangedEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event)); + Platform.runLater(() -> { + if (event.hasNewSelection()) { + loadEmployeeTimetable(event.getNewSelection().employee); + } else { + loadMainTimetable(); + } + }); + } + + @Subscribe + private void handleExportTimetableAsImageRequestEvent(ExportTimetableAsImageRequestEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event) + ": Exporting timetable as image...."); + Platform.runLater(() -> exportTimetableAsImage(event.filename)); + } + + @Subscribe + private void handleExportTimetableAsImageAndEmailRequestEvent(ExportTimetableAsImageAndEmailRequestEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event) + + ": Exporting timetable as image to send email...."); + Platform.runLater(() -> exportTimetableAsImageAndEmail(event.filename, event.email)); + } + +} +``` +###### \resources\view\AdminModeDisplay.fxml +``` fxml + + +``` +###### \resources\view\MainWindow.fxml +``` fxml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` +###### \resources\view\OutletDetailsPanel.fxml +``` fxml + + + + +``` +###### \resources\view\PtmanLogoDisplay.fxml +``` fxml + + + +``` +###### \resources\view\TimetableView.fxml +``` fxml + + + + + +``` diff --git a/collated/functional/koo1993.md b/collated/functional/koo1993.md new file mode 100644 index 000000000000..fb9a36f97f64 --- /dev/null +++ b/collated/functional/koo1993.md @@ -0,0 +1,1139 @@ +# koo1993 +###### \java\seedu\ptman\commons\services\EmailService.java +``` java +/** + * A email services that sends email to user + */ +public class EmailService { + private static EmailService singleInstance = new EmailService(); + + private final String senderEmailReset = "ptmanager.reset@gmail.com"; + private final String senderEmailTimetable = "ptmanager.timetable@gmail.com"; + private final String password = "DEFAULT!1"; + + + private Session session; + + + private EmailService() { + Properties props = new Properties(); + props.put("mail.smtp.auth", "true"); + props.put("mail.smtp.starttls.enable", "true"); + props.put("mail.smtp.host", "smtp.gmail.com"); + props.put("mail.smtp.port", "587"); + + session = Session.getInstance(props, + new javax.mail.Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(senderEmailReset, password); + } + }); + } + + public static synchronized EmailService getInstance() { + return singleInstance; + } + + /** + * Send a standard reset password message over to user + * @param name + * @param email + * @param newPassword + * @throws MessagingException + */ + public void sendResetPasswordMessage(String name, String email, String newPassword) throws MessagingException { + Message message = new MimeMessage(session); + message.setFrom(new InternetAddress(senderEmailReset)); + message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(email)); + message.setSubject("[PTMan] Password Reset"); + message.setText("Hi " + name + ", \n\n" + "Your password has been reset: " + newPassword + "\n\n" + + "Please reset your password immediately in PTMan.\n\nBest Regards,\nThe PTMan Team"); + Transport.send(message); + } + +``` +###### \java\seedu\ptman\logic\commands\ChangeAdminPasswordCommand.java +``` java +/** + * Change password of the outlet in PTMan. + */ +public class ChangeAdminPasswordCommand extends Command { + + public static final String COMMAND_WORD = "changeadminpw"; + public static final String COMMAND_ALIAS = "cap"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + " pw/currentPassword " + + "pw/NewPassword " + "pw/ConfirmNewPassword "; + + + public static final String MESSAGE_INVALID_CONFIMREDPASSWORD = "New password entered are not the same"; + public static final String MESSAGE_SUCCESS = "your master password is changed."; + + + private final ArrayList passwords; + + + /** + * @param passwords should contain 3 password String in the sequence of: + * confirmed password, new password, confirmed new password + */ + public ChangeAdminPasswordCommand(ArrayList passwords) { + requireNonNull(passwords); + this.passwords = passwords; + } + + @Override + public CommandResult execute() throws CommandException { + + checkConfirmedPassword(passwords.get(1), passwords.get(2)); + + Password enteredPassword = parsePassword(passwords.get(0)); + Password newPassword = parsePassword(passwords.get(1)); + + if (!model.isAdminPassword(enteredPassword) + && !model.isCorrectTempPwd(model.getOutletInformation(), enteredPassword)) { + throw new InvalidPasswordException(); + } + + model.setAdminPassword(newPassword); + return new CommandResult(MESSAGE_SUCCESS); + } + + + /** + * Check confirmed new password with new password + * @throws CommandException if both password are not the same + */ + private void checkConfirmedPassword(String newPassword, String confirmedPassword) throws CommandException { + if (!newPassword.equals(confirmedPassword)) { + throw new CommandException(MESSAGE_INVALID_CONFIMREDPASSWORD); + } + } + + + + @Override + public void setData(Model model, CommandHistory history, UndoRedoStack undoRedoStack) { + super.setData(model, history, undoRedoStack); + undoRedoStack.resetRedoUndoStack(); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ChangeAdminPasswordCommand)) { + return false; + } + + // state check + ChangeAdminPasswordCommand e = (ChangeAdminPasswordCommand) other; + return passwords.equals(e.passwords); + } + + /** + * Parses a {@code String password} into an {@code Password}. + * Leading and trailing whitespaces will be trimmed. + * + */ + public static Password parsePassword(String password) { + requireNonNull(password); + String trimmedPassword = password.trim(); + Password newPassword = new Password(); + newPassword.createPassword(trimmedPassword); + return newPassword; + } +} +``` +###### \java\seedu\ptman\logic\commands\ChangePasswordCommand.java +``` java +/** + * Change password of an existing employee in PTMan. + */ +public class ChangePasswordCommand extends Command { + + public static final String COMMAND_WORD = "changepw"; + public static final String COMMAND_ALIAS = "cp"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + " INDEX" + " pw/currentPassword " + + "pw/NewPassword " + "pw/ConfirmNewPassword "; + + + public static final String MESSAGE_INVALID_CONFIMREDPASSWORD = "New password entered are not the same"; + public static final String MESSAGE_SUCCESS = "%1$s, your password is changed."; + + + private final Index index; + private final ArrayList passwords; + + private Employee employeeToEdit; + private Employee editedEmployee; + + /** + * @param index of the employee in the filtered employee list to edit + * @param passwords should contain 3 password String in the sequence of: + * confirmed password, new password, confirmed new password + */ + public ChangePasswordCommand(Index index, ArrayList passwords) { + requireNonNull(index); + requireNonNull(passwords); + this.index = index; + this.passwords = passwords; + } + + @Override + public CommandResult execute() throws CommandException { + + checkConfirmedPassword(passwords.get(1), passwords.get(2)); + + List lastShownList = model.getFilteredEmployeeList(); + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_EMPLOYEE_DISPLAYED_INDEX); + } + + Password currentPassword = parsePassword(passwords.get(0)); + employeeToEdit = lastShownList.get(index.getZeroBased()); + + checkAuthenticity(currentPassword, employeeToEdit); + + editedEmployee = createNewPasswordEmployee(employeeToEdit, parsePassword(passwords.get(1))); + + try { + model.updateEmployee(employeeToEdit, editedEmployee); + } catch (DuplicateEmployeeException dpe) { + throw new CommandException("MESSAGE_DUPLICATE_EMPLOYEE"); + } catch (EmployeeNotFoundException pnfe) { + throw new AssertionError("The target employee cannot be missing"); + } + + return new CommandResult(String.format(MESSAGE_SUCCESS, editedEmployee.getName())); + } + + /** + * Check password given is employee's or it's temp password. + * @param currentPassword + * @throws InvalidPasswordException if password is invalid + */ + private void checkAuthenticity(Password currentPassword, Employee employeeToEdit) + throws InvalidPasswordException { + if (!employeeToEdit.isCorrectPassword(currentPassword) + && !model.isCorrectTempPwd(employeeToEdit, currentPassword)) { + throw new InvalidPasswordException(); + } + } + + /** + * Check confirmed new password with new password + * @throws CommandException if both password are not the same + */ + private void checkConfirmedPassword(String newPassword, String confirmedPassword) throws CommandException { + if (!newPassword.equals(confirmedPassword)) { + throw new CommandException(MESSAGE_INVALID_CONFIMREDPASSWORD); + } + } + + + /** + * Creates and returns a {@code Employee} with the details of {@code employeeToEdit} + * edited with {@code editEmployeeDescriptor}. + */ + private static Employee createNewPasswordEmployee(Employee employeeToEdit, + Password password) { + assert employeeToEdit != null; + + Name name = employeeToEdit.getName(); + Phone phone = employeeToEdit.getPhone(); + Email email = employeeToEdit.getEmail(); + Address address = employeeToEdit.getAddress(); + Salary salary = employeeToEdit.getSalary(); + Set tags = employeeToEdit.getTags(); + Password updatedPassword = password; + return new Employee(name, phone, email, address, salary, updatedPassword, tags); + } + + @Override + public void setData(Model model, CommandHistory history, UndoRedoStack undoRedoStack) { + super.setData(model, history, undoRedoStack); + undoRedoStack.resetRedoUndoStack(); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ChangePasswordCommand)) { + return false; + } + + // state check + ChangePasswordCommand e = (ChangePasswordCommand) other; + return index.equals(e.index) + && passwords.equals(e.passwords) + && Objects.equals(employeeToEdit, e.employeeToEdit) + && Objects.equals(editedEmployee, e.editedEmployee); + } + + /** + * Parses a {@code String password} into an {@code Password}. + * Leading and trailing whitespaces will be trimmed. + * + */ + public static Password parsePassword(String password) { + requireNonNull(password); + String trimmedPassword = password.trim(); + Password newPassword = new Password(); + newPassword.createPassword(trimmedPassword); + return newPassword; + } +} +``` +###### \java\seedu\ptman\logic\commands\LogInAdminCommand.java +``` java +/** + * Login command for admin mode access. + */ +public class LogInAdminCommand extends Command { + + public static final String COMMAND_WORD = "login"; + + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Log in to access admin command.\n" + + "Example: " + COMMAND_WORD + " pw/AdminPassword"; + + public static final String MESSAGE_LOGGEDIN = "You are already logged in"; + + public static final String MESSAGE_SUCCESS = "You are now in admin mode. \n" + + "please remember to log out after amending all the data."; + + + private final Password toCheck; + + public LogInAdminCommand(Password password) { + requireNonNull(password); + toCheck = password; + } + + @Override + public CommandResult execute() throws CommandException { + + if (model.isAdminMode()) { + return new CommandResult(MESSAGE_LOGGEDIN); + } + + if (!model.setTrueAdminMode(toCheck)) { + throw new InvalidPasswordException(); + } + + EventsCenter.getInstance().post(new UserModeChangedEvent(true)); + return new CommandResult(MESSAGE_SUCCESS); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof LogInAdminCommand // instanceof handles nulls + && toCheck.equals(((LogInAdminCommand) other).toCheck)); + } +} +``` +###### \java\seedu\ptman\logic\commands\LogOutAdminCommand.java +``` java +/** + * logout command from admin mode. + */ +public class LogOutAdminCommand extends Command { + + public static final String COMMAND_WORD = "logout"; + + public static final String MESSAGE_SUCCESS = "You have logged out from admin mode"; + + public static final String MESSAGE_LOGGEDOUT = "You already logged out"; + + + @Override + public CommandResult execute() { + if (!model.isAdminMode()) { + return new CommandResult(MESSAGE_LOGGEDOUT); + } + model.setFalseAdminMode(); + EventsCenter.getInstance().post(new UserModeChangedEvent(false)); + return new CommandResult(MESSAGE_SUCCESS); + } +} +``` +###### \java\seedu\ptman\logic\commands\ResetAdminPasswordCommand.java +``` java +/** + * Reset password for the outlet in PTMan. + */ +public class ResetAdminPasswordCommand extends Command { + + public static final String COMMAND_WORD = "resetadminpw"; + public static final String COMMAND_ALIAS = "rap"; + + public static final String MESSAGE_USAGE = COMMAND_WORD; + public static final String MESSAGE_SUCCESS = "Email with the new password is sent to you at: %1$s"; + + public static final String MESSAGE_SENTFAIL = "Reset password Fail, please check your internet connection"; + public static final String MESSAGE_EMAILFAIL = "No such email address %1$s"; + + + @Override + public CommandResult execute() throws CommandException { + + Password newPassword; + OutletInformation outletRequested = model.getOutletInformation(); + + try { + newPassword = createAndSendRandomPassword(outletRequested); + } catch (AddressException ae) { + System.out.println(ae.toString()); + return new CommandResult(String.format(MESSAGE_EMAILFAIL, outletRequested.getOutletEmail())); + } catch (MessagingException e) { + System.out.println(e.toString()); + return new CommandResult(MESSAGE_SENTFAIL); + } + + model.storeResetPassword(outletRequested, newPassword); + return new CommandResult(String.format(MESSAGE_SUCCESS, outletRequested.getOutletEmail())); + } + + /** + * Generate random password with 8 characters + * @return Password with the new password. + */ + private Password createAndSendRandomPassword(OutletInformation outlet) throws MessagingException { + String newPassword = Password.generateRandomPassword(); + + EmailService email = EmailService.getInstance(); + email.sendResetPasswordMessage(outlet.getName().toString(), + outlet.getOutletEmail().toString(), newPassword); + + return parsePassword(newPassword); + } + + + @Override + public boolean equals(Object other) { + // instanceof handles nulls + return (other instanceof ResetAdminPasswordCommand); + } + + /** + * Parses a {@code String password} into an {@code Password}. + * Leading and trailing whitespaces will be trimmed. + */ + public static Password parsePassword(String password) { + requireNonNull(password); + String trimmedPassword = password.trim(); + Password newPassword = new Password(); + newPassword.createPassword(trimmedPassword); + return newPassword; + } + +} +``` +###### \java\seedu\ptman\logic\commands\ResetPasswordCommand.java +``` java +/** + * Reset password for an existing employee in PTMan. + */ +public class ResetPasswordCommand extends Command { + + public static final String COMMAND_WORD = "resetpw"; + public static final String COMMAND_ALIAS = "rp"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + " INDEX"; + public static final String MESSAGE_SUCCESS = "Email with the new password is sent to you at: %1$s"; + + public static final String MESSAGE_SENTFAIL = "Reset password Fail, please check your internet connection"; + public static final String MESSAGE_EMAILFAIL = "No such email address %1$s"; + + private final Index index; + + /** + * @param index of the employee in the filtered employee list to edit + */ + public ResetPasswordCommand(Index index) { + requireNonNull(index); + this.index = index; + } + + @Override + public CommandResult execute() throws CommandException { + + List lastShownList = model.getFilteredEmployeeList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_EMPLOYEE_DISPLAYED_INDEX); + } + + Employee requestedEmployee = lastShownList.get(index.getZeroBased()); + Password newPassword = null; + + try { + newPassword = createAndSendRandomPassword(requestedEmployee); + } catch (AddressException ae) { + System.out.println(ae.toString()); + return new CommandResult(String.format(MESSAGE_EMAILFAIL, requestedEmployee.getEmail())); + } catch (MessagingException e) { + System.out.println(e.toString()); + return new CommandResult(MESSAGE_SENTFAIL); + } + + model.storeResetPassword(requestedEmployee, newPassword); + return new CommandResult(String.format(MESSAGE_SUCCESS, requestedEmployee.getEmail())); + } + + /** + * Generate random password with 8 characters + * @return Password with the new password. + */ + private Password createAndSendRandomPassword(Employee requestedEmployee) throws MessagingException { + String newPassword = Password.generateRandomPassword(); + + EmailService email = EmailService.getInstance(); + email.sendResetPasswordMessage(requestedEmployee.getName().toString(), + requestedEmployee.getEmail().toString(), newPassword); + + return parsePassword(newPassword); + } + + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ResetPasswordCommand)) { + return false; + } + + // state check + ResetPasswordCommand e = (ResetPasswordCommand) other; + return index.equals(e.index); + } + + /** + * Parses a {@code String password} into an {@code Password}. + * Leading and trailing whitespaces will be trimmed. + * + */ + public static Password parsePassword(String password) { + requireNonNull(password); + String trimmedPassword = password.trim(); + Password newPassword = new Password(); + newPassword.createPassword(trimmedPassword); + return newPassword; + } + +} +``` +###### \java\seedu\ptman\logic\LogicManager.java +``` java + /** + * Obscure sensitive information like password by replacing it with "a space" + * @param input + * @return the processed input + */ + private String processInput(String input) { + StringBuilder newString = new StringBuilder(input); + int indexOfPrefix = newString.indexOf(PREFIX_PASSWORD.getPrefix()); + int indexOfSpace = newString.indexOf(" ", indexOfPrefix); + while (indexOfPrefix >= 0) { + if (indexOfSpace == -1) { + indexOfSpace = newString.length(); + } + newString.replace(indexOfPrefix + 3 , indexOfSpace, " "); + + indexOfPrefix = newString.indexOf(PREFIX_PASSWORD.getPrefix(), indexOfPrefix + 3); + indexOfSpace = newString.indexOf(" ", indexOfPrefix); + + } + return newString.toString(); + } + +``` +###### \java\seedu\ptman\logic\parser\ChangeAdminPasswordCommandParser.java +``` java +/** + * Parses input arguments and creates a new ChangeAdminPasswordCommand object + */ +public class ChangeAdminPasswordCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ChangeMasterPassword + * and returns an ChangeAdminPasswordCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ChangeAdminPasswordCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_PASSWORD); + + if (!arePrefixesPresent(argMultimap, PREFIX_PASSWORD) || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ChangeAdminPasswordCommand.MESSAGE_USAGE)); + } + + ArrayList passwords; + + try { + passwords = parsePasswords(argMultimap.getAllValues(PREFIX_PASSWORD)); + } catch (IllegalValueException ive) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ChangeAdminPasswordCommand.MESSAGE_USAGE)); + } + + checkPasswordValidity(passwords.get(1)); + + return new ChangeAdminPasswordCommand(passwords); + } + + /** + * Check validity of the password string given + * @param passwords + * @throws ParseException if it does not satisfy the password 8 length restriction. + */ + private void checkPasswordValidity(String passwords) throws ParseException { + if (!Password.isValidPassword(passwords)) { + throw new ParseException(Password.MESSAGE_PASSWORD_CONSTRAINTS); + } + } + + /** + * 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()); + } + + /** + * Parses {@code Collection passwords} into a {@code Set}. + */ + public static ArrayList parsePasswords(Collection passwords) throws ParseException { + requireNonNull(passwords); + final ArrayList passwordSet = new ArrayList<>(); + for (String password : passwords) { + passwordSet.add(password); + } + + if (passwordSet.size() != 3) { + throw new ParseException("Incorrect number of password provided"); + } + + return passwordSet; + } + +} +``` +###### \java\seedu\ptman\logic\parser\ChangePasswordCommandParser.java +``` java +/** + * Parses input arguments and creates a new ChangePasswordCommand object + */ +public class ChangePasswordCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ChangePasswordCommand + * and returns an ChangePasswordCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ChangePasswordCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_PASSWORD); + + if (!arePrefixesPresent(argMultimap, PREFIX_PASSWORD)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ChangePasswordCommand.MESSAGE_USAGE)); + } + + Index index; + ArrayList passwords; + + try { + passwords = parsePasswords(argMultimap.getAllValues(PREFIX_PASSWORD)); + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (IllegalValueException ive) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ChangePasswordCommand.MESSAGE_USAGE)); + } + + checkPasswordValidity(passwords.get(1)); + + return new ChangePasswordCommand(index, passwords); + } + + /** + * Check validity of the password string given + * @param passwords + * @throws ParseException if it does not satisfy the password 8 length restriction. + */ + private void checkPasswordValidity(String passwords) throws ParseException { + if (!Password.isValidPassword(passwords)) { + throw new ParseException(Password.MESSAGE_PASSWORD_CONSTRAINTS); + } + } + + /** + * 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()); + } + + /** + * Parses {@code Collection passwords} into a {@code Set}. + */ + public static ArrayList parsePasswords(Collection passwords) throws ParseException { + requireNonNull(passwords); + final ArrayList passwordSet = new ArrayList<>(); + for (String password : passwords) { + passwordSet.add(password); + } + + if (passwordSet.size() != 3) { + throw new ParseException("Incorrect number of passwords provided"); + } + + return passwordSet; + } + +} +``` +###### \java\seedu\ptman\logic\parser\LogInAdminCommandParser.java +``` java +/** + * Parses input arguments and creates a new LogInAdminCommand object + */ +public class LogInAdminCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the LogInAdminCommand + * and returns an LogInAdminCommand object for execution. + * + * @throws ParseException if the user input does not conform the expected format. + */ + public LogInAdminCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_PASSWORD); + + if (!ParserUtil.arePrefixesPresent(argMultimap, PREFIX_PASSWORD)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, LogInAdminCommand.MESSAGE_USAGE)); + } + + Password adminPassword = ParserUtil.parsePassword(argMultimap.getValue(PREFIX_PASSWORD)).get(); + return new LogInAdminCommand(adminPassword); + } +} +``` +###### \java\seedu\ptman\logic\parser\ResetAdminPasswordCommandParser.java +``` java +/** + * Parses input arguments and creates a new ResetAdminPasswordCommand object + */ +public class ResetAdminPasswordCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ResetAdminPasswordCommand + * and returns an ResetAdminPasswordCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ResetAdminPasswordCommand parse(String args) { + return new ResetAdminPasswordCommand(); + } + +} +``` +###### \java\seedu\ptman\logic\parser\ResetPasswordCommandParser.java +``` java +/** + * Parses input arguments and creates a new ResetPasswordCommand object + */ +public class ResetPasswordCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ResetPasswordCommand + * and returns an ResetPasswordCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ResetPasswordCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new ResetPasswordCommand(index); + } catch (IllegalValueException ive) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ResetPasswordCommand.MESSAGE_USAGE)); + } + } + +} +``` +###### \java\seedu\ptman\model\employee\Salary.java +``` java +/** + * Represents a Employee's Salary earned so far in PTMan. + * Guarantees: immutable; is valid as declared in {@link #isValidSalary(String)} + */ +public class Salary { + + public static final String MESSAGE_SALARY_CONSTRAINTS = + "Salary can only contain positive numbers"; + public static final String SALARY_VALIDATION_REGEX = "^[0-9]\\d*$"; + public final String value; + + + /** + * Constructs a {@code Salary} + * + * @param salary A valid salary. + */ + public Salary(String salary) { + requireNonNull(salary); + checkArgument(isValidSalary(salary), MESSAGE_SALARY_CONSTRAINTS); + this.value = salary; + } + + public static boolean isValidSalary(String test) { + return test.matches(SALARY_VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Salary // instanceof handles nulls + && this.value.equals(((Salary) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} +``` +###### \java\seedu\ptman\model\ModelManager.java +``` java + @Override + public boolean isAdminMode() { + return partTimeManager.isAdminMode(); + } + + @Override + public synchronized boolean setTrueAdminMode(Password password) { + requireNonNull(password); + if (!partTimeManager.isAdminPassword(password)) { + return false; + } + partTimeManager.setAdminMode(partTimeManager.isAdminPassword(password)); + return true; + } + + @Override + public synchronized void setFalseAdminMode() { + partTimeManager.setAdminMode(false); + } + + @Override + public boolean isAdminPassword(Password password) { + return partTimeManager.isAdminPassword(password); + } + + @Override + public void setAdminPassword(Password password) { + partTimeManager.setAdminPassword(password); + indicatePartTimeManagerChanged(); + } + + @Override + public void storeResetPassword(Employee employee, Password tempPassword) { + tempPasswordMap.put(employee, tempPassword); + } + + @Override + public void storeResetPassword(OutletInformation outlet, Password tempPassword) { + tempAdminPasswordMap.put(outlet, tempPassword); + } + + @Override + public boolean isCorrectTempPwd(Employee employee, Password tempPassword) { + if (!tempPasswordMap.containsKey(employee)) { + return false; + } else { + return tempPasswordMap.get(employee).equals(tempPassword); + } + } + + @Override + public boolean isCorrectTempPwd(OutletInformation outlet, Password tempPassword) { + if (!tempAdminPasswordMap.containsKey(outlet)) { + return false; + } else { + return tempAdminPasswordMap.get(outlet).equals(tempPassword); + } + } + +``` +###### \java\seedu\ptman\model\PartTimeManager.java +``` java + //// authorization operations + public boolean isAdminMode() { + return this.isAdminMode; + } + + /** + * Check if given password is of outlet's + * @param password + * @return true if password is the same + */ + public boolean isAdminPassword(Password password) { + return outlet.getMasterPassword().equals(password); + } + + /** + * set admin mode only after check against adminPassword + * @param isAdmin + */ + public void setAdminMode(boolean isAdmin) { + isAdminMode = isAdmin; + } + + /** + * set password for outlet + * @param password + */ + public void setAdminPassword(Password password) { + outlet.setOutletPassword(password); + } + +``` +###### \java\seedu\ptman\model\Password.java +``` java +/** + * Represents a Password in PartTimeManger + * Store password as hashCode + */ +public class Password { + + public static final String MESSAGE_PASSWORD_CONSTRAINTS = + "Password should be at least 8 character and no spaces."; + + public static final String DEFAULT_PASSWORD = + "DEFAULT1"; + + /** + * accept all password that do not have whitespaces and at least 8 characters. + */ + public static final String PASSWORD_VALIDATION_REGEX = "^(?=\\S+$).{8,}$"; + + private String passwordHash; + private final String initialValue = "IV"; + + /** + * constructor for default password + */ + public Password() { + createPassword(DEFAULT_PASSWORD); + } + + /** + * use this if hashcode is known + * @param hashCode + */ + public Password(String hashCode) { + requireNonNull(hashCode); + passwordHash = hashCode; + } + + public Password(Password masterPassword) { + requireNonNull(masterPassword); + this.passwordHash = masterPassword.getPasswordHash(); + } + + + /** + * @param test + * @return true if password is of correct format + */ + public static boolean isValidPassword(String test) { + return test.matches(PASSWORD_VALIDATION_REGEX); + } + + /** + * check if passwordHash generated from the string is same as current passwordHash + * @param password + * @return true if same + */ + public boolean isCorrectPassword(String password) { + return passwordHash.equals(generatePasswordHash(password)); + } + + /** + * Change password given a password + * @return true if password is changed + */ + public boolean checkAndChangePassword(String oldPassword, String newPassword) { + if (isCorrectPassword(oldPassword)) { + createPassword(newPassword); + return true; + } + return false; + } + + /** + * Create passwordHash when password is entered in plain text + * @param password + */ + public void createPassword(String password) { + requireNonNull(password); + passwordHash = generatePasswordHash(password); + } + + public String getPasswordHash() { + return passwordHash; + } + + /** + * Generate passwordHash given a string password + * @param password + * @return passwordHash in String + */ + private String generatePasswordHash(String password) { + String encodedHash = null; + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + digest.update(initialValue.getBytes()); + byte[] byteHash = digest.digest(password.getBytes(StandardCharsets.UTF_8)); + encodedHash = Base64.getEncoder().encodeToString(byteHash); + } catch (NoSuchAlgorithmException noSuchAlgoException) { + Logger.logMsg(Logger.ERROR, "cannot generate hash: MessageDigest.getInstance"); + } + return encodedHash; + } + + /** + * + * @return random 8 character String password. + */ + public static String generateRandomPassword() { + String charactersAllowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + String randomPwd = RandomStringUtils.random(8, charactersAllowed); + return randomPwd; + } + + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Password // instanceof handles nulls + && this.passwordHash.equals(((Password) other).passwordHash)); // state check + } + + @Override + public int hashCode() { + return passwordHash.hashCode(); + } +} +``` +###### \java\seedu\ptman\ui\CommandBox.java +``` java + public CommandBox(Logic logic) { + super(FXML); + this.logic = logic; + + // calls #setStyleToDefault() whenever there is a change to the text of the command box. + commandTextField.textProperty().addListener((unused1, unused2, unused3) -> setStyleToDefault()); + + commandTextField.setSkin(new PasswordFieldSkinAndCaret(commandTextField , Color.web("969696"))); + historySnapshot = logic.getHistorySnapshot(); + tooltip.setText("Tip: Enter \"help\" when you get stuck"); + commandTextField.setTooltip(tooltip); + + } + + /** + * Obscure sensitive information like password by replacing each character by "*" + * @param input + * @return the processed input + */ + private String processInput(String input) { + StringBuilder newString = new StringBuilder(input); + int indexOfPrefix = input.indexOf(PREFIX_PASSWORD.getPrefix()); + int indexOfSpace = input.indexOf(" ", indexOfPrefix); + + while (indexOfPrefix >= 0) { + if (indexOfSpace == -1) { + indexOfSpace = input.length(); + } + for (int i = indexOfPrefix + 3; i < indexOfSpace; i++) { + newString.replace(i, i + 1, "*"); + } + indexOfPrefix = input.indexOf(PREFIX_PASSWORD.getPrefix(), indexOfPrefix + 3); + indexOfSpace = input.indexOf(" ", indexOfPrefix); + } + return newString.toString(); + } + + +``` +###### \java\seedu\ptman\ui\CommandBox.java +``` java + /** + * class to set up caret colour for textField and skinning of password + */ + public class PasswordFieldSkinAndCaret extends TextFieldSkin { + public static final String ASTERISK = "*"; + + public PasswordFieldSkinAndCaret(PasswordField passwordField, Color caretColor) { + super(passwordField, new PasswordFieldBehavior(passwordField)); + setCaretColor(caretColor); + } + private void setCaretColor(Color color) { + caretPath.strokeProperty().unbind(); + caretPath.fillProperty().unbind(); + caretPath.setStroke(color); + caretPath.setFill(color); + } + + @Override + protected String maskText(String txt) { + TextField textField = getSkinnable(); + + StringBuilder newString = new StringBuilder(textField.getText()); + int indexOfPrefix = newString.indexOf(PREFIX_PASSWORD.getPrefix()); + int indexOfSpace = newString.indexOf(" ", indexOfPrefix); + + while (indexOfPrefix >= 0) { + if (indexOfSpace == -1) { + indexOfSpace = newString.length(); + } + for (int i = indexOfPrefix + 3; i < indexOfSpace; i++) { + newString.replace(i, i + 1, ASTERISK); + } + indexOfPrefix = newString.indexOf(PREFIX_PASSWORD.getPrefix(), indexOfPrefix + 3); + indexOfSpace = newString.indexOf(" ", indexOfPrefix); + } + + return newString.toString(); + } + } + +} + +``` diff --git a/collated/functional/shanwpf.md b/collated/functional/shanwpf.md new file mode 100644 index 000000000000..a94e21c79d36 --- /dev/null +++ b/collated/functional/shanwpf.md @@ -0,0 +1,1231 @@ +# shanwpf +###### \java\seedu\ptman\commons\util\DateUtil.java +``` java +/** + * Utility methods for handling dates + */ +public class DateUtil { + /** + * Returns the week number for {@code date} from the start of the year + */ + public static int getWeekFromDate(LocalDate date) { + requireNonNull(date); + TemporalField woy = WeekFields.of(Locale.FRANCE).weekOfWeekBasedYear(); + return date.get(woy); + } + + /** + * Given a {@code date}, returns the date of the week's Monday + */ + public static LocalDate getMondayOfDate(LocalDate date) { + requireNonNull(date); + int week = getWeekFromDate(date); + // We use Locale.FRANCE because it sets the first day of the week + // to be Monday. + WeekFields weekFields = WeekFields.of(Locale.FRANCE); + return LocalDate.now() + .withYear(date.getYear()) + .with(weekFields.weekOfYear(), week) + .with(weekFields.dayOfWeek(), 1); + } +} +``` +###### \java\seedu\ptman\logic\commands\AddShiftCommand.java +``` java +/** + * Adds a shift to PTMan. + */ +public class AddShiftCommand extends UndoableCommand { + + public static final String COMMAND_WORD = "addshift"; + public static final String COMMAND_ALIAS = "as"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a shift. " + + "Parameters: " + + PREFIX_DATE + "DATE (in dd-mm-yy format) " + + PREFIX_TIME_START + "START_TIME " + + PREFIX_TIME_END + "END_TIME " + + PREFIX_CAPACITY + "CAPACITY " + + "Example: " + COMMAND_WORD + " " + + PREFIX_DATE + "12-03-18 " + + PREFIX_TIME_START + "0900 " + + PREFIX_TIME_END + "1600 " + + PREFIX_CAPACITY + "5 "; + + public static final String MESSAGE_SUCCESS = "New shift added: %1$s"; + public static final String MESSAGE_DUPLICATE_SHIFT = "This shift already exists in PTMan"; + + private final Shift toAdd; + + /** + * Creates an AddCommand to add the specified {@code Shift} + */ + public AddShiftCommand(Shift shift) { + requireNonNull(shift); + toAdd = shift; + } + + @Override + public CommandResult executeUndoableCommand() throws CommandException { + requireNonNull(model); + + if (!model.isAdminMode()) { + throw new CommandException(MESSAGE_ACCESS_DENIED); + } + + try { + model.addShift(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } catch (DuplicateShiftException e) { + throw new CommandException(MESSAGE_DUPLICATE_SHIFT); + } + + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddShiftCommand // instanceof handles nulls + && toAdd.equals(((AddShiftCommand) other).toAdd)); + } +} +``` +###### \java\seedu\ptman\logic\commands\ApplyCommand.java +``` java +/** + * Registers an employee to a shift identified using their last displayed index from PTMan. + */ +public class ApplyCommand extends UndoableCommand { + + public static final String COMMAND_WORD = "apply"; + public static final String COMMAND_ALIAS = "ap"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Applies an employee for the shift identified by the index number.\n" + + "Parameters: EMPLOYEE_INDEX (must be a positive integer) " + + "SHIFT_INDEX " + + "[" + PREFIX_PASSWORD + "PASSWORD]\n" + + "Example: " + COMMAND_WORD + " 1 1 " + PREFIX_PASSWORD + "hunter2"; + + public static final String MESSAGE_APPLY_SHIFT_SUCCESS = "Employee %1$s applied for shift %2$s"; + public static final String MESSAGE_DUPLICATE_EMPLOYEE = "Employee is already in the shift"; + public static final String MESSAGE_SHIFT_FULL = "Shift %1$s is full"; + + private final Index employeeIndex; + private final Index shiftIndex; + private final Optional optionalPassword; + + private Employee applicant; + private Shift shiftToApply; + private Shift editedShift; + + public ApplyCommand(Index employeeIndex, Index shiftIndex, Optional optionalPassword) { + this.optionalPassword = optionalPassword; + this.employeeIndex = employeeIndex; + this.shiftIndex = shiftIndex; + } + + + @Override + public CommandResult executeUndoableCommand() throws CommandException { + requireNonNull(applicant); + + // Check if password is present when not in admin mode + if (!model.isAdminMode()) { + if (!optionalPassword.isPresent()) { + throw new MissingPasswordException(); + } + if (!applicant.isCorrectPassword(optionalPassword.get())) { + throw new InvalidPasswordException(); + } + } + + try { + model.updateShift(shiftToApply, editedShift); + } catch (ShiftNotFoundException e) { + throw new AssertionError("Shift not found"); + } catch (DuplicateShiftException e) { + throw new AssertionError("Duplicate shift"); + } + + return new CommandResult(String.format(MESSAGE_APPLY_SHIFT_SUCCESS, + applicant.getName(), shiftIndex.getOneBased())); + } + + @Override + protected void preprocessUndoableCommand() throws CommandException { + List lastShownList = model.getFilteredEmployeeList(); + List shiftList = model.getFilteredShiftList(); + + if (employeeIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_EMPLOYEE_DISPLAYED_INDEX); + } + if (shiftIndex.getZeroBased() >= shiftList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_SHIFT_DISPLAYED_INDEX); + } + + applicant = lastShownList.get(employeeIndex.getZeroBased()); + shiftToApply = shiftList.get(shiftIndex.getZeroBased()); + editedShift = new Shift(shiftToApply); + try { + editedShift.addEmployee(applicant); + } catch (DuplicateEmployeeException e) { + throw new CommandException(MESSAGE_DUPLICATE_EMPLOYEE); + } catch (ShiftFullException e) { + throw new CommandException(String.format(MESSAGE_SHIFT_FULL, shiftIndex.getOneBased())); + } + + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ApplyCommand that = (ApplyCommand) o; + return Objects.equals(employeeIndex, that.employeeIndex) + && Objects.equals(shiftIndex, that.shiftIndex) + && Objects.equals(applicant, that.applicant) + && Objects.equals(shiftToApply, that.shiftToApply); + } + + @Override + public int hashCode() { + return Objects.hash(employeeIndex, shiftIndex, applicant, shiftToApply); + } +} +``` +###### \java\seedu\ptman\logic\commands\DeleteShiftCommand.java +``` java +/** + * Deletes a shift identified using it's last displayed index in the timetable. + */ +public class DeleteShiftCommand extends UndoableCommand { + + public static final String COMMAND_WORD = "deleteshift"; + public static final String COMMAND_ALIAS = "ds"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the shift identified by the index number used in the timetable.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_SHIFT_SUCCESS = "Deleted Shift: %1$s"; + + private final Index targetIndex; + + private Shift shiftToDelete; + + public DeleteShiftCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + + @Override + public CommandResult executeUndoableCommand() throws CommandException { + requireNonNull(shiftToDelete); + + if (!model.isAdminMode()) { + throw new CommandException(MESSAGE_ACCESS_DENIED); + } + + try { + model.deleteShift(shiftToDelete); + } catch (ShiftNotFoundException pnfe) { + throw new AssertionError("The target shift cannot be missing"); + } + + return new CommandResult(String.format(MESSAGE_DELETE_SHIFT_SUCCESS, shiftToDelete)); + } + + @Override + protected void preprocessUndoableCommand() throws CommandException { + List lastShownList = model.getFilteredShiftList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_SHIFT_DISPLAYED_INDEX); + } + + shiftToDelete = lastShownList.get(targetIndex.getZeroBased()); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteShiftCommand // instanceof handles nulls + && this.targetIndex.equals(((DeleteShiftCommand) other).targetIndex) // state check + && Objects.equals(this.shiftToDelete, ((DeleteShiftCommand) other).shiftToDelete)); + } +} +``` +###### \java\seedu\ptman\logic\commands\UnapplyCommand.java +``` java +/** + * Registers an employee to a shift identified using their last displayed index from PTMan. + */ +public class UnapplyCommand extends UndoableCommand { + + public static final String COMMAND_WORD = "unapply"; + public static final String COMMAND_ALIAS = "uap"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Removes an employee from the shift identified by the index number.\n" + + "Parameters: EMPLOYEE_INDEX (must be a positive integer) " + + "SHIFT_INDEX " + + "[" + PREFIX_PASSWORD + "PASSWORD]\n" + + "Example: " + COMMAND_WORD + " 1 1 " + PREFIX_PASSWORD + "hunter2"; + + public static final String MESSAGE_UNAPPLY_SHIFT_SUCCESS = "Employee %1$s removed from shift %2$s"; + public static final String MESSAGE_EMPLOYEE_NOT_FOUND = "Employee is not in the shift."; + + private final Index employeeIndex; + private final Index shiftIndex; + private final Optional optionalPassword; + + private Employee applicant; + private Shift shiftToUnapply; + private Shift editedShift; + + public UnapplyCommand(Index employeeIndex, Index shiftIndex, Optional optionalPassword) { + this.optionalPassword = optionalPassword; + this.employeeIndex = employeeIndex; + this.shiftIndex = shiftIndex; + } + + + @Override + public CommandResult executeUndoableCommand() throws CommandException { + requireNonNull(applicant); + + // Check if password is present when not in admin mode + if (!model.isAdminMode()) { + if (!optionalPassword.isPresent()) { + throw new MissingPasswordException(); + } + if (!applicant.isCorrectPassword(optionalPassword.get())) { + throw new InvalidPasswordException(); + } + } + + try { + model.updateShift(shiftToUnapply, editedShift); + } catch (ShiftNotFoundException e) { + throw new AssertionError("Shift not found"); + } catch (DuplicateShiftException e) { + throw new AssertionError("Duplicate shift"); + } + + return new CommandResult(String.format(MESSAGE_UNAPPLY_SHIFT_SUCCESS, + applicant.getName(), shiftIndex.getOneBased())); + } + + @Override + protected void preprocessUndoableCommand() throws CommandException { + List lastShownList = model.getFilteredEmployeeList(); + List shiftList = model.getFilteredShiftList(); + + if (employeeIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_EMPLOYEE_DISPLAYED_INDEX); + } + if (shiftIndex.getZeroBased() >= shiftList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_SHIFT_DISPLAYED_INDEX); + } + + applicant = lastShownList.get(employeeIndex.getZeroBased()); + shiftToUnapply = shiftList.get(shiftIndex.getZeroBased()); + editedShift = new Shift(shiftToUnapply); + try { + editedShift.removeEmployee(applicant); + } catch (EmployeeNotFoundException e) { + throw new CommandException(MESSAGE_EMPLOYEE_NOT_FOUND); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + UnapplyCommand that = (UnapplyCommand) o; + return Objects.equals(employeeIndex, that.employeeIndex) + && Objects.equals(shiftIndex, that.shiftIndex) + && Objects.equals(applicant, that.applicant) + && Objects.equals(shiftToUnapply, that.shiftToUnapply); + } + + @Override + public int hashCode() { + return Objects.hash(employeeIndex, shiftIndex, applicant, shiftToUnapply, optionalPassword); + } +} +``` +###### \java\seedu\ptman\logic\parser\AddShiftCommandParser.java +``` java +/** + * Parses input arguments and creates a new AddShiftCommand object + */ +public class AddShiftCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddShiftCommand + * and returns an AddShiftCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddShiftCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_DATE, PREFIX_TIME_START, + PREFIX_TIME_END, PREFIX_CAPACITY); + + if (!arePrefixesPresent(argMultimap, PREFIX_DATE, PREFIX_TIME_START, + PREFIX_TIME_END, PREFIX_CAPACITY) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddShiftCommand.MESSAGE_USAGE)); + } + + try { + Date date = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE)).get(); + Time startTime = ParserUtil.parseTime(argMultimap.getValue(PREFIX_TIME_START)).get(); + Time endTime = ParserUtil.parseTime(argMultimap.getValue(PREFIX_TIME_END)).get(); + Capacity capacity = ParserUtil.parseCapacity(argMultimap.getValue(PREFIX_CAPACITY)).get(); + + Shift shift = new Shift(date, startTime, endTime, capacity); + + return new AddShiftCommand(shift); + } catch (IllegalValueException ive) { + throw new ParseException(ive.getMessage(), ive); + } + } +} +``` +###### \java\seedu\ptman\logic\parser\ApplyCommandParser.java +``` java +/** + * Parses input arguments and creates a new ApplyCommand object + */ +public class ApplyCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ApplyCommand + * and returns an ApplyCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ApplyCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_PASSWORD); + try { + Optional password = ParserUtil.parsePassword(argMultimap.getValue(PREFIX_PASSWORD)); + Index employeeIndex = ParserUtil.parseFirstIndex(clearPasswordFromCommand(args)); + Index shiftIndex = ParserUtil.parseSecondIndex(clearPasswordFromCommand(args)); + return new ApplyCommand(employeeIndex, shiftIndex, password); + } catch (IllegalValueException ive) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ApplyCommand.MESSAGE_USAGE)); + } + } +} +``` +###### \java\seedu\ptman\logic\parser\DeleteShiftCommandParser.java +``` java +/** + * Parses input arguments and creates a new DeleteShiftCommand object + */ +public class DeleteShiftCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteShiftCommand + * and returns an DeleteShiftCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteShiftCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(clearPasswordFromCommand(args)); + return new DeleteShiftCommand(index); + } catch (IllegalValueException ive) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteShiftCommand.MESSAGE_USAGE)); + } + } +} +``` +###### \java\seedu\ptman\logic\parser\UnapplyCommandParser.java +``` java +/** + * Parses input arguments and creates a new UnapplyCommand object + */ +public class UnapplyCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the UnapplyCommand + * and returns an UnapplyCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public UnapplyCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_PASSWORD); + try { + Optional password = ParserUtil.parsePassword(argMultimap.getValue(PREFIX_PASSWORD)); + Index employeeIndex = ParserUtil.parseFirstIndex(ParserUtil.clearPasswordFromCommand(args)); + Index shiftIndex = ParserUtil.parseSecondIndex(ParserUtil.clearPasswordFromCommand(args)); + return new UnapplyCommand(employeeIndex, shiftIndex, password); + } catch (IllegalValueException ive) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnapplyCommand.MESSAGE_USAGE)); + } + } + +} +``` +###### \java\seedu\ptman\model\ModelManager.java +``` java + @Override + public void addShift(Shift shift) throws DuplicateShiftException { + partTimeManager.addShift(shift); + indicatePartTimeManagerChanged(); + } + + @Override + public ObservableList getFilteredShiftList() { + SortedList sortedShiftList = new SortedList<>(filteredShifts, Shift::compareTo); + return FXCollections.unmodifiableObservableList(sortedShiftList); + } + + @Override + public void deleteShift(Shift target) throws ShiftNotFoundException { + partTimeManager.removeShift(target); + indicatePartTimeManagerChanged(); + } + + @Override + public void updateShift(Shift target, Shift editedShift) throws ShiftNotFoundException, DuplicateShiftException { + partTimeManager.updateShift(target, editedShift); + indicatePartTimeManagerChanged(); + } + + @Override + public void updateFilteredShiftList(Predicate predicate) { + requireNonNull(predicate); + filteredShifts.setPredicate(predicate); + } + + @Override + public void updateEmployee(Employee target, Employee editedEmployee) + throws DuplicateEmployeeException, EmployeeNotFoundException { + requireAllNonNull(target, editedEmployee); + + partTimeManager.updateEmployee(target, editedEmployee); + indicatePartTimeManagerChanged(); + } + +``` +###### \java\seedu\ptman\model\PartTimeManager.java +``` java + public void setShifts(List shifts) throws DuplicateShiftException { + this.shifts.setShifts(shifts); + } +``` +###### \java\seedu\ptman\model\PartTimeManager.java +``` java + /** + * Removes {@code key} from this {@code PartTimeManager}. + * @throws ShiftNotFoundException if the {@code key} is not in this {@code PartTimeManager} + */ + public boolean removeShift(Shift key) throws ShiftNotFoundException { + return shifts.remove(key); + } + + /** + * Adds a shift to PTMan. + * @throws DuplicateShiftException if a equivalent shift already exists. + */ + public void addShift(Shift p) throws DuplicateShiftException { + shifts.add(p); + } + + /** + * Replaces the given shift {@code target} in the list with {@code editedShift}. + * + * @throws DuplicateShiftException if updating the shift's details causes the shift to be equivalent to + * another existing shift in the list. + * @throws ShiftNotFoundException if {@code target} could not be found in the list. + */ + public void updateShift(Shift target, Shift editedShift) throws ShiftNotFoundException, DuplicateShiftException { + shifts.setShift(target, editedShift); + } +``` +###### \java\seedu\ptman\model\PartTimeManager.java +``` java + @Override + public ObservableList getShiftList() { + return shifts.asObservableList(); + } +``` +###### \java\seedu\ptman\model\shift\Capacity.java +``` java +/** + * Represents a shift's capacity + * Guarantees: immutable; is valid as declared in {@link #isValidCapacity(String)} + */ +public class Capacity { + + public static final String MESSAGE_CAPACITY_CONSTRAINTS = "Capacity should be a positive integer."; + public static final String CAPACITY_VALIDATION_REGEX = "^[1-9]\\d*$"; + + public final int capacity; + + public Capacity(String capacity) { + requireNonNull(capacity); + checkArgument(isValidCapacity(capacity), MESSAGE_CAPACITY_CONSTRAINTS); + this.capacity = Integer.parseInt(capacity); + } + + public static Boolean isValidCapacity(String test) { + return test.matches(CAPACITY_VALIDATION_REGEX); + } + + public int getCapacity() { + return capacity; + } + + @Override + public String toString() { + return String.valueOf(capacity); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Capacity capacity1 = (Capacity) o; + return Objects.equals(capacity, capacity1.capacity); + } + + @Override + public int hashCode() { + return new Integer(capacity).hashCode(); + } +} +``` +###### \java\seedu\ptman\model\shift\Date.java +``` java +/** + * Represents a shift's date + * Guarantees: immutable; is valid as declared in {@link #isValidDate(String)} + */ +public class Date { + + public static final String STRING_DATE_PATTERN = "dd-MM-yy"; + public static final String MESSAGE_DATE_CONSTRAINTS = "Date should be in dd-mm-yy format"; + + public final LocalDate date; + + public Date(String date) { + requireNonNull(date); + checkArgument(isValidDate(date), MESSAGE_DATE_CONSTRAINTS); + this.date = LocalDate.parse(date, DateTimeFormatter.ofPattern(STRING_DATE_PATTERN)); + } + + /** + * Returns true if a given string is a valid date. + * @param test + * @return + */ + public static Boolean isValidDate(String test) { + try { + LocalDate.parse(test, DateTimeFormatter.ofPattern(STRING_DATE_PATTERN)); + } catch (DateTimeParseException e) { + return false; + } + return true; + } + + @Override + public String toString() { + return date.format(DateTimeFormatter.ofPattern(STRING_DATE_PATTERN)); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Date date1 = (Date) o; + return Objects.equals(date, date1.date); + } + + @Override + public int hashCode() { + return date.hashCode(); + } + + public int compareTo(Date startDate) { + return date.compareTo(startDate.getLocalDate()); + } + + public LocalDate getLocalDate() { + return date; + } +} +``` +###### \java\seedu\ptman\model\shift\exceptions\DuplicateShiftException.java +``` java +/** + * Signals that the operation will result in duplicate Shift objects. + */ +public class DuplicateShiftException extends DuplicateDataException { + public DuplicateShiftException() { + super("Operation would result in duplicate employees"); + } +} +``` +###### \java\seedu\ptman\model\shift\exceptions\ShiftFullException.java +``` java +/** + * Signals that the operation is attempting to add an employee to a full shift + */ +public class ShiftFullException extends Exception {} +``` +###### \java\seedu\ptman\model\shift\exceptions\ShiftNotFoundException.java +``` java +/** + * Signals that the operation is unable to find the specified shift. + */ +public class ShiftNotFoundException extends Exception {} +``` +###### \java\seedu\ptman\model\shift\Shift.java +``` java +/** + * Represents a shift that employees can work in. + */ +public class Shift { + public static final String MESSAGE_SHIFT_CONSTRAINTS = "Start time should be after the end time."; + private Time startTime; + private Time endTime; + private Date date; + private UniqueEmployeeList uniqueEmployeeList; + private Capacity capacity; + + public Shift(Date date, Time startTime, Time endTime, Capacity capacity) { + requireAllNonNull(startTime, endTime, capacity); + checkArgument(endTime.isAfter(startTime), MESSAGE_SHIFT_CONSTRAINTS); + this.date = date; + this.startTime = startTime; + this.endTime = endTime; + this.capacity = capacity; + this.uniqueEmployeeList = new UniqueEmployeeList(); + } + + public Shift(Shift shift) { + this.date = shift.getDate(); + this.startTime = shift.getStartTime(); + this.endTime = shift.getEndTime(); + this.capacity = shift.getCapacity(); + this.uniqueEmployeeList = new UniqueEmployeeList(); + setEmployees(shift); + } + + public Shift(Date date, Time startTime, Time endTime, Capacity capacity, List employees) { + requireAllNonNull(date, startTime, endTime, capacity, employees); + checkArgument(endTime.isAfter(startTime), MESSAGE_SHIFT_CONSTRAINTS); + this.startTime = startTime; + this.endTime = endTime; + this.capacity = capacity; + this.date = date; + this.uniqueEmployeeList = new UniqueEmployeeList(); + try { + this.uniqueEmployeeList.setEmployees(employees); + } catch (DuplicateEmployeeException e) { + e.printStackTrace(); + } + } + + protected boolean contains(Employee employee) { + return uniqueEmployeeList.contains(employee); + } + + /** + * Adds an employee to this shift + * @throws DuplicateEmployeeException + * @throws ShiftFullException + */ + public void addEmployee(Employee employee) throws DuplicateEmployeeException, ShiftFullException { + if (this.isFull()) { + throw new ShiftFullException(); + } + uniqueEmployeeList.add(employee); + } + + /** + * Removes an employee from this shift + * @throws EmployeeNotFoundException + */ + public void removeEmployee(Employee employee) throws EmployeeNotFoundException { + uniqueEmployeeList.remove(employee); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Shift shift = (Shift) o; + return startTime.equals(shift.startTime) + && endTime.equals(shift.endTime) + && date.equals(shift.date) + && uniqueEmployeeList.equals(shift.uniqueEmployeeList) + && capacity.equals(shift.capacity); + } + + @Override + public int hashCode() { + return Objects.hash(startTime, endTime, date, uniqueEmployeeList, capacity); + } + + public ObservableList getEmployeeList() { + return uniqueEmployeeList.asObservableList(); + } + + public Time getStartTime() { + return startTime; + } + + public Time getEndTime() { + return endTime; + } + + public Capacity getCapacity() { + return capacity; + } + + public int getSlotsLeft() { + int numEmployees = Iterables.size(uniqueEmployeeList); + return capacity.getCapacity() - numEmployees; + } + + public boolean isFull() { + return getEmployeeList().size() >= this.capacity.getCapacity(); + } + + /** + * Compares this shift to another. + * Returns a negative integer if the argument is an earlier shift, + * 0 if the shifts are equal, or a positive integer if the argument is a later shift. + */ + public int compareTo(Shift other) { + if (date.equals(other.getDate())) { + return startTime.compareTo(other.getStartTime()); + } else if (date.compareTo(other.getDate()) < 0) { + return -1; + } else { + return 1; + } + } + + public UniqueEmployeeList getUniqueEmployeeList() { + return uniqueEmployeeList; + } + + public void setEmployees(Shift shift) { + for (final Employee employee : shift.getEmployeeList()) { + try { + uniqueEmployeeList.add(employee); + } catch (DuplicateEmployeeException e) { + e.printStackTrace(); + } + } + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + return sb.append("Date: ") + .append(date) + .append(" Start time: ") + .append(startTime) + .append(" End time: ") + .append(endTime) + .append(" Capacity: ") + .append(capacity).toString(); + } + + public Date getDate() { + return date; + } +} +``` +###### \java\seedu\ptman\model\shift\Time.java +``` java +/** + * Represents a shift's start or end time. + * Guarantees: immutable; is valid as declared in {@link #isValidTime(String)} + */ +public class Time { + + public static final String MESSAGE_TIME_CONSTRAINTS = "Time should be in 24-hour format."; + + public final LocalTime time; + + public Time(String time) { + requireNonNull(time); + checkArgument(isValidTime(time), MESSAGE_TIME_CONSTRAINTS); + this.time = LocalTime.parse(time, DateTimeFormatter.ofPattern("HHmm")); + } + + /** + * Returns true if a given string is a valid time. + * @param test + * @return + */ + public static Boolean isValidTime(String test) { + try { + LocalTime.parse(test, DateTimeFormatter.ofPattern("HHmm")); + } catch (DateTimeParseException e) { + return false; + } + return true; + } + + public boolean isAfter(Time t) { + return time.isAfter(t.time); + } + + @Override + public String toString() { + return time.toString().replace(":", ""); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Time time1 = (Time) o; + return Objects.equals(time, time1.time); + } + + @Override + public int hashCode() { + return time.hashCode(); + } + + public int compareTo(Time startTime) { + return time.compareTo(startTime.getLocalTime()); + } + + public LocalTime getLocalTime() { + return time; + } +} +``` +###### \java\seedu\ptman\model\shift\UniqueShiftList.java +``` java +/** + * A list of shifts that enforces uniqueness between its elements and does not allow nulls. + * + * Supports a minimal set of list operations. + * + * @see Shift#equals(Object) + * @see CollectionUtil#elementsAreUnique(Collection) + */ +public class UniqueShiftList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + + /** + * Returns true if the list contains an equivalent shift as the given argument. + */ + public boolean contains(Shift toCheck) { + requireNonNull(toCheck); + return internalList.contains(toCheck); + } + + /** + * Adds a shift to the list. + * + * @throws DuplicateShiftException if the shift to add is a duplicate of an existing shift in the list. + */ + public void add(Shift toAdd) throws DuplicateShiftException { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateShiftException(); + } + internalList.add(toAdd); + } + + /** + * Replaces the shift {@code target} in the list with {@code editedShift}. + * + * @throws DuplicateShiftException if the replacement is equivalent to another existing shift in the list. + * @throws ShiftNotFoundException if {@code target} could not be found in the list. + */ + public void setShift(Shift target, Shift editedShift) + throws DuplicateShiftException, ShiftNotFoundException { + requireNonNull(editedShift); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new ShiftNotFoundException(); + } + + if (!target.equals(editedShift) && internalList.contains(editedShift)) { + throw new DuplicateShiftException(); + } + + internalList.set(index, editedShift); + } + + /** + * Removes the equivalent shift from the list. + * + * @throws ShiftNotFoundException if no such shift could be found in the list. + */ + public boolean remove(Shift toRemove) throws ShiftNotFoundException { + requireNonNull(toRemove); + final boolean shiftFoundAndDeleted = internalList.remove(toRemove); + if (!shiftFoundAndDeleted) { + throw new ShiftNotFoundException(); + } + return shiftFoundAndDeleted; + } + + public void setShifts(UniqueShiftList replacement) { + this.internalList.setAll(replacement.internalList); + } + + public void setShifts(List shifts) throws DuplicateShiftException { + requireAllNonNull(shifts); + final UniqueShiftList replacement = new UniqueShiftList(); + for (final Shift shift : shifts) { + replacement.add(shift); + } + setShifts(replacement); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asObservableList() { + return FXCollections.unmodifiableObservableList(internalList); + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueShiftList // instanceof handles nulls + && this.internalList.equals(((UniqueShiftList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } +} +``` +###### \java\seedu\ptman\storage\XmlAdaptedShift.java +``` java +/** + * JAXB-friendly version of the Shift. + */ +public class XmlAdaptedShift { + + public static final String MISSING_FIELD_MESSAGE_FORMAT_SHIFT = "Shifts's %s field is missing!"; + public static final String DECRYPT_FAIL_MESSAGE = "Cannot decrypt %s."; + + @XmlElement(required = true) + private String date; + @XmlElement(required = true) + private String startTime; + @XmlElement(required = true) + private String endTime; + @XmlElement(required = true) + private String capacity; + + @XmlElement + private List employees = new ArrayList<>(); + + /** + * Constructs an XmlAdaptedShift. + * This is the no-arg constructor that is required by JAXB. + */ + public XmlAdaptedShift() {} + + /** + * Constructs an {@code XmlAdaptedShift} with the given shift details. + */ + public XmlAdaptedShift(String date, String startTime, String endTime, + String capacity, List employees) { + try { + this.date = encrypt(date); + this.startTime = encrypt(startTime); + this.endTime = encrypt(endTime); + this.capacity = encrypt(capacity); + } catch (Exception e) { + setAttributesFromStrings(date, startTime, endTime, capacity); + } + + if (employees != null) { + this.employees = new ArrayList<>(employees); + } + } + + /** + * Converts a given Shift into this class for JAXB use. + * + * @param source future changes to this will not affect the created XmlAdaptedShift + */ + public XmlAdaptedShift(Shift source) { + try { + date = encrypt(source.getDate().toString()); + startTime = encrypt(source.getStartTime().toString()); + endTime = encrypt(source.getEndTime().toString()); + capacity = encrypt(source.getCapacity().toString()); + } catch (Exception e) { + setAttributesFromSource(source); + } + + employees = new ArrayList<>(); + for (Employee employee : source.getEmployeeList()) { + employees.add(new XmlAdaptedEmployee(employee)); + } + } + + public void setAttributesFromSource(Shift source) { + date = source.getDate().toString(); + startTime = source.getStartTime().toString(); + endTime = source.getEndTime().toString(); + capacity = source.getCapacity().toString(); + } + + public void setAttributesFromStrings(String date, String startTime, String endTime, String capacity) { + this.date = date; + this.startTime = startTime; + this.endTime = endTime; + this.capacity = capacity; + } + + /** + * Decrypts date + * @return + * @throws IllegalValueException + */ + private Date decryptDate() throws IllegalValueException { + String decryptedDate; + try { + decryptedDate = decrypt(this.date); + } catch (Exception e) { + throw new IllegalValueException(String.format(DECRYPT_FAIL_MESSAGE, Date.class.getSimpleName())); + } + if (decryptedDate == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT_SHIFT, + Date.class.getSimpleName())); + } + if (!Date.isValidDate(decryptedDate)) { + throw new IllegalValueException(Date.MESSAGE_DATE_CONSTRAINTS); + } + return new Date(decryptedDate); + } + + /** + * Decryptes time + * @param time + * @return + * @throws IllegalValueException + */ + private Time decryptTime(String time) throws IllegalValueException { + String decryptedTime; + try { + decryptedTime = decrypt(time); + } catch (Exception e) { + throw new IllegalValueException(String.format(DECRYPT_FAIL_MESSAGE, Time.class.getSimpleName())); + } + if (decryptedTime == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT_SHIFT, + Time.class.getSimpleName())); + } + if (!Time.isValidTime(decryptedTime)) { + throw new IllegalValueException(Time.MESSAGE_TIME_CONSTRAINTS); + } + return new Time(decryptedTime); + } + + /** + * Decryptes capacity + * @return + * @throws IllegalValueException + */ + private Capacity decryptCapacity() throws IllegalValueException { + String decryptedCapacity; + try { + decryptedCapacity = decrypt(this.capacity); + } catch (Exception e) { + throw new IllegalValueException(String.format(DECRYPT_FAIL_MESSAGE, Capacity.class.getSimpleName())); + } + if (decryptedCapacity == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT_SHIFT, + Capacity.class.getSimpleName())); + } + if (!Capacity.isValidCapacity(decryptedCapacity)) { + throw new IllegalValueException(Capacity.MESSAGE_CAPACITY_CONSTRAINTS); + } + return new Capacity(decryptedCapacity); + } + + /** + * Converts this jaxb-friendly adapted shift object into the model's Shift object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted shift + */ + public Shift toModelType() throws IllegalValueException { + final List employees = new ArrayList<>(); + for (XmlAdaptedEmployee employee : this.employees) { + employees.add(employee.toModelType()); + } + + final Date date = decryptDate(); + final Time startTime = decryptTime(this.startTime); + final Time endTime = decryptTime(this.endTime); + final Capacity capacity = decryptCapacity(); + + return new Shift(date, startTime, endTime, capacity, employees); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof XmlAdaptedShift)) { + return false; + } + + XmlAdaptedShift otherShift = (XmlAdaptedShift) other; + return Objects.equals(date, otherShift.date) + && Objects.equals(startTime, otherShift.startTime) + && Objects.equals(endTime, otherShift.endTime) + && Objects.equals(capacity, otherShift.capacity) + && employees.equals(otherShift.employees); + } +} +``` diff --git a/collated/test/SunBangjie.md b/collated/test/SunBangjie.md new file mode 100644 index 000000000000..c3c3264d13a0 --- /dev/null +++ b/collated/test/SunBangjie.md @@ -0,0 +1,50 @@ +# SunBangJie +###### \java\seedu\ptman\logic\parser\PartTimeManagerParserTest.java +``` java + @Test + public void parseCommand_editoutlet() throws Exception { + String name = "EditedOutlet"; + String operatingHours = "10:00-17:00"; + String outletContact = "91234567"; + String outletEmail = "EditedOutlet@gmail.com"; + EditOutletCommand command = (EditOutletCommand) parser.parseCommand(EditOutletCommand.COMMAND_WORD + + " " + PREFIX_OUTLET_NAME + name + + " " + PREFIX_OPERATING_HOURS + operatingHours + + " " + PREFIX_OUTLET_CONTACT + outletContact + + " " + PREFIX_OUTLET_EMAIL + outletEmail); + assertEquals(new EditOutletCommand(new OutletName(name), new OperatingHours(operatingHours), + new OutletContact(outletContact), new OutletEmail(outletEmail)), command); + } + + @Test + public void parseCommand_editoutletAlias() throws Exception { + String name = "EditedOutlet"; + String operatingHours = "10:00-17:00"; + String outletContact = "91234567"; + String outletEmail = "EditedOutlet@gmail.com"; + EditOutletCommand command = (EditOutletCommand) parser.parseCommand(EditOutletCommand.COMMAND_ALIAS + + " " + PREFIX_OUTLET_NAME + name + + " " + PREFIX_OPERATING_HOURS + operatingHours + + " " + PREFIX_OUTLET_CONTACT + outletContact + + " " + PREFIX_OUTLET_EMAIL + outletEmail); + assertEquals(new EditOutletCommand(new OutletName(name), new OperatingHours(operatingHours), + new OutletContact(outletContact), new OutletEmail(outletEmail)), command); + } + + @Test + public void parseCommand_announcement() throws Exception { + String announcement = "new announcement."; + AnnouncementCommand command = (AnnouncementCommand) parser.parseCommand(AnnouncementCommand.COMMAND_WORD + + " " + announcement); + assertEquals(new AnnouncementCommand(new Announcement(announcement)), command); + } + + @Test + public void parseCommand_announcementAlias() throws Exception { + String announcement = "new announcement."; + AnnouncementCommand command = (AnnouncementCommand) parser.parseCommand(AnnouncementCommand.COMMAND_ALIAS + + " " + announcement); + assertEquals(new AnnouncementCommand(new Announcement(announcement)), command); + } + +``` diff --git a/collated/test/hzxcaryn.md b/collated/test/hzxcaryn.md new file mode 100644 index 000000000000..e25b5fc4e372 --- /dev/null +++ b/collated/test/hzxcaryn.md @@ -0,0 +1,613 @@ +# hzxcaryn +###### \java\guitests\guihandles\AdminModeDisplayHandle.java +``` java +/** + * A handler for the {@code AdminModeDetails} of the UI + */ +public class AdminModeDisplayHandle extends NodeHandle