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 15 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();
}

Check warning on line 43 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#L40-L43

Added lines #L40 - L43 were not covered by tests

@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;

Check warning on line 73 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#L73

Added line #L73 was not covered by tests
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
105 changes: 105 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,105 @@
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> {

Check warning on line 12 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#L12

Added line #L12 was not covered by tests

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();

Check warning on line 22 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#L22

Added line #L22 was not covered by tests

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

Check warning on line 25 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#L25

Added line #L25 was not covered by tests

// 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));

Check warning on line 29 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#L29

Added line #L29 was not covered by tests
}

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

Check warning on line 33 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#L33

Added line #L33 was not covered by tests

// 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 37 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#L37

Added line #L37 was not covered by tests
}

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

Check warning on line 43 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#L41-L43

Added lines #L41 - L43 were 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.");
}

Check warning on line 54 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#L49-L54

Added lines #L49 - L54 were not covered by tests

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

Check warning on line 58 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#L58

Added line #L58 was not covered by tests
}

// Time validation
String timeString = argParts[2];
String hourPart = timeString.substring(0, 2);
String minutePart = timeString.substring(3, 5);

Check warning on line 64 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#L62-L64

Added lines #L62 - L64 were not covered by tests
if (!timeString.substring(2, 3).equals(":")) {
throw new ParseException("Invalid time format. Please use HH:MM.");

Check warning on line 66 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#L66

Added line #L66 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.");
}

Check warning on line 75 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#L71-L75

Added lines #L71 - L75 were not covered by tests
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
throw new ParseException("Hour and minute must be valid.");

Check warning on line 77 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#L77

Added line #L77 was not covered by tests
}

// Parse the index
String indexString = argParts[0];

Check warning on line 81 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#L81

Added line #L81 was not covered by tests
Index index;
try {
int indexValue = Integer.parseInt(indexString);

Check warning on line 84 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#L84

Added line #L84 was not covered by tests
if (indexValue <= 0) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,

Check warning on line 86 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#L86

Added line #L86 was not covered by tests
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));
}

Check warning on line 92 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#L89-L92

Added lines #L89 - L92 were not covered by tests

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

Check warning on line 95 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#L95

Added line #L95 was not covered by tests

// 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 99 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#L99

Added line #L99 was not covered by tests
}

// Return the constructed AppointmentCommand
return new AppointmentCommand(index, appointmentDateTime);

Check warning on line 103 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#L103

Added line #L103 was not covered by tests
}
}
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