Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Appointment Feature #57

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, thanks for rectifying this

Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ This project is based on the AddressBook-Level3 project created by the [SE-EDU i
* The project simulates an address book in the form of a desktop application ued for managing contact details.
* It is **written in OOP fashion**. It provides a **reasonably well-written** code base.
* It is **well-designed and documented**.
* It is named `Murphy's List` (`ML` for short) because it was named after the medical prodigy `Sean Murphy` from the popular show `The Good Doctor`
* It is named `Murphy's List` (`ML` for short) because it was named after the medical prodigy `Sean Murphy` from the popular show `The Good Doctor`
* For the detailed documentation of this project, see our **[Developer Guide](https://github.com/AY2425S1-CS2103T-W11-1a/tp/blob/master/docs/DeveloperGuide.md)**.
85 changes: 85 additions & 0 deletions src/main/java/seedu/address/logic/commands/AppointmentCommand.java

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neat implementation, code is readable and variables are grouped nicely, keep it up

Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package seedu.address.logic.commands;

import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;

import java.util.List;

import seedu.address.commons.core.index.Index;
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.person.Appointment;
import seedu.address.model.person.Person;

/**
* Sets or updates an appointment for the person identified by the displayed index from the address book.
*/
public class AppointmentCommand extends Command {

public static final String COMMAND_WORD = "appointment";

public static final String MESSAGE_USAGE = COMMAND_WORD
+ ": Sets an appointment for the person identified by the index number used in the displayed person list.\n"
+ "Parameters: INDEX (must be a positive integer), a/APPOINTMENT (in the format DD-MM-YYYY HH:MM)\n"
+ "Example: " + COMMAND_WORD + " 1 a/25-12-2024 14:30";

public static final String MESSAGE_SET_APPOINTMENT_SUCCESS = "Set Appointment for Person: %1$s";
public static final String MESSAGE_INVALID_APPOINTMENT_FORMAT = "Please use DD-MM-YYYY HH:MM.";
public static final String MESSAGE_NO_APPOINTMENT_PROVIDED = "Please provide a valid appointment date.";

private final Index index;
private final String appointmentString; // Store appointment string to convert to LocalDateTime

/**
* Constructor to create an AppointmentCommand.
*
* @param targetIndex Index of the person to set the appointment for.
* @param appointmentString String of the appointment in the format a/DD-MM-YYYY HH:MM.
*/
public AppointmentCommand(Index targetIndex, String appointmentString) {
this.index = targetIndex;
this.appointmentString = appointmentString.trim();
}

@Override
public CommandResult execute(Model model) throws CommandException {
List<Person> lastShownList = model.getFilteredPersonList();

Check warning on line 47 in src/main/java/seedu/address/logic/commands/AppointmentCommand.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/logic/commands/AppointmentCommand.java#L47

Added line #L47 was not covered by tests

if (this.index.getZeroBased() >= lastShownList.size()) {
throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);

Check warning on line 50 in src/main/java/seedu/address/logic/commands/AppointmentCommand.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/logic/commands/AppointmentCommand.java#L50

Added line #L50 was not covered by tests
}

Person personToEdit = lastShownList.get(index.getZeroBased());
Person editedPerson = new Person(
personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(), personToEdit.getNric(),
personToEdit.getAddress(), personToEdit.getRemark(), personToEdit.getTags(),

Check warning on line 56 in src/main/java/seedu/address/logic/commands/AppointmentCommand.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/logic/commands/AppointmentCommand.java#L53-L56

Added lines #L53 - L56 were not covered by tests
new Appointment(this.appointmentString));
model.setPerson(personToEdit, editedPerson);
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
return new CommandResult("Set appointment for " + personToEdit.getName() + " on " + this.appointmentString);

Check warning on line 60 in src/main/java/seedu/address/logic/commands/AppointmentCommand.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/logic/commands/AppointmentCommand.java#L58-L60

Added lines #L58 - L60 were not covered by tests
}

@Override
public boolean equals(Object other) {
if (other == this) {
return true;

Check warning on line 66 in src/main/java/seedu/address/logic/commands/AppointmentCommand.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/logic/commands/AppointmentCommand.java#L66

Added line #L66 was not covered by tests
}

if (!(other instanceof AppointmentCommand)) {
return false;

Check warning on line 70 in src/main/java/seedu/address/logic/commands/AppointmentCommand.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/logic/commands/AppointmentCommand.java#L70

Added line #L70 was not covered by tests
}

AppointmentCommand otherCommand = (AppointmentCommand) other;
return index.equals(otherCommand.index)
&& appointmentString.equals(otherCommand.appointmentString);
}

@Override
public String toString() {
return new ToStringBuilder(this)
.add("targetIndex", index)
.add("appointmentString", appointmentString)
.toString();

Check warning on line 83 in src/main/java/seedu/address/logic/commands/AppointmentCommand.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/logic/commands/AppointmentCommand.java#L80-L83

Added lines #L80 - L83 were not covered by tests
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ private static Person createEditedPerson(Person personToEdit, EditPersonDescript
Set<Tag> updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags());

return new Person(updatedName, updatedPhone, updatedEmail, updatedNric,
updatedAddress, updatedRemark, updatedTags);
updatedAddress, updatedRemark, updatedTags, null);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public CommandResult execute(Model model) throws CommandException {
Person personToEdit = lastShownList.get(index.getZeroBased());
Person editedPerson = new Person(
personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(), personToEdit.getNric(),
personToEdit.getAddress(), remark, personToEdit.getTags());
personToEdit.getAddress(), remark, personToEdit.getTags(), null);

model.setPerson(personToEdit, editedPerson);
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public AddCommand parse(String args) throws ParseException {
Remark remark = new Remark("");
Set<Tag> tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));

Person person = new Person(name, phone, email, nric, address, remark, tagList);
Person person = new Person(name, phone, email, nric, address, remark, tagList, null);

return new AddCommand(person);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import seedu.address.commons.core.LogsCenter;
import seedu.address.logic.commands.AddCommand;
import seedu.address.logic.commands.AppointmentCommand;
import seedu.address.logic.commands.ClearCommand;
import seedu.address.logic.commands.Command;
import seedu.address.logic.commands.DeleteCommand;
Expand Down Expand Up @@ -76,6 +77,9 @@
case ExitCommand.COMMAND_WORD:
return new ExitCommand();

case AppointmentCommand.COMMAND_WORD:
return new AppointmentCommandParser().parse(arguments);

Check warning on line 81 in src/main/java/seedu/address/logic/parser/AddressBookParser.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/logic/parser/AddressBookParser.java#L81

Added line #L81 was not covered by tests

case HelpCommand.COMMAND_WORD:
return new HelpCommand();

Expand Down
126 changes: 126 additions & 0 deletions src/main/java/seedu/address/logic/parser/AppointmentCommandParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package seedu.address.logic.parser;

import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;

import seedu.address.commons.core.index.Index;
import seedu.address.logic.commands.AppointmentCommand;
import seedu.address.logic.parser.exceptions.ParseException;

/**
* Parses input arguments and creates a new AppointmentCommand object.
*/
public class AppointmentCommandParser implements Parser<AppointmentCommand> {

private static final String APPOINTMENT_PREFIX = "a/";

/**
* Parses the given {@code String} of arguments in the context of the AppointmentCommand
* and returns an AppointmentCommand object for execution.
* @throws ParseException if the user input does not conform to the expected format.
*/
public AppointmentCommand parse(String args) throws ParseException {
String trimmedArgs = args.trim();

// Split the arguments into components
String[] argParts = trimmedArgs.split("\\s+");

// Check if the input has exactly 3 parts: INDEX, a/DATE, TIME
if (argParts.length != 3 || !argParts[1].startsWith(APPOINTMENT_PREFIX)) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AppointmentCommand.MESSAGE_USAGE));
}

