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 fc6890d commit 2552b3d
Show file tree
Hide file tree
Showing 29 changed files with 805 additions and 12 deletions.
48 changes: 48 additions & 0 deletions src/main/java/seedu/address/logic/commands/SortCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package seedu.address.logic.commands;

import static java.util.Objects.requireNonNull;
import static seedu.address.logic.parser.CliSyntax.PREFIX_SORT_ORDER;

import seedu.address.model.Model;
import seedu.address.model.person.PersonComparator;
import seedu.address.model.person.SortCriteria;
import seedu.address.model.person.SortOrder;

/**
* Sorts the client list in the address book.
*/
public class SortCommand extends Command {

public static final String COMMAND_WORD = "sort";

public static final String MESSAGE_USAGE = COMMAND_WORD
+ ": sorts the client list in the address book. "
+ "Parameters: "
+ "CRITERIA "
+ PREFIX_SORT_ORDER + "ORDER\n"
+ "Example: " + COMMAND_WORD + " "
+ "priority "
+ PREFIX_SORT_ORDER + "desc";

public static final String MESSAGE_SUCCESS = "Client list sorted.";
private final SortCriteria sortCriteria;
private final SortOrder sortOrder;

/**
* Creates a SortCommand to sort the client list.
*/
public SortCommand(SortCriteria sortCriteria, SortOrder sortOrder) {
requireNonNull(sortCriteria);
requireNonNull(sortOrder);

this.sortCriteria = sortCriteria;
this.sortOrder = sortOrder;
}

@Override
public CommandResult execute(Model model) {
requireNonNull(model);
model.sortFilteredPersonList(PersonComparator.getComparator(sortCriteria, sortOrder));
return new CommandResult(MESSAGE_SUCCESS);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import seedu.address.logic.commands.ListCommand;
import seedu.address.logic.commands.MarkCommand;
import seedu.address.logic.commands.ScheduleCommand;
import seedu.address.logic.commands.SortCommand;
import seedu.address.logic.commands.ViewCommand;
import seedu.address.logic.parser.exceptions.ParseException;

Expand Down Expand Up @@ -101,6 +102,9 @@ public Command parseCommand(String userInput) throws ParseException {
case DeletePolicyCommand.COMMAND_WORD:
return new DeletePolicyCommandParser().parse(arguments);

case SortCommand.COMMAND_WORD:
return new SortCommandParser().parse(arguments);

default:
logger.finer("This user input caused a ParseException: " + userInput);
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/seedu/address/logic/parser/CliSyntax.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ public class CliSyntax {
public static final Prefix PREFIX_TAG = new Prefix("t/");
public static final Prefix PREFIX_POLICYNAME = new Prefix("n/");
public static final Prefix PREFIX_POLICYID = new Prefix("i/");

public static final Prefix PREFIX_SORT_ORDER = new Prefix("o/");
}
34 changes: 34 additions & 0 deletions src/main/java/seedu/address/logic/parser/ParserUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import seedu.address.model.person.Name;
import seedu.address.model.person.Phone;
import seedu.address.model.person.Priority;
import seedu.address.model.person.SortCriteria;
import seedu.address.model.person.SortOrder;
import seedu.address.model.policy.Policy;
import seedu.address.model.tag.Tag;

Expand Down Expand Up @@ -207,4 +209,36 @@ public static String parsePolicyId(String policyId) throws ParseException {
}
return trimmedPolicyId;
}

/**
* Parses {@code String sortOrder} into a {@code SortOrder}.
*
* @param sortOrder the sort order
* @return the sort order
* @throws ParseException if the given {@code sortOrder} is invalid
*/
public static SortOrder parseSortOrder(String sortOrder) throws ParseException {
requireNonNull(sortOrder);
String trimmedSortOrder = sortOrder.trim();
if (!SortOrder.isValidSortOrder(trimmedSortOrder)) {
throw new ParseException(SortOrder.MESSAGE_CONSTRAINTS);
}
return SortOrder.getSortOrder(trimmedSortOrder);
}

/**
* Parses {@code String sortCriteria} into a {@code SortCriteria}.
*
* @param sortCriteria the sort criteria
* @return the sort criteria
* @throws ParseException if the given {@code sortCriteria} is invalid
*/
public static SortCriteria parseSortCriteria(String sortCriteria) throws ParseException {
requireNonNull(sortCriteria);
String trimmedSortCriteria = sortCriteria.trim();
if (!SortCriteria.isValidSortCriteria(trimmedSortCriteria)) {
throw new ParseException(SortCriteria.MESSAGE_CONSTRAINTS);
}
return SortCriteria.getSortCriteria(trimmedSortCriteria);
}
}
44 changes: 44 additions & 0 deletions src/main/java/seedu/address/logic/parser/SortCommandParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package seedu.address.logic.parser;

import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_SORT_ORDER;

import java.util.stream.Stream;

import seedu.address.logic.commands.SortCommand;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.person.SortCriteria;
import seedu.address.model.person.SortOrder;

/**
* Parses input arguments and creates a new SortCommand object
*/
public class SortCommandParser implements Parser<SortCommand> {

/**
* Parses the given {@code String} of arguments in the context of the SortCommand
* and returns an SortCommand object for execution.
*/
public SortCommand parse(String args) throws ParseException {
ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_SORT_ORDER);

if (!arePrefixesPresent(argMultimap, PREFIX_SORT_ORDER)
|| argMultimap.getPreamble().isEmpty()) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE));
}

argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_SORT_ORDER);
SortCriteria sortCriteria = ParserUtil.parseSortCriteria(argMultimap.getPreamble());
SortOrder sortOrder = ParserUtil.parseSortOrder(argMultimap.getValue(PREFIX_SORT_ORDER).get());

return new SortCommand(sortCriteria, sortOrder);
}

/**
* Returns true if none of the prefixes contains empty {@code Optional} values in the given
* {@code ArgumentMultimap}.
*/
private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
}
}
7 changes: 7 additions & 0 deletions src/main/java/seedu/address/model/Model.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package seedu.address.model;

import java.nio.file.Path;
import java.util.Comparator;
import java.util.function.Predicate;

import javafx.collections.ObservableList;
Expand Down Expand Up @@ -85,6 +86,12 @@ public interface Model {
*/
void updateFilteredPersonList(Predicate<Person> predicate);

/**
* Sorts the filtered person list by the given {@code comparator}.
* @throws NullPointerException if {@code comparator} is null.
*/
void sortFilteredPersonList(Comparator<Person> comparator);

/**
* Returns the client to be displayed in ClientViewPanel.
*/
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/seedu/address/model/ModelManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;

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

Expand Down Expand Up @@ -163,6 +164,12 @@ public void updateFilteredPersonList(Predicate<Person> predicate) {
filteredPersons.setPredicate(predicate);
}

@Override
public void sortFilteredPersonList(Comparator<Person> comparator) {
requireNonNull(comparator);
filteredPersons.sort(comparator);
}

@Override
public boolean equals(Object other) {
if (other == this) {
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/seedu/address/model/person/Address.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* Represents a Person's address in the address book.
* Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)}
*/
public class Address {
public class Address implements Comparable<Address> {

public static final String MESSAGE_CONSTRAINTS = "Addresses can take any values, and it should not be blank";

Expand Down Expand Up @@ -62,4 +62,8 @@ public int hashCode() {
return value.hashCode();
}

@Override
public int compareTo(Address other) {
return this.value.compareTo(other.value);
}
}
7 changes: 6 additions & 1 deletion src/main/java/seedu/address/model/person/Birthday.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* Represents a Client's birthday in the address book.
* Guarantees: immutable; is valid as declared in {@link #isValidBirthday(String)}
*/
public class Birthday {
public class Birthday implements Comparable<Birthday> {
public static final String MESSAGE_CONSTRAINTS = DateUtil.getMessageConstraintsForDateType("Birthday");
public final LocalDate date;

Expand Down Expand Up @@ -64,4 +64,9 @@ public boolean equals(Object other) {
public int hashCode() {
return date.hashCode();
}

@Override
public int compareTo(Birthday other) {
return date.compareTo(other.date);
}
}
6 changes: 5 additions & 1 deletion src/main/java/seedu/address/model/person/Email.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* Represents a Person's email in the address book.
* Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)}
*/
public class Email {
public class Email implements Comparable<Email> {

private static final String SPECIAL_CHARACTERS = "+_.-";
public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format local-part@domain "
Expand Down Expand Up @@ -76,4 +76,8 @@ public int hashCode() {
return value.hashCode();
}

@Override
public int compareTo(Email other) {
return this.value.compareTo(other.value);
}
}
27 changes: 26 additions & 1 deletion src/main/java/seedu/address/model/person/LastMet.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
/**
* Represents the last met date with a Client in the address book.
*/
public class LastMet {
public class LastMet implements Comparable<LastMet> {
private static long lastMetDuration = 90;
private final LocalDate lastMetDate;
private boolean isOverdue;
Expand Down Expand Up @@ -78,4 +78,29 @@ public String showLastMet() {
public String toString() {
return DateUtil.parseDateToString(this.lastMetDate);
}

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

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

LastMet otherLastMet = (LastMet) other;
return lastMetDate.equals(otherLastMet.lastMetDate);
}

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

@Override
public int compareTo(LastMet other) {
return lastMetDate.compareTo(other.lastMetDate);
}
}
6 changes: 5 additions & 1 deletion src/main/java/seedu/address/model/person/Name.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* Represents a Person's name in the address book.
* Guarantees: immutable; is valid as declared in {@link #isValidName(String)}
*/
public class Name {
public class Name implements Comparable<Name> {

public static final String MESSAGE_CONSTRAINTS =
"Names should only contain alphanumeric characters and spaces, and it should not be blank";
Expand Down Expand Up @@ -64,4 +64,8 @@ public int hashCode() {
return fullName.hashCode();
}

@Override
public int compareTo(Name other) {
return this.fullName.compareTo(other.fullName);
}
}
51 changes: 51 additions & 0 deletions src/main/java/seedu/address/model/person/PersonComparator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package seedu.address.model.person;

import java.util.Comparator;

/**
* Comparator for comparing persons.
*/
public class PersonComparator {
/**
* Returns a comparator based on the sort criteria and sort order.
*
* @param sortCriteria the sort criteria
* @param sortOrder the sort order
* @return the comparator
*/
public static Comparator<Person> getComparator(SortCriteria sortCriteria, SortOrder sortOrder) {
Comparator<Person> comparator;
switch (sortCriteria) {
case NAME:
comparator = Comparator.comparing(Person::getName);
break;
case PHONE:
comparator = Comparator.comparing(Person::getPhone);
break;
case EMAIL:
comparator = Comparator.comparing(Person::getEmail);
break;
case ADDRESS:
comparator = Comparator.comparing(Person::getAddress);
break;
case PRIORITY:
comparator = Comparator.comparing(Person::getPriority);
break;
case BIRTHDAY:
comparator = Comparator.comparing(Person::getBirthday);
break;
case LASTMET:
comparator = Comparator.comparing(Person::getLastMet);
break;
case SCHEDULE:
comparator = Comparator.comparing(Person::getSchedule);
break;
default:
return Comparator.comparing(Person::getName);
}
if (sortOrder == SortOrder.DESC) {
return comparator.reversed();
}
return comparator;
}
}
6 changes: 5 additions & 1 deletion src/main/java/seedu/address/model/person/Phone.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* Represents a Person's phone number in the address book.
* Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)}
*/
public class Phone {
public class Phone implements Comparable<Phone> {

public static final String MESSAGE_CONSTRAINTS =
"Phone numbers should only contain numbers, and it should be at least 3 digits long";
Expand Down Expand Up @@ -57,4 +57,8 @@ public int hashCode() {
return value.hashCode();
}

@Override
public int compareTo(Phone other) {
return this.value.compareTo(other.value);
}
}
Loading

0 comments on commit 2552b3d

Please sign in to comment.