diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java index df5adc5fa72..a8608f56068 100644 --- a/src/main/java/seedu/address/logic/Messages.java +++ b/src/main/java/seedu/address/logic/Messages.java @@ -5,6 +5,7 @@ import java.util.stream.Stream; import seedu.address.logic.parser.Prefix; +import seedu.address.model.appointment.AppointmentDescriptor; import seedu.address.model.person.Person; import seedu.address.model.person.PersonDescriptor; @@ -56,4 +57,20 @@ public static String formatPerson(PersonDescriptor person) { return builder.toString(); } + /** + * Formats the {@code appointment} for display to the user. + */ + public static String formatAppointment(AppointmentDescriptor appointment) { + final StringBuilder builder = new StringBuilder(); + builder.append(appointment.getAppointmentType()) + .append("; Id: ") + .append(appointment.getPersonId()) + .append("; Date and Time") + .append(appointment.getAppointmentDateTime()) + .append("; Sickness: ") + .append(appointment.getSickness()) + .append("; Medicine: ") + .append(appointment.getMedicine()); + return builder.toString(); + } } diff --git a/src/main/java/seedu/address/logic/commands/AddAppointmentCommand.java b/src/main/java/seedu/address/logic/commands/AddAppointmentCommand.java new file mode 100644 index 00000000000..543a47045b0 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddAppointmentCommand.java @@ -0,0 +1,121 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_APPOINTMENT_TYPE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATETIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEDICINE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PERSON_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SICKNESS; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.appointment.AppointmentDescriptor; + +/** + * Adds an appointment to the appointment book. + */ +public class AddAppointmentCommand extends AddCommand { + public static final String COMMAND_WORD = "add appt"; + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Adds an appointment to the appointment book. " + + "Parameters: " + + PREFIX_APPOINTMENT_TYPE + "Appointment_TYPE" + + PREFIX_PERSON_ID + "PersonId" + + PREFIX_DATETIME + "Date" + + PREFIX_SICKNESS + "Sickness" + + PREFIX_MEDICINE + "Medicine" + + "Example: " + COMMAND_WORD + + PREFIX_APPOINTMENT_TYPE + "Check up" + + PREFIX_DATETIME + "16/10/2024 12:00" + + PREFIX_PERSON_ID + "1" + + PREFIX_SICKNESS + "Common Cold" + + PREFIX_MEDICINE + "Paracetamol"; + + public static final String MESSAGE_SUCCESS = "New appointment added: %1$s"; + public static final String MESSAGE_DUPLICATE_APPOINTMENT = "This appointment already exists in the address book"; + + private final AppointmentDescriptor toAdd; + + /** + * Creates an AddAppointmentCommand to add the specified {@code Appointment} + */ + public AddAppointmentCommand(AppointmentDescriptor appointment) { + requireNonNull(appointment); + toAdd = appointment; + } + + /* + * Checks if the entity being added to model already exists. + */ + @Override + protected boolean alreadyExists(Model model) { + return model.hasAppointment(toAdd); + }; + + /* + * Adds the entity to the model. + */ + @Override + protected void addEntity(Model model) { + model.addAppointment(toAdd); + }; + + /* + * Returns success message to display upon adding entity. + */ + @Override + protected String getSuccessMessage() { + return MESSAGE_SUCCESS; + }; + + /* + * Returns the message to display when there is a duplicate. + */ + @Override + protected String getDuplicateEntityMessage() { + return MESSAGE_DUPLICATE_APPOINTMENT; + }; + + /** + * Formats the entity for displaying in the success message. + */ + @Override + protected String formatEntity() { + return Messages.formatAppointment(toAdd); + }; + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (alreadyExists(model)) { + throw new CommandException(getDuplicateEntityMessage()); + } + + addEntity(model); + return new CommandResult(String.format(getSuccessMessage(), formatEntity())); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AddAppointmentCommand otherAddAppointmentCommand)) { + return false; + } + + return toAdd.equals(otherAddAppointmentCommand.toAdd); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("toAdd", toAdd) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java index 326595bb3d3..128b99b1a05 100644 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java @@ -2,19 +2,30 @@ import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_APPOINTMENT_TYPE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATETIME; import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MEDICINE; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PERSON_ID; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_SICKNESS; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import static seedu.address.logic.parser.ParserUtil.APPOINTMENT_ENTITY_STRING; import static seedu.address.logic.parser.ParserUtil.PERSON_ENTITY_STRING; +import java.time.LocalDateTime; import java.util.Set; import java.util.stream.Stream; +import seedu.address.logic.commands.AddAppointmentCommand; import seedu.address.logic.commands.AddCommand; import seedu.address.logic.commands.AddPersonCommand; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.appointment.AppointmentDescriptor; +import seedu.address.model.appointment.AppointmentType; +import seedu.address.model.appointment.Medicine; +import seedu.address.model.appointment.Sickness; import seedu.address.model.person.Address; import seedu.address.model.person.Email; import seedu.address.model.person.Name; @@ -34,7 +45,9 @@ public class AddCommandParser implements Parser { */ public AddCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_TAG, PREFIX_PERSON_ID, PREFIX_DATETIME, + PREFIX_APPOINTMENT_TYPE, PREFIX_SICKNESS, PREFIX_MEDICINE); String entityType = argMultimap.getEntityType(); switch (entityType) { @@ -54,7 +67,26 @@ public AddCommand parse(String args) throws ParseException { return new AddPersonCommand(person); case APPOINTMENT_ENTITY_STRING: - //TODO: Instantiate and return AddAppointmentCommand + if (!arePrefixesPresent(argMultimap, PREFIX_PERSON_ID, PREFIX_DATETIME, + PREFIX_APPOINTMENT_TYPE, PREFIX_SICKNESS, PREFIX_MEDICINE)) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddAppointmentCommand.MESSAGE_USAGE)); + } + + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_PERSON_ID, PREFIX_DATETIME, + PREFIX_APPOINTMENT_TYPE, PREFIX_SICKNESS, PREFIX_MEDICINE); + int personId = ParserUtil.parsePersonId(argMultimap.getValue(PREFIX_PERSON_ID).get()); + LocalDateTime appointmentDateTime = ParserUtil.parseAppointmentDateTime( + argMultimap.getValue(PREFIX_DATETIME).get()); + AppointmentType appointmentType = ParserUtil.parseAppointmentType( + argMultimap.getValue(PREFIX_APPOINTMENT_TYPE).get()); + Sickness sickness = ParserUtil.parseSickness(argMultimap.getValue(PREFIX_SICKNESS).get()); + Medicine medicine = ParserUtil.parseMedicine(argMultimap.getValue(PREFIX_MEDICINE).get()); + + AppointmentDescriptor appointment = new AppointmentDescriptor( + appointmentType, appointmentDateTime, personId, sickness, medicine); + + return new AddAppointmentCommand(appointment); default: throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); } diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java index da7e04b14f7..6f32a1742bd 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -12,6 +12,8 @@ public class CliSyntax { public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); public static final Prefix PREFIX_TAG = new Prefix("t/"); + public static final Prefix PREFIX_PERSON_ID = new Prefix("i/"); + public static final Prefix PREFIX_DATETIME = new Prefix("d/"); public static final Prefix PREFIX_APPOINTMENT_TYPE = new Prefix("t/"); public static final Prefix PREFIX_MEDICINE = new Prefix("m/"); public static final Prefix PREFIX_SICKNESS = new Prefix("s/"); diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java index 9d2ddd134a1..ac3006f64bd 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java @@ -2,6 +2,9 @@ import static java.util.Objects.requireNonNull; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -9,6 +12,9 @@ import seedu.address.commons.core.index.Index; import seedu.address.commons.util.StringUtil; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.appointment.AppointmentType; +import seedu.address.model.appointment.Medicine; +import seedu.address.model.appointment.Sickness; import seedu.address.model.person.Address; import seedu.address.model.person.Email; import seedu.address.model.person.Name; @@ -123,4 +129,82 @@ public static Set parseTags(Collection tags) throws ParseException } return tagSet; } + + /** + * Parses a {@code String personId} into a {@code int personId}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code personId} is invalid. + */ + public static int parsePersonId(String personId) throws ParseException { + requireNonNull(personId); + String trimmerPersonId = personId.trim(); + int parsedPersonId = Integer.parseInt(trimmerPersonId); + if (parsedPersonId < 0) { + throw new ParseException("person Id needs to be a positive intger"); + } + return parsedPersonId; + }; + + + /** + * Parses a {@code String appointmentDateTime} into a {@code LocalDateTime}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code appointmentDateTime} is invalid. + */ + public static LocalDateTime parseAppointmentDateTime(String appointmentDateTime) throws ParseException { + requireNonNull(appointmentDateTime); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + try { + return LocalDateTime.parse(appointmentDateTime, formatter); + } catch (DateTimeParseException e) { + throw new ParseException("Invalid date-time format. Expected format: YYYY-MM-DD HH:mm:ss", e); + } + } + + /** + * Parses a {@code String appointmentType} into a {@code AppointmentType}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code appointmentType} is invalid. + */ + public static AppointmentType parseAppointmentType(String appointmentType) throws ParseException { + requireNonNull(appointmentType); + String trimmedAppointmentType = appointmentType.trim(); + if (!AppointmentType.isValidAppointmentType(trimmedAppointmentType)) { + throw new ParseException(AppointmentType.MESSAGE_CONSTRAINTS); + } + return new AppointmentType(trimmedAppointmentType); + } + + /** + * Parses a {@code String sickness} into a {@code Sickness}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code sickness} is invalid. + */ + public static Sickness parseSickness(String sickness) throws ParseException { + requireNonNull(sickness); + String trimmedSickness = sickness.trim(); + if (!Sickness.isValidSickness(sickness)) { + throw new ParseException(Sickness.MESSAGE_CONSTRAINTS); + } + return new Sickness(trimmedSickness); + } + + /** + * Parses a {@code String medicine} into a {@code Medicine}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code medicine} is invalid. + */ + public static Medicine parseMedicine(String medicine) throws ParseException { + requireNonNull(medicine); + String trimmedMedicine = medicine.trim(); + if (!Medicine.isValidMedicine(medicine)) { + throw new ParseException(Medicine.MESSAGE_CONSTRAINTS); + } + return new Medicine(trimmedMedicine); + } } diff --git a/src/main/java/seedu/address/model/AppointmentBook.java b/src/main/java/seedu/address/model/AppointmentBook.java index 21e9d51eaef..413a92faee9 100644 --- a/src/main/java/seedu/address/model/AppointmentBook.java +++ b/src/main/java/seedu/address/model/AppointmentBook.java @@ -7,6 +7,7 @@ import javafx.collections.ObservableList; import seedu.address.commons.util.ToStringBuilder; import seedu.address.model.appointment.Appointment; +import seedu.address.model.appointment.AppointmentDescriptor; import seedu.address.model.appointment.UniqueAppointmentList; /** @@ -67,6 +68,14 @@ public boolean hasAppointment(Appointment appointment) { return appointments.contains(appointment); } + /** + * Returns true if an appointment with the same identity as {@code appointment} exists in the appointment book. + */ + public boolean hasAppointment(AppointmentDescriptor appointmentDescriptor) { + requireNonNull(appointmentDescriptor); + return appointments.contains(appointmentDescriptor); + } + /** * Adds an appointment to the appointment book. * The appointment must not already exist in the appointment book. @@ -76,6 +85,16 @@ public String addAppointment(Appointment appointment) { return appointment.toString(); } + /** + * Adds an appointment to the appointment book. + * The appointment must not already exist in the appointment book. + */ + public String addAppointment(AppointmentDescriptor appointmentDescriptor) { + requireNonNull(appointmentDescriptor); + appointments.add(new Appointment(appointmentDescriptor)); + return appointmentDescriptor.toString(); + } + /** * Replaces the given appointment {@code target} in the list with {@code editedAppointment}. * {@code target} must exist in the appointment book. diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index b16a496544c..a5071b69dde 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -6,6 +6,7 @@ import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; import seedu.address.model.appointment.Appointment; +import seedu.address.model.appointment.AppointmentDescriptor; import seedu.address.model.person.Person; import seedu.address.model.person.PersonDescriptor; @@ -104,7 +105,7 @@ public interface Model { /** * Returns true if an appointment with the same identity as {@code appointment} exists in the appointment book. */ - boolean hasAppointment(Appointment appointment); + boolean hasAppointment(AppointmentDescriptor appointmentDescriptor); /** * Deletes the given appointment. @@ -116,7 +117,7 @@ public interface Model { * Adds the given appointment. * {@code appointment} must not already exist in the appointment book. */ - void addAppointment(Appointment appointment); + void addAppointment(AppointmentDescriptor appointmentDescriptor); /** * Replaces the given appointment {@code target} with {@code editedAppointment}. diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 538f5108393..23af996e8b5 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -12,6 +12,7 @@ import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; import seedu.address.model.appointment.Appointment; +import seedu.address.model.appointment.AppointmentDescriptor; import seedu.address.model.person.Person; import seedu.address.model.person.PersonDescriptor; @@ -151,7 +152,7 @@ public ReadOnlyAppointmentBook getAppointmentBook() { } @Override - public boolean hasAppointment(Appointment appointment) { + public boolean hasAppointment(AppointmentDescriptor appointment) { requireNonNull(appointment); return appointmentBook.hasAppointment(appointment); } @@ -162,7 +163,7 @@ public void deleteAppointment(Appointment target) { } @Override - public void addAppointment(Appointment appointment) { + public void addAppointment(AppointmentDescriptor appointment) { appointmentBook.addAppointment(appointment); updateFilteredAppointmentList(PREDICATE_SHOW_ALL_APPOINTMENTS); } diff --git a/src/main/java/seedu/address/model/appointment/Appointment.java b/src/main/java/seedu/address/model/appointment/Appointment.java index 681b3d4e06b..c9453b6fa5b 100644 --- a/src/main/java/seedu/address/model/appointment/Appointment.java +++ b/src/main/java/seedu/address/model/appointment/Appointment.java @@ -40,6 +40,17 @@ public Appointment( this.medicine = medicine; } + /** + * Creates an appointment object using appointmentDescriptor. + */ + public Appointment(AppointmentDescriptor appointmentDescriptor) { + this.appointmentType = appointmentDescriptor.getAppointmentType(); + this.appointmentDateTime = appointmentDescriptor.getAppointmentDateTime(); + this.personId = appointmentDescriptor.getPersonId(); + this.sickness = appointmentDescriptor.getSickness(); + this.medicine = appointmentDescriptor.getMedicine(); + } + public AppointmentType getAppointmentType() { return appointmentType; } diff --git a/src/main/java/seedu/address/model/appointment/AppointmentDescriptor.java b/src/main/java/seedu/address/model/appointment/AppointmentDescriptor.java new file mode 100644 index 00000000000..65260740532 --- /dev/null +++ b/src/main/java/seedu/address/model/appointment/AppointmentDescriptor.java @@ -0,0 +1,119 @@ +package seedu.address.model.appointment; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.time.LocalDateTime; +import java.util.Objects; + +import seedu.address.commons.util.ToStringBuilder; + +/** + * Represents an appointment's details in the address book. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class AppointmentDescriptor { + private final AppointmentType appointmentType; + private final LocalDateTime appointmentDateTime; + private final int personId; + + // Data fields + private final Sickness sickness; + private final Medicine medicine; + + /** + * Every field must be present and not null. + */ + public AppointmentDescriptor( + AppointmentType appointmentType, + LocalDateTime appointmentDateTime, + int personId, + Sickness sickness, + Medicine medicine) { + requireAllNonNull(appointmentType, personId, sickness, medicine); + this.appointmentType = appointmentType; + this.appointmentDateTime = appointmentDateTime; + this.personId = personId; + this.sickness = sickness; + this.medicine = medicine; + } + + public int getPersonId() { + return personId; + } + + public AppointmentType getAppointmentType() { + return this.appointmentType; + } + + public LocalDateTime getAppointmentDateTime() { + return this.appointmentDateTime; + } + + public Sickness getSickness() { + return this.sickness; + } + + public Medicine getMedicine() { + return this.medicine; + } + + /** + * Returns true if both appointments have the same personId and appointmentDateTime. + * This defines a weaker notion of equality between two appointments. + */ + public boolean isSameAppointment(AppointmentDescriptor otherAppointment) { + if (otherAppointment == this) { + return true; + } + + return otherAppointment != null + && otherAppointment.getPersonId() == getPersonId() + && otherAppointment.getAppointmentDateTime().equals(getAppointmentDateTime()); + } + + /** + * Returns true if both appointments have the same name. + * This defines a weaker notion of equality between two appointments. + */ + public boolean isSameAppointment(Appointment otherAppointment) { + return otherAppointment != null + && otherAppointment.getPersonId() == getPersonId() + && otherAppointment.getAppointmentDateTime().equals(getAppointmentDateTime()); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AppointmentDescriptor)) { + return false; + } + + AppointmentDescriptor otherAppointment = (AppointmentDescriptor) other; + return appointmentType.equals(otherAppointment.getAppointmentType()) + && appointmentDateTime.equals(otherAppointment.getAppointmentDateTime()) + && personId == otherAppointment.getPersonId() + && sickness.equals(otherAppointment.getSickness()) + && medicine.equals(otherAppointment.getMedicine()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(appointmentType, appointmentDateTime, personId, sickness, medicine); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("Appointment Type", appointmentType) + .add("Appointment Date-Time", appointmentDateTime) + .add("Person Id", personId) + .add("Sickness", sickness) + .add("medicine", medicine) + .toString(); + } +} diff --git a/src/main/java/seedu/address/model/appointment/UniqueAppointmentList.java b/src/main/java/seedu/address/model/appointment/UniqueAppointmentList.java index e8f460ebbe7..7af229b6a78 100644 --- a/src/main/java/seedu/address/model/appointment/UniqueAppointmentList.java +++ b/src/main/java/seedu/address/model/appointment/UniqueAppointmentList.java @@ -38,6 +38,14 @@ public boolean contains(Appointment toCheck) { return internalList.stream().anyMatch(toCheck::isSameAppointment); } + /** + * Returns true if the list contains an equivalent appointment as the given argument. + */ + public boolean contains(AppointmentDescriptor toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameAppointment); + } + /** * Adds an appointment to the list. * The appointment must not already exist in the list.