Skip to content

Commit

Permalink
Add sorting for person attributes
Browse files Browse the repository at this point in the history
Unable to sort clients by their attributes.

Addition of sort will allow convenient access when looking up clients.

Let's
* make person attributes comparable, by implementing the interface.
* Add sorting command and parser.
* Add person comparator to abstract to logic for handling comparators.
* Add tests in model, commands and parser.
* Update ui to refresh client list after every command execute.
  • Loading branch information
solomonng2001 committed Mar 25, 2024
1 parent 2552b3d commit 9fdd34e
Show file tree
Hide file tree
Showing 6 changed files with 313 additions and 37 deletions.
50 changes: 47 additions & 3 deletions src/main/java/seedu/address/logic/commands/SortCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import static java.util.Objects.requireNonNull;
import static seedu.address.logic.parser.CliSyntax.PREFIX_SORT_ORDER;

import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
import seedu.address.model.person.PersonComparator;
import seedu.address.model.person.SortCriteria;
Expand All @@ -24,7 +26,7 @@ public class SortCommand extends Command {
+ "priority "
+ PREFIX_SORT_ORDER + "desc";

public static final String MESSAGE_SUCCESS = "Client list sorted.";
private static final String MESSAGE_SUCCESS = "Client list sorted by %s in %s order.";
private final SortCriteria sortCriteria;
private final SortOrder sortOrder;

Expand All @@ -40,9 +42,51 @@ public SortCommand(SortCriteria sortCriteria, SortOrder sortOrder) {
}

@Override
public CommandResult execute(Model model) {
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
model.sortFilteredPersonList(PersonComparator.getComparator(sortCriteria, sortOrder));
return new CommandResult(MESSAGE_SUCCESS);
return new CommandResult(getMessageSuccess(sortCriteria, sortOrder));
}

/**
* Returns a success message based on the sort criteria and sort order.
*
* @param sortCriteria the sort criteria
* @param sortOrder the sort order
* @return the success message
*/
public static String getMessageSuccess(SortCriteria sortCriteria, SortOrder sortOrder)
throws IllegalArgumentException {
if (sortCriteria == null || sortOrder == null) {
throw new IllegalArgumentException("SortCriteria and SortOrder cannot be null.");
}
if (sortCriteria == SortCriteria.INVALID || sortOrder == SortOrder.INVALID) {
return String.format(MESSAGE_SUCCESS, SortCriteria.NAME, SortOrder.ASC);
}
return String.format(MESSAGE_SUCCESS, sortCriteria, sortOrder);
}

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

// instanceof handles nulls
if (!(other instanceof SortCommand)) {
return false;
}

SortCommand otherSortCommand = (SortCommand) other;
return sortCriteria.equals(otherSortCommand.sortCriteria)
&& sortOrder.equals(otherSortCommand.sortOrder);
}

@Override
public String toString() {
return new ToStringBuilder(this)
.add("sortCriteria", sortCriteria)
.add("sortOrder", sortOrder)
.toString();
}
}
5 changes: 3 additions & 2 deletions src/main/java/seedu/address/model/ModelManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class ModelManager implements Model {

private final AddressBook addressBook;
private final UserPrefs userPrefs;
private final FilteredList<Person> filteredPersons;
private FilteredList<Person> filteredPersons;
private final DisplayClient displayClient;

/**
Expand Down Expand Up @@ -167,7 +167,8 @@ public void updateFilteredPersonList(Predicate<Person> predicate) {
@Override
public void sortFilteredPersonList(Comparator<Person> comparator) {
requireNonNull(comparator);
filteredPersons.sort(comparator);
ObservableList<Person> sortedList = this.addressBook.getPersonList().sorted(comparator);
filteredPersons = new FilteredList<>(sortedList);
}

@Override
Expand Down
35 changes: 33 additions & 2 deletions src/main/java/seedu/address/ui/MainWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,29 @@ void fillInnerParts() {
commandBoxPlaceholder.getChildren().add(commandBox.getRoot());
}

/**
* Clears the placeholders of client list panel.
*/
private void clearClientListPanel() {
clientListPanelPlaceholder.getChildren().clear();
}

/**
* Fills up the placeholders of client list panel.
*/
private void addClientListPanel() {
clientListPanel = new ClientListPanel(logic.getFilteredPersonList());
clientListPanelPlaceholder.getChildren().add(clientListPanel.getRoot());
}

/**
* Refreshes the client list panel.
*/
private void refreshClientListPanel() {
clearClientListPanel();
addClientListPanel();
}

/**
* Fills up placeholders of client view panel.
*/
Expand Down Expand Up @@ -178,6 +201,15 @@ private void refreshClientViewPanel() {
}
}