String dayString;
String dayPart;
String monthPart;
String yearPart;

try {
// Argument parts validation
dayString = argParts[1].substring(2, 12);

// Extract day, month, and year
dayPart = dayString.substring(0, 2);
monthPart = dayString.substring(3, 5);
yearPart = dayString.substring(6, 10);

} catch (StringIndexOutOfBoundsException e) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AppointmentCommand.MESSAGE_USAGE));
}


// Check if the string is in the correct format (with '-' at the right positions)
if (!dayString.substring(2, 3).equals("-") || !dayString.substring(5, 6).equals("-")) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AppointmentCommand.MESSAGE_USAGE));

Check warning on line 53 in src/main/java/seedu/address/logic/parser/AppointmentCommandParser.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/logic/parser/AppointmentCommandParser.java#L53

Added line #L53 was not covered by tests
}

int day;
int month;
int year;
try {
day = Integer.parseInt(dayPart);
month = Integer.parseInt(monthPart);
year = Integer.parseInt(yearPart);
} catch (NumberFormatException e) {
throw new ParseException("Invalid date format. Day, month, and year must be numbers.");
}

// Validate day and month ranges
if (day < 1 || day > 31 || month < 1 || month > 12) {
throw new ParseException("Day and months must be valid.");
}

// Time validation
String timeString;
String hourPart;
String minutePart;

try {
timeString = argParts[2];
hourPart = timeString.substring(0, 2);
minutePart = timeString.substring(3, 5);
} catch (StringIndexOutOfBoundsException e) {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, AppointmentCommand.MESSAGE_USAGE));
}

if (!timeString.substring(2, 3).equals(":")) {
throw new ParseException("Invalid time format. Please use HH:MM.");

Check warning on line 87 in src/main/java/seedu/address/logic/parser/AppointmentCommandParser.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/logic/parser/AppointmentCommandParser.java#L87

Added line #L87 was not covered by tests
}
int hour;
int minute;
try {
hour = Integer.parseInt(hourPart);
minute = Integer.parseInt(minutePart);
} catch (NumberFormatException e) {
throw new ParseException("Invalid time format. Hour and minute must be numbers.");
}
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
throw new ParseException("Hour and minute must be valid.");
}

// Parse the index
String indexString = argParts[0];
Index index;
try {
int indexValue = Integer.parseInt(indexString);
if (indexValue <= 0) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
AppointmentCommand.MESSAGE_USAGE));
}
index = Index.fromZeroBased(indexValue - 1); // Adjust to zero-based index
} catch (NumberFormatException e) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AppointmentCommand.MESSAGE_USAGE));
}

// Extract and validate the appointment date and time
String appointmentDateTime = trimmedArgs.substring(trimmedArgs.indexOf(APPOINTMENT_PREFIX) + 2).trim();

// If appointmentDateTime is not valid, throw an error
if (appointmentDateTime.isEmpty()) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AppointmentCommand.MESSAGE_USAGE));

Check warning on line 120 in src/main/java/seedu/address/logic/parser/AppointmentCommandParser.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/logic/parser/AppointmentCommandParser.java#L120

Added line #L120 was not covered by tests
}

// Return the constructed AppointmentCommand
return new AppointmentCommand(index, appointmentDateTime);
}
}
53 changes: 53 additions & 0 deletions src/main/java/seedu/address/model/person/Appointment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package seedu.address.model.person;

import static java.util.Objects.requireNonNull;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
* Represents a Person's appointment in the address book.
*/
public class Appointment {

public static final String MESSAGE_CONSTRAINTS = "Appointments should be of the format DD-MM-YYYY HH:MM";

public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm");

public final LocalDateTime value;

/**
* Constructs an {@code Appointment}.
*
* @param dateTimeString A valid appointment date string.
*/
public Appointment(String dateTimeString) {
requireNonNull(dateTimeString);
this.value = LocalDateTime.parse(dateTimeString, FORMATTER);
}

@Override
public String toString() {
return value.format(FORMATTER);
}

@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}

if (!(other instanceof Appointment)) {
return false;
}

Appointment otherAppointment = (Appointment) other;
return (value.toString()).equals(otherAppointment.value.toString());
}

@Override
public int hashCode() {
return value.hashCode();
}

}
27 changes: 23 additions & 4 deletions src/main/java/seedu/address/model/person/Person.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,20 @@
private final Address address;
private final Remark remark;
private final Set<Tag> tags = new HashSet<>();
private Appointment appointment; // New appointment field

/**
* Every field must be present and not null.
*/
public Person(Name name, Phone phone, Email email, Nric nric, Address address, Remark remark, Set<Tag> tags) {
public Person(
Name name,
Phone phone,
Email email,
Nric nric,
Address address,
Remark remark,
Set<Tag> tags,
Appointment appointment) {
requireAllNonNull(name, phone, email, nric, address, tags);
this.name = name;
this.phone = phone;
Expand All @@ -39,6 +48,7 @@
this.address = address;
this.remark = remark;
this.tags.addAll(tags);
this.appointment = appointment; // Appointment field initialized
}

public Name getName() {
Expand All @@ -61,6 +71,14 @@
return address;
}

public Appointment getAppointment() {
return appointment;
}

public void setAppointment(Appointment appointment) {
this.appointment = appointment;
}

Check warning on line 80 in src/main/java/seedu/address/model/person/Person.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Person.java#L79-L80

Added lines #L79 - L80 were not covered by tests

public Remark getRemark() {
return remark;
}
Expand Down Expand Up @@ -107,13 +125,14 @@
&& email.equals(otherPerson.email)
&& nric.equals(otherPerson.nric)
&& address.equals(otherPerson.address)
&& tags.equals(otherPerson.tags);
&& tags.equals(otherPerson.tags)
&& Objects.equals(appointment, otherPerson.appointment); // Include appointment in equality check
}

@Override
public int hashCode() {
// use this method for custom fields hashing instead of implementing your own
return Objects.hash(name, phone, email, nric, address, tags);
return Objects.hash(name, phone, email, nric, address, tags, appointment); // Include appointment in hashCode

Check warning on line 135 in src/main/java/seedu/address/model/person/Person.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/model/person/Person.java#L135

Added line #L135 was not covered by tests
}

@Override
Expand All @@ -126,7 +145,7 @@
.add("address", address)
.add("remark", remark)
.add("tags", tags)
.add("appointment", appointment) // Include appointment in toString
.toString();
}

}
Loading