Skip to content

Commit

Permalink
Merge pull request nus-cs2103-AY2324S1#111 from jx124/add-sort-meetin…
Browse files Browse the repository at this point in the history
…g-time-feature

Add sort meeting time feature
  • Loading branch information
garylow2001 authored Nov 1, 2023
2 parents 68a3831 + a06a569 commit 78c9e21
Show file tree
Hide file tree
Showing 18 changed files with 222 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package seedu.address.logic.commands;

import static java.util.Objects.requireNonNull;

import java.util.function.Predicate;

import seedu.address.model.Model;
import seedu.address.model.person.Person;

/**
* Lists persons in the address book sorted by their meeting times.
*/
public class SortMeetingTimeCommand extends Command {

public static final String COMMAND_WORD = "sortmeeting";

public static final String MESSAGE_SUCCESS = "Sorted all meeting times chronologically";

public static final Predicate<Person> PREDICATE_HAS_MEETING_TIME = person -> person.getMeetingTime().isPresent();
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
model.sortFilteredPersonList();
return new CommandResult(MESSAGE_SUCCESS);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import seedu.address.logic.commands.ListClientCommand;
import seedu.address.logic.commands.ListCommand;
import seedu.address.logic.commands.ListLeadCommand;
import seedu.address.logic.commands.SortMeetingTimeCommand;
import seedu.address.logic.commands.ViewCommand;
import seedu.address.logic.parser.exceptions.ParseException;

Expand Down Expand Up @@ -105,6 +106,9 @@ public Command parseCommand(String userInput) throws ParseException {
case ConvertLeadToClientCommand.COMMAND_WORD:
return new ConvertLeadToClientCommandParser().parse(arguments);

case SortMeetingTimeCommand.COMMAND_WORD:
return new SortMeetingTimeCommand();

default:
logger.finer("This user input caused a ParseException: " + userInput);
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/seedu/address/model/Model.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,6 @@ public interface Model {
* Updates the filter of the filtered person list to just the specified person {@code predicate}.
*/
void view(Person clientToView);

void sortFilteredPersonList();
}
21 changes: 20 additions & 1 deletion src/main/java/seedu/address/model/ModelManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@

import static java.util.Objects.requireNonNull;
import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
import static seedu.address.logic.commands.SortMeetingTimeCommand.PREDICATE_HAS_MEETING_TIME;

import java.nio.file.Path;
import java.util.function.Predicate;
import java.util.logging.Logger;

import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import seedu.address.commons.core.GuiSettings;
import seedu.address.commons.core.LogsCenter;
import seedu.address.model.person.Client;
import seedu.address.model.person.Lead;
import seedu.address.model.person.Person;
import seedu.address.model.person.PersonMeetingTimeComparator;

/**
* Represents the in-memory model of the address book data.
Expand All @@ -24,6 +27,7 @@ public class ModelManager implements Model {
private final AddressBook addressBook;
private final UserPrefs userPrefs;
private final FilteredList<Person> filteredPersons;
private final SortedList<Person> sortedFilteredPersons;

/**
* Initializes a ModelManager with the given addressBook and userPrefs.
Expand All @@ -36,6 +40,9 @@ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs
this.addressBook = new AddressBook(addressBook);
this.userPrefs = new UserPrefs(userPrefs);
filteredPersons = new FilteredList<>(this.addressBook.getPersonList());

// default sortedFilteredPersons is identical to filteredPersons due to null comparator
sortedFilteredPersons = new SortedList<>(filteredPersons, null);
}

public ModelManager() {
Expand Down Expand Up @@ -137,15 +144,27 @@ public void setPerson(Person target, Person editedPerson) {
*/
@Override
public ObservableList<Person> getFilteredPersonList() {
return filteredPersons;
return sortedFilteredPersons;
}

@Override
public void updateFilteredPersonList(Predicate<Person> predicate) {
requireNonNull(predicate);

// We need to set comparator to null first since PersonMeetingTimeComparator
// throws an exception when sorting Persons without a meeting time,
// thus passing a null comparator means the list will not be sorted.
sortedFilteredPersons.setComparator(null);

filteredPersons.setPredicate(predicate);
}

@Override
public void sortFilteredPersonList() {
filteredPersons.setPredicate(PREDICATE_HAS_MEETING_TIME);
sortedFilteredPersons.setComparator(new PersonMeetingTimeComparator());
}

@Override
public boolean equals(Object other) {
if (other == this) {
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/seedu/address/model/person/MeetingTime.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ public class MeetingTime {

// Replace uuuu in format to yyyy to not confuse users
public static final String MESSAGE_CONSTRAINTS = "Meeting time should be in the format of "
+ DATE_TIME_FORMAT.replace("u", "y");
+ DATE_TIME_FORMAT.replace("u", "y")
+ " and have a valid date and time";

public final LocalDateTime value;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package seedu.address.model.person;

import java.util.Comparator;
import java.util.NoSuchElementException;

/**
* Implements the comparator to sort Persons based on their meeting times.
*/
public class PersonMeetingTimeComparator implements Comparator<Person> {

/**
* Compares the meeting times of two Person objects.
* Both Person objects should have a valid meeting time that is not null.
*
* @param person1 the first Person object to be compared.
* @param person2 the second Person object to be compared.
* @return The relative order of the Person objects.
*/
@Override
public int compare(Person person1, Person person2) throws NoSuchElementException {
MeetingTime meetingTime1 = person1.getMeetingTime().orElseThrow();
MeetingTime meetingTime2 = person2.getMeetingTime().orElseThrow();

return meetingTime1.value.compareTo(meetingTime2.value);
}
}
12 changes: 6 additions & 6 deletions src/main/java/seedu/address/model/util/SampleDataUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,28 @@ public class SampleDataUtil {
public static Person[] getSamplePersons() {
return new Person[] {
new Client(new Name("Alex Yeoh"), new Phone("87438807"), new Email("[email protected]"),
new Address("Blk 30 Geylang Street 29, #06-40"), Optional.of(new MeetingTime("10/10/2023 14:30")),
new Address("Blk 30 Geylang Street 29, #06-40"), Optional.of(new MeetingTime("12/10/2023 14:30")),
getTagSet("friends")),
new Client(new Name("Bernice Yu"), new Phone("99272758"), new Email("[email protected]"),
new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"),
Optional.of(new MeetingTime("10/10/2023 14:30")),
Optional.empty(),
getTagSet("colleagues", "friends")),
new Client(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("[email protected]"),
new Address("Blk 11 Ang Mo Kio Street 74, #11-04"),
Optional.of(new MeetingTime("10/10/2023 14:30")),
Optional.of(new MeetingTime("11/10/2023 13:30")),
getTagSet("neighbours")),
new Lead(new Name("David Li"), new Phone("91031282"), new Email("[email protected]"),
new Address("Blk 436 Serangoon Gardens Street 26, #16-43"),
new KeyMilestone("01/12/2023"), Optional.of(new MeetingTime("10/10/2023 14:30")),
new KeyMilestone("01/12/2023"), Optional.of(new MeetingTime("11/10/2023 10:30")),
getTagSet("family")),
new Lead(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("[email protected]"),
new Address("Blk 47 Tampines Street 20, #17-35"),
new KeyMilestone("01/12/2023"),
Optional.of(new MeetingTime("10/10/2023 14:30")),
Optional.empty(),
getTagSet("classmates")),
new Lead(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("[email protected]"),
new Address("Blk 45 Aljunied Street 85, #11-31"),
new KeyMilestone("01/12/2023"), Optional.of(new MeetingTime("10/10/2023 14:30")),
new KeyMilestone("01/12/2023"), Optional.of(new MeetingTime("12/10/2023 16:30")),
getTagSet("colleagues"))
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"type": "client",
"address" : "311, Clementi Ave 2, #02-25",
"keyMilestone": null,
"meetingTime" : "12/12/2020 12:00",
"meetingTime" : "13/12/2023 12:00",
"tags" : [ "owesMoney", "friends" ]
}, {
"name" : "Carl Kurz",
Expand All @@ -25,7 +25,7 @@
"type": "client",
"keyMilestone": null,
"address" : "wall street",
"meetingTime" : null,
"meetingTime" : "12/12/2023 14:00",
"tags" : []
}, {
"name" : "Daniel Meier",
Expand Down Expand Up @@ -61,7 +61,7 @@
"type": "lead",
"keyMilestone": "01/12/2023",
"address" : "4th street",
"meetingTime" : null,
"meetingTime" : "13/12/2023 08:00",
"tags" : []
} ]
}
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ public void updateFilteredPersonList(Predicate<Person> predicate) {
throw new AssertionError("This method should not be called.");
}

@Override
public void sortFilteredPersonList() {
throw new AssertionError("This method should not be called.");
}

@Override
public void view(Person personToView) {
throw new AssertionError("This method should not be called.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ public ObservableList<Person> getFilteredPersonList() {
public void updateFilteredPersonList(Predicate<Person> predicate) {
throw new AssertionError("This method should not be called.");
}

@Override
public void sortFilteredPersonList() {
throw new AssertionError("This method should not be called.");
}
@Override
public void view(Person personToView) {
throw new AssertionError("This method should not be called.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ public ObservableList<Person> getFilteredPersonList() {
public void updateFilteredPersonList(Predicate<Person> predicate) {
throw new AssertionError("This method should not be called.");
}

@Override
public void sortFilteredPersonList() {
throw new AssertionError("This method should not be called.");
}
@Override
public void view(Person personToView) {
throw new AssertionError("This method should not be called.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ public ObservableList<Person> getFilteredPersonList() {
public void updateFilteredPersonList(Predicate<Person> predicate) {
throw new AssertionError("This method should not be called.");
}

@Override
public void sortFilteredPersonList() {
throw new AssertionError("This method should not be called.");
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import seedu.address.model.person.Client;
import seedu.address.model.person.Lead;
import seedu.address.model.person.Person;
import seedu.address.model.person.PersonMeetingTimeComparator;
import seedu.address.testutil.PersonBuilder;

public class ConvertLeadToClientCommandTest {
Expand Down Expand Up @@ -226,6 +227,18 @@ public void updateFilteredPersonList(Predicate<Person> person) {
);
}

@Override
public void sortFilteredPersonList() {
Predicate<Person> meetingTimePredicate = person -> person.getMeetingTime().isPresent();

filteredPersons.setAll(
leadsAdded.stream()
.filter(meetingTimePredicate)
.sorted(new PersonMeetingTimeComparator())
.collect(Collectors.toList())
);
}

@Override
public void setPerson(Person target, Person converted) {
leadsAdded.remove(target);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public void deleteMeeting_personIsLead() {

@Test
public void deleteMeeting_personNoMeetingTime_returnsPerson() {
Index indexPersonWithNoMeeting = Index.fromOneBased(3); // Third person in typical address book
Index indexPersonWithNoMeeting = Index.fromOneBased(4); // Third person in typical address book
Person personToDeleteMeeting = model.getFilteredPersonList()
.get(indexPersonWithNoMeeting.getZeroBased());

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

import static org.junit.jupiter.api.Assertions.assertEquals;
import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
import static seedu.address.logic.commands.SortMeetingTimeCommand.MESSAGE_SUCCESS;
import static seedu.address.testutil.TypicalPersons.ALICE;
import static seedu.address.testutil.TypicalPersons.BENSON;
import static seedu.address.testutil.TypicalPersons.CARL;
import static seedu.address.testutil.TypicalPersons.ELLE;
import static seedu.address.testutil.TypicalPersons.GEORGE;
import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;

import java.util.Arrays;

import org.junit.jupiter.api.Test;

import seedu.address.model.Model;
import seedu.address.model.ModelManager;
import seedu.address.model.UserPrefs;

public class SortMeetingTimeCommandTest {
private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());

@Test
public void execute_sortPersonList_correctOrder() {
expectedModel.sortFilteredPersonList();
assertCommandSuccess(new SortMeetingTimeCommand(), model, MESSAGE_SUCCESS, expectedModel);
assertEquals(Arrays.asList(ALICE, ELLE, CARL, GEORGE, BENSON), model.getFilteredPersonList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import seedu.address.logic.commands.AddClientCommand;
import seedu.address.logic.commands.AddLeadCommand;
import seedu.address.logic.commands.ClearCommand;
import seedu.address.logic.commands.ConvertClientToLeadCommand;
import seedu.address.logic.commands.ConvertLeadToClientCommand;
import seedu.address.logic.commands.DeleteCommand;
import seedu.address.logic.commands.DeleteMeetingCommand;
import seedu.address.logic.commands.EditCommand;
Expand All @@ -31,6 +33,7 @@
import seedu.address.logic.commands.ListClientCommand;
import seedu.address.logic.commands.ListCommand;
import seedu.address.logic.commands.ListLeadCommand;
import seedu.address.logic.commands.SortMeetingTimeCommand;
import seedu.address.logic.commands.ViewCommand;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.person.Client;
Expand Down Expand Up @@ -151,4 +154,24 @@ public void parseCommand_listClient() throws Exception {
public void parseCommand_listLead() throws Exception {
assertTrue(parser.parseCommand(ListLeadCommand.COMMAND_WORD) instanceof ListLeadCommand);
}

@Test
public void parseCommand_sortMeetingTime() throws Exception {
assertTrue(parser.parseCommand(SortMeetingTimeCommand.COMMAND_WORD) instanceof SortMeetingTimeCommand);
}

@Test
public void parseCommand_convertLeadToClient() throws Exception {
ConvertLeadToClientCommand command = (ConvertLeadToClientCommand) parser.parseCommand(
ConvertLeadToClientCommand.COMMAND_WORD + " " + 3);
assertEquals(new ConvertLeadToClientCommand(Index.fromOneBased(3)), command);
}

@Test
public void parseCommand_convertClientToLead() throws Exception {
ConvertClientToLeadCommand command = (ConvertClientToLeadCommand) parser.parseCommand(
ConvertClientToLeadCommand.COMMAND_WORD + " " + 3);
assertEquals(new ConvertClientToLeadCommand(Index.fromOneBased(3)), command);
}

}
Loading

0 comments on commit 78c9e21

Please sign in to comment.