/**
* Refreshes the client view panel, client list panel and reminders panel.
*/
private void refreshAllPanels() {
refreshClientViewPanel();
refreshClientListPanel();
// refreshRemindersPanel();
}

// /**
// * Fills up placeholders of reminders panel.
// */
Expand Down Expand Up @@ -257,8 +289,7 @@ private CommandResult executeCommand(String commandText) throws CommandException
logger.info("Result: " + commandResult.getFeedbackToUser());
resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser());

refreshClientViewPanel();
// refreshRemindersPanel();
refreshAllPanels();

if (commandResult.isShowHelp()) {
handleHelp();
Expand Down
102 changes: 101 additions & 1 deletion src/test/java/seedu/address/logic/commands/SortCommandTest.java
Original file line number Diff line number Diff line change
@@ -1,2 +1,102 @@
package seedu.address.logic.commands;public class SortCommandTest {
package seedu.address.logic.commands;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;

import org.junit.jupiter.api.Test;

import seedu.address.model.AddressBook;
import seedu.address.model.ModelManager;
import seedu.address.model.UserPrefs;
import seedu.address.model.person.PersonComparator;
import seedu.address.model.person.SortCriteria;
import seedu.address.model.person.SortOrder;

public class SortCommandTest {

private final ModelManager model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
private final String invalidSortPriorityAsc = SortCommand.getMessageSuccess(SortCriteria.PRIORITY, SortOrder.ASC);

@Test
public void constructor_nullArguments_throwsNullPointerException() {
// null sort criteria
assertThrows(NullPointerException.class, () -> new SortCommand(null, SortOrder.ASC));
// null sort order
assertThrows(NullPointerException.class, () -> new SortCommand(SortCriteria.PRIORITY, null));
// both null
assertThrows(NullPointerException.class, () -> new SortCommand(null, null));
}

@Test
public void execute_sortNoPersonsModel_sortSuccesstul() {
ModelManager expectedEmptyModel = new ModelManager(new AddressBook(new AddressBook()), new UserPrefs());
expectedEmptyModel.sortFilteredPersonList(PersonComparator.getComparator(SortCriteria.PRIORITY, SortOrder.ASC));
CommandTestUtil.assertCommandSuccess(new SortCommand(SortCriteria.PRIORITY, SortOrder.ASC),
new ModelManager(new AddressBook(new AddressBook()), new UserPrefs()),
invalidSortPriorityAsc, expectedEmptyModel);
}

@Test
public void execute_sortPersonsModel_sortSuccessful() {
ModelManager expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
expectedModel.sortFilteredPersonList(PersonComparator.getComparator(SortCriteria.PRIORITY, SortOrder.ASC));
CommandTestUtil.assertCommandSuccess(new SortCommand(SortCriteria.PRIORITY, SortOrder.ASC), model,
invalidSortPriorityAsc, expectedModel);
}

@Test
public void execute_invalidSortCriteria_sortSuccessful() {
ModelManager expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
SortCommand sortCommand = new SortCommand(SortCriteria.INVALID, SortOrder.ASC);
expectedModel.sortFilteredPersonList(PersonComparator.getComparator(SortCriteria.INVALID, SortOrder.ASC));
String expectedMessage = SortCommand.getMessageSuccess(SortCriteria.INVALID, SortOrder.ASC);
CommandTestUtil.assertCommandSuccess(sortCommand, model, expectedMessage, expectedModel);
}

@Test
public void execute_invalidSortOrder_sortSuccessful() {
ModelManager expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
SortCommand sortCommand = new SortCommand(SortCriteria.PRIORITY, SortOrder.INVALID);
expectedModel.sortFilteredPersonList(PersonComparator.getComparator(SortCriteria.PRIORITY, SortOrder.INVALID));
String expectedMessage = SortCommand.getMessageSuccess(SortCriteria.PRIORITY, SortOrder.INVALID);
CommandTestUtil.assertCommandSuccess(sortCommand, model, expectedMessage, expectedModel);
}

@Test
public void equals() {
SortCommand sortPriorityAscCommand = new SortCommand(SortCriteria.PRIORITY, SortOrder.ASC);
SortCommand sortPriorityDescCommand = new SortCommand(SortCriteria.PRIORITY, SortOrder.DESC);
SortCommand sortNameAscCommand = new SortCommand(SortCriteria.NAME, SortOrder.ASC);

// same object -> returns true
assertTrue(sortPriorityAscCommand.equals(sortPriorityAscCommand));

// same values -> returns true
SortCommand sortPriorityAscCommandCopy = new SortCommand(SortCriteria.PRIORITY, SortOrder.ASC);
assertTrue(sortPriorityAscCommand.equals(sortPriorityAscCommandCopy));

// different types -> returns false
assertFalse(sortPriorityAscCommand.equals(1));

// null -> returns false
assertFalse(sortPriorityAscCommand.equals(null));

// different sort criteria -> returns false
assertFalse(sortPriorityAscCommand.equals(sortNameAscCommand));

// different sort order -> returns false
assertFalse(sortPriorityAscCommand.equals(sortPriorityDescCommand));
}

@Test
public void toStringMethod() {
SortCommand sortPriorityAscCommand = new SortCommand(SortCriteria.PRIORITY, SortOrder.ASC);
String expected = SortCommand.class.getCanonicalName()
+ "{sortCriteria=" + SortCriteria.PRIORITY
+ ", sortOrder=" + SortOrder.ASC + "}";
assertEquals(expected, sortPriorityAscCommand.toString());
}
}
130 changes: 129 additions & 1 deletion src/test/java/seedu/address/logic/parser/SortCommandParserTest.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,138 @@
package seedu.address.logic.parser;

import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_SORT_ORDER;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;

import org.junit.jupiter.api.Test;

import seedu.address.logic.Messages;
import seedu.address.logic.commands.SortCommand;
import seedu.address.model.person.SortCriteria;
import seedu.address.model.person.SortOrder;

public class SortCommandParserTest {
public static final String VALID_SORT_NAME_ASC = SortCriteria.NAME + " " + PREFIX_SORT_ORDER + SortOrder.ASC;
public static final String VALID_SORT_NAME_DESC = SortCriteria.NAME + " " + PREFIX_SORT_ORDER + SortOrder.DESC;
public static final String VALID_SORT_PHONE_ASC = SortCriteria.PHONE + " " + PREFIX_SORT_ORDER + SortOrder.ASC;
public static final String VALID_SORT_PHONE_DESC = SortCriteria.PHONE + " " + PREFIX_SORT_ORDER + SortOrder.DESC;
public static final String VALID_SORT_EMAIL_ASC = SortCriteria.EMAIL + " " + PREFIX_SORT_ORDER + SortOrder.ASC;
public static final String VALID_SORT_EMAIL_DESC = SortCriteria.EMAIL + " " + PREFIX_SORT_ORDER + SortOrder.DESC;
public static final String VALID_SORT_ADDRESS_ASC = SortCriteria.ADDRESS + " " + PREFIX_SORT_ORDER + SortOrder.ASC;
public static final String VALID_SORT_ADDRESS_DESC = SortCriteria.ADDRESS + " " + PREFIX_SORT_ORDER
+ SortOrder.DESC;
public static final String VALID_SORT_PRIORITY_ASC = SortCriteria.PRIORITY + " " + PREFIX_SORT_ORDER
+ SortOrder.ASC;
public static final String VALID_SORT_PRIORITY_DESC = SortCriteria.PRIORITY + " " + PREFIX_SORT_ORDER
+ SortOrder.DESC;
public static final String VALID_SORT_BIRTHDAY_ASC = SortCriteria.BIRTHDAY + " " + PREFIX_SORT_ORDER
+ SortOrder.ASC;
public static final String VALID_SORT_BIRTHDAY_DESC = SortCriteria.BIRTHDAY + " " + PREFIX_SORT_ORDER
+ SortOrder.DESC;
public static final String VALID_SORT_LASTMET_ASC = SortCriteria.LASTMET + " " + PREFIX_SORT_ORDER + SortOrder.ASC;
public static final String VALID_SORT_LASTMET_DESC = SortCriteria.LASTMET + " " + PREFIX_SORT_ORDER
+ SortOrder.DESC;
public static final String VALID_SORT_SCHEDULE_ASC = SortCriteria.SCHEDULE + " " + PREFIX_SORT_ORDER
+ SortOrder.ASC;
public static final String VALID_SORT_SCHEDULE_DESC = SortCriteria.SCHEDULE + " " + PREFIX_SORT_ORDER
+ SortOrder.DESC;

private final SortCommandParser parser = new SortCommandParser();

@Test
public void parse_allFieldsPresent_success() {

// whitespace only preamble
assertParseSuccess(parser, PREAMBLE_WHITESPACE + VALID_SORT_NAME_ASC,
new SortCommand(SortCriteria.NAME, SortOrder.ASC));
assertParseSuccess(parser, PREAMBLE_WHITESPACE + VALID_SORT_NAME_DESC,
new SortCommand(SortCriteria.NAME, SortOrder.DESC));
assertParseSuccess(parser, PREAMBLE_WHITESPACE + VALID_SORT_PHONE_ASC,
new SortCommand(SortCriteria.PHONE, SortOrder.ASC));
assertParseSuccess(parser, PREAMBLE_WHITESPACE + VALID_SORT_PHONE_DESC,
new SortCommand(SortCriteria.PHONE, SortOrder.DESC));
assertParseSuccess(parser, PREAMBLE_WHITESPACE + VALID_SORT_EMAIL_ASC,
new SortCommand(SortCriteria.EMAIL, SortOrder.ASC));
assertParseSuccess(parser, PREAMBLE_WHITESPACE + VALID_SORT_EMAIL_DESC,
new SortCommand(SortCriteria.EMAIL, SortOrder.DESC));
assertParseSuccess(parser, PREAMBLE_WHITESPACE + VALID_SORT_ADDRESS_ASC,
new SortCommand(SortCriteria.ADDRESS, SortOrder.ASC));
assertParseSuccess(parser, PREAMBLE_WHITESPACE + VALID_SORT_ADDRESS_DESC,
new SortCommand(SortCriteria.ADDRESS, SortOrder.DESC));
assertParseSuccess(parser, PREAMBLE_WHITESPACE + VALID_SORT_PRIORITY_ASC,
new SortCommand(SortCriteria.PRIORITY, SortOrder.ASC));
assertParseSuccess(parser, PREAMBLE_WHITESPACE + VALID_SORT_PRIORITY_DESC,
new SortCommand(SortCriteria.PRIORITY, SortOrder.DESC));
assertParseSuccess(parser, PREAMBLE_WHITESPACE + VALID_SORT_BIRTHDAY_ASC,
new SortCommand(SortCriteria.BIRTHDAY, SortOrder.ASC));
assertParseSuccess(parser, PREAMBLE_WHITESPACE + VALID_SORT_BIRTHDAY_DESC,
new SortCommand(SortCriteria.BIRTHDAY, SortOrder.DESC));
assertParseSuccess(parser, PREAMBLE_WHITESPACE + VALID_SORT_LASTMET_ASC,
new SortCommand(SortCriteria.LASTMET, SortOrder.ASC));
assertParseSuccess(parser, PREAMBLE_WHITESPACE + VALID_SORT_LASTMET_DESC,
new SortCommand(SortCriteria.LASTMET, SortOrder.DESC));
assertParseSuccess(parser, PREAMBLE_WHITESPACE + VALID_SORT_SCHEDULE_ASC,
new SortCommand(SortCriteria.SCHEDULE, SortOrder.ASC));
assertParseSuccess(parser, PREAMBLE_WHITESPACE + VALID_SORT_SCHEDULE_DESC,
new SortCommand(SortCriteria.SCHEDULE, SortOrder.DESC));

// without whitespace preamble
assertParseSuccess(parser, VALID_SORT_NAME_ASC, new SortCommand(SortCriteria.NAME, SortOrder.ASC));
assertParseSuccess(parser, VALID_SORT_NAME_DESC, new SortCommand(SortCriteria.NAME, SortOrder.DESC));
assertParseSuccess(parser, VALID_SORT_PHONE_ASC, new SortCommand(SortCriteria.PHONE, SortOrder.ASC));
assertParseSuccess(parser, VALID_SORT_PHONE_DESC, new SortCommand(SortCriteria.PHONE, SortOrder.DESC));
assertParseSuccess(parser, VALID_SORT_EMAIL_ASC, new SortCommand(SortCriteria.EMAIL, SortOrder.ASC));
assertParseSuccess(parser, VALID_SORT_EMAIL_DESC, new SortCommand(SortCriteria.EMAIL, SortOrder.DESC));
assertParseSuccess(parser, VALID_SORT_ADDRESS_ASC, new SortCommand(SortCriteria.ADDRESS, SortOrder.ASC));
assertParseSuccess(parser, VALID_SORT_ADDRESS_DESC, new SortCommand(SortCriteria.ADDRESS, SortOrder.DESC));
assertParseSuccess(parser, VALID_SORT_PRIORITY_ASC, new SortCommand(SortCriteria.PRIORITY, SortOrder.ASC));
assertParseSuccess(parser, VALID_SORT_PRIORITY_DESC, new SortCommand(SortCriteria.PRIORITY, SortOrder.DESC));
assertParseSuccess(parser, VALID_SORT_BIRTHDAY_ASC, new SortCommand(SortCriteria.BIRTHDAY, SortOrder.ASC));
assertParseSuccess(parser, VALID_SORT_BIRTHDAY_DESC, new SortCommand(SortCriteria.BIRTHDAY, SortOrder.DESC));
assertParseSuccess(parser, VALID_SORT_LASTMET_ASC, new SortCommand(SortCriteria.LASTMET, SortOrder.ASC));
assertParseSuccess(parser, VALID_SORT_LASTMET_DESC, new SortCommand(SortCriteria.LASTMET, SortOrder.DESC));
assertParseSuccess(parser, VALID_SORT_SCHEDULE_ASC, new SortCommand(SortCriteria.SCHEDULE, SortOrder.ASC));
assertParseSuccess(parser, VALID_SORT_SCHEDULE_DESC, new SortCommand(SortCriteria.SCHEDULE, SortOrder.DESC));
}

@Test
public void parse_repeatedParameter_failure() {
// multiple sort orders
assertParseFailure(parser, VALID_SORT_NAME_ASC + " " + PREFIX_SORT_ORDER + SortOrder.DESC,
Messages.getErrorMessageForDuplicatePrefixes(PREFIX_SORT_ORDER));
}

@Test
public void parse_compulsoryFieldMissing_failure() {
String expectedMessage = String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE);

// missing sort criteria
assertParseFailure(parser, " " + PREFIX_SORT_ORDER + SortOrder.ASC, expectedMessage);

// missing sort order
assertParseFailure(parser, SortCriteria.NAME.toString(), expectedMessage);
assertParseFailure(parser, SortCriteria.NAME.toString() + " " + SortOrder.DESC, expectedMessage);

// missing both sort criteria and sort order
assertParseFailure(parser, "", expectedMessage);
}

@Test
public void parse_invalidValue_failure() {
// invalid sort criteria
assertParseFailure(parser, "invalid" + " " + PREFIX_SORT_ORDER + SortOrder.ASC,
SortCriteria.MESSAGE_CONSTRAINTS);
assertParseFailure(parser, "x" + " " + PREFIX_SORT_ORDER + SortOrder.DESC,
SortCriteria.MESSAGE_CONSTRAINTS);
assertParseFailure(parser, " name " + VALID_SORT_NAME_ASC,
SortCriteria.MESSAGE_CONSTRAINTS);

// invalid sort order
assertParseFailure(parser, SortCriteria.NAME + " " + PREFIX_SORT_ORDER + "invalid",
SortOrder.MESSAGE_CONSTRAINTS);
assertParseFailure(parser, SortCriteria.NAME + " " + PREFIX_SORT_ORDER + "x",
SortOrder.MESSAGE_CONSTRAINTS);
assertParseFailure(parser, SortCriteria.NAME + " " + PREFIX_SORT_ORDER,
SortOrder.MESSAGE_CONSTRAINTS);
}
}
Loading

0 comments on commit 9fdd34e

Please sign in to comment.