Skip to content

Commit

Permalink
Merge pull request #164 from whitesnowx/133-multiplesorting
Browse files Browse the repository at this point in the history
Sort Persons Functionality for MeetingList & Implement MultiComparator to Sort multiple attributes
  • Loading branch information
Pluiexo authored Apr 4, 2024
2 parents 5304f36 + 27fe0e2 commit ec037cf
Show file tree
Hide file tree
Showing 25 changed files with 553 additions and 59 deletions.
20 changes: 10 additions & 10 deletions docs/DeveloperGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,24 +267,24 @@ The following activity diagram summarizes what happens when a user executes a ne
#### Design considerations:
**Aspect: Determining order of sorting of an attribute:**

* **Current Design:** Use a configured comparator for each attribute in ascending order.
* Pros: Controlled and more simple for user.
* Cons: Less flexibility and unable to do more advance sorting such as multiple attributes. We must implement a comparator for each attribute used for sorting.

* **Alternative 1:** Get order of sorting from user, prompting for an input in the form of toCompare.
* **Current Design:** Get order of sorting from user, prompting for an input in the form of toCompare.
* Pros: More functionality and more suited to the user's needs.
* Cons: Harder to implement and guide user to use, may have more leeway for error. User unlikely to use this advancement.

**Aspect: Number of Attribute:**
* **Alternative 1:** Use a configured comparator for each attribute in ascending order.
* Pros: Controlled and more simple for user.
* Cons: Less flexibility and unable to do more advance sorting such as multiple attributes. We must implement a comparator for each attribute used for sorting.

* **Current Design:** Only 1 attribute per sort.
* Pros: Easy to implement, controlled and less likely to be used incorrectly. This increase ease of use for users.
* Cons: Limited sorting and lesser functionality.
**Aspect: Number of Attribute:**

* **Alternative 1:** 1 or more attribute per sort.
* **Current Design:** 1 or more attribute per sort.
* Pros: More functionality, more advanced view of contacts.
* Cons: Harder to implement, order of prefix affects priority of attribute and have to specify to user.

* **Alternative 1:** Only 1 attribute per sort.
* Pros: Easy to implement, controlled and less likely to be used incorrectly. This increase ease of use for users.
* Cons: Limited sorting and lesser functionality.

### Find feature

#### How the feature is implemented
Expand Down
6 changes: 5 additions & 1 deletion docs/UserGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,13 @@ Format: `sort [ATTRIBUTE]`
* The order of character priority would be letters (A-Z), numbers (0-9), special characters (!@#$%^&*).
* The capitalisation of the letters do not affect their priority such that `Aaron` will have same priority as `aaron`.
* For attribute with exact same values, the tie-breaker is determined by their added order.
* For sorting of multiple attributes, the weightage will be determined by the order in which it was entered. E.g `sort m/ p/ v/` will sort by contact by their module, among those with equal modules would then be sorted by their phone number and similarly for venue.
* `[ATTRIBUTE]` is to be noted by their prefix. e.g `name` will be `n/`.
* `s/` sorts contacts by person with the earliest meeting
* `meet/` sorts contacts by person with the earliest meeting, followed by alphanumeric order of meeting description

Examples:
* `sort m/ p/` returns person by ascending module codes followed by ascending phone numbers `CS2000 80000000`, `CS2000 90000000`, `CS3000 80000000`followed by `CS3000 90000000`
* `sort n/` returns person by ascending names `Alex`, `Bernice` followed by `Charlotte`
* `sort p/` returns person by ascending phone numbers `87438807`, `91031282` followed by `92492021`<br>
![result for 'sort p/'](images/sortByPhoneNumberResult.png)
Expand Down Expand Up @@ -331,7 +335,7 @@ Action | Format, Examples
**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [m/MODULE] [f/FACULTY] [v/VENUE] [t/TAG]…​ [a/AVAILABILITY]…​`<br> e.g.,`edit 2 n/James Lee e/[email protected]`
**Filter** | `filter [m/MODULE] [f/FACULTY] [t/TAG]… [a/AVAILABILITY]…`<br> e.g., `filter m/CS2100 t/friends`
**Find** | `find KEYWORD [MORE_KEYWORDS]`<br> e.g., `find James Jake`
**Sort** | `sort [ATTRIBUTE]`<br> e.g., `sort p/`
**Sort** | `sort [n/] [p/] [m/] [f/] [v/] [s/] [meet/]...`<br> e.g., `sort n/ p/ m/`
**Add Meeting** | `meeting INDEX [d/DESCRIPTION] [s/DATETIME]`<br> e.g., `meeting 1 d/ Meet for finals preparation s/ 12/04/2024 18:00`
**Set as Favourite** | `fav INDEX`<br> e.g., `fav 3`
**Remove as Favourite** | `unfav INDEX`<br> e.g., `unfav 3`
Expand Down
19 changes: 15 additions & 4 deletions docs/diagrams/SortActivityDiagram.puml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,21 @@ skinparam ArrowFontSize 12
start
:User enters a sort command;

if () then ([attribute is sortable])
:Listed contacts are sorted
based on the given attribute;
else ([else])

if() then ([attributes are not sortable])
:get person from list;
while (have person to compare)
while(attribute value with the next person in the list are equal or no more attributes)
:use next attribute to check;
endwhile
if() then ([person smaller])
:shift front;
else([person bigger])
:shift back;
endif
endwhile
:Shows sorted person list;
else([any attribute is not sortable])
:displays Sort Usage message;
endif
stop
Expand Down
19 changes: 6 additions & 13 deletions docs/diagrams/SortSequenceDiagram.puml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ box Logic LOGIC_COLOR_T1
participant ":LogicManager" as LogicManager LOGIC_COLOR
participant ":StaffConnectParser" as StaffConnectParser LOGIC_COLOR
participant ":SortCommandParser" as SortCommandParser LOGIC_COLOR
participant "<<class>>\nNameComparator" as NameComparator LOGIC_COLOR
participant "c:NameComparator" as NAME_COMPARATOR LOGIC_COLOR
participant "c:MultiComparator" as MultiComparator LOGIC_COLOR
participant "s:SortCommand" as SortCommand LOGIC_COLOR
participant "r:CommandResult" as CommandResult LOGIC_COLOR
end box
Expand All @@ -35,19 +34,13 @@ activate SortCommandParser
SortCommandParser -> SortCommandParser : parseComparatorForKeywords("n/")
activate SortCommandParser

create NameComparator
SortCommandParser -> NameComparator
activate NameComparator
create MultiComparator
SortCommandParser -> MultiComparator
activate MultiComparator

create NAME_COMPARATOR
NameComparator -> NAME_COMPARATOR
activate NAME_COMPARATOR
MultiComparator --> SortCommandParser : c
deactivate MultiComparator

NAME_COMPARATOR --> NameComparator : c
deactivate NAME_COMPARATOR

NameComparator --> SortCommandParser : c
deactivate NameComparator
SortCommandParser --> SortCommandParser : c
deactivate SortCommandParser

Expand Down
Binary file modified docs/images/SortActivityDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/SortSequenceDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/main/java/staffconnect/logic/commands/SortCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import static java.util.Objects.requireNonNull;
import static staffconnect.logic.parser.CliSyntax.PREFIX_FACULTY;
import static staffconnect.logic.parser.CliSyntax.PREFIX_MEETING;
import static staffconnect.logic.parser.CliSyntax.PREFIX_MEETING_STARTDATE;
import static staffconnect.logic.parser.CliSyntax.PREFIX_MODULE;
import static staffconnect.logic.parser.CliSyntax.PREFIX_NAME;
import static staffconnect.logic.parser.CliSyntax.PREFIX_PHONE;
Expand Down Expand Up @@ -30,6 +32,8 @@ public class SortCommand extends Command {
+ "[" + PREFIX_MODULE + "] "
+ "[" + PREFIX_FACULTY + "] "
+ "[" + PREFIX_VENUE + "] "
+ "[" + PREFIX_MEETING_STARTDATE + "] "
+ "[" + PREFIX_MEETING + "] "
+ "Example: " + COMMAND_WORD + " " + PREFIX_NAME;

private final Comparator<Person> comparator;
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/staffconnect/logic/parser/CliSyntax.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public class CliSyntax {
public static final Prefix PREFIX_TAG = new Prefix("t/");
public static final Prefix PREFIX_AVAILABILITY = new Prefix("a/");

public static final Prefix PREFIX_MEETING = new Prefix("meet/");

public static final Prefix PREFIX_MEETING_DESCRIPTION = new Prefix("d/");
public static final Prefix PREFIX_MEETING_STARTDATE = new Prefix("s/");
public static final Prefix PREFIX_MEETING_INDEX = new Prefix("i/");
Expand Down
38 changes: 36 additions & 2 deletions src/main/java/staffconnect/logic/parser/SortCommandParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,90 @@

import static staffconnect.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static staffconnect.logic.parser.CliSyntax.PREFIX_FACULTY;
import static staffconnect.logic.parser.CliSyntax.PREFIX_MEETING;
import static staffconnect.logic.parser.CliSyntax.PREFIX_MEETING_STARTDATE;
import static staffconnect.logic.parser.CliSyntax.PREFIX_MODULE;
import static staffconnect.logic.parser.CliSyntax.PREFIX_NAME;
import static staffconnect.logic.parser.CliSyntax.PREFIX_PHONE;
import static staffconnect.logic.parser.CliSyntax.PREFIX_VENUE;
import static staffconnect.model.person.comparators.FacultyComparator.FACULTY_COMPARATOR;
import static staffconnect.model.person.comparators.MeetingListComparator.MEETING_LIST_COMPARATOR;
import static staffconnect.model.person.comparators.MeetingListDateComparator.MEETING_LIST_DATE_COMPARATOR;
import static staffconnect.model.person.comparators.ModuleComparator.MODULE_COMPARATOR;
import static staffconnect.model.person.comparators.NameComparator.NAME_COMPARATOR;
import static staffconnect.model.person.comparators.PhoneComparator.PHONE_COMPARATOR;
import static staffconnect.model.person.comparators.VenueComparator.VENUE_COMPARATOR;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.logging.Logger;

import staffconnect.logic.commands.SortCommand;
import staffconnect.logic.parser.exceptions.ParseException;
import staffconnect.model.person.Person;
import staffconnect.model.person.comparators.MultiComparator;

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

private static final Logger logger = Logger.getLogger(SortCommandParser.class.getName());

/**
* Parses the given {@code String} of arguments in the context of the SortCommand
* and returns a SortCommand object for execution.
* @throws ParseException if the user input does not conform the expected format
*/
public SortCommand parse(String args) throws ParseException {
logger.info("Parsing SortCommand with arguments: " + args);
String trimmedArgs = args.trim();
if (trimmedArgs.isEmpty()) {
logger.warning("Empty argument provided.");
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE));
}

return new SortCommand(parseComparatorForKeywords(trimmedArgs));
return new SortCommand(new MultiComparator(parseComparatorsForKeywords(trimmedArgs)));
}

private List<Comparator<Person>> parseComparatorsForKeywords(String keywords) throws ParseException {
String[] keywordsArray = keywords.split("\\s+");

List<Comparator<Person>> comparators = new ArrayList<>();
for (String keyword : keywordsArray) {
comparators.add(parseComparatorForKeyword(keyword));
}

private Comparator<Person> parseComparatorForKeywords(String keyword) throws ParseException {
return comparators;
}

private Comparator<Person> parseComparatorForKeyword(String keyword) throws ParseException {
logger.info("Parsing comparator for keyword: " + keyword);
if (keyword.equals(PREFIX_NAME.getPrefix())) {
logger.fine("Using NameComparator.");
return NAME_COMPARATOR;
} else if (keyword.equals(PREFIX_PHONE.getPrefix())) {
logger.fine("Using PhoneComparator.");
return PHONE_COMPARATOR;
} else if (keyword.equals(PREFIX_MODULE.getPrefix())) {
logger.fine("Using ModuleComparator.");
return MODULE_COMPARATOR;
} else if (keyword.equals(PREFIX_FACULTY.getPrefix())) {
logger.fine("Using FacultyComparator.");
return FACULTY_COMPARATOR;
} else if (keyword.equals(PREFIX_VENUE.getPrefix())) {
logger.fine("Using VenueComparator.");
return VENUE_COMPARATOR;
} else if (keyword.equals(PREFIX_MEETING_STARTDATE.getPrefix())) {
logger.fine("Using MeetingListDateComparator.");
return MEETING_LIST_DATE_COMPARATOR;
} else if (keyword.equals(PREFIX_MEETING.getPrefix())) {
logger.fine("Using MeetingListComparator.");
return MEETING_LIST_COMPARATOR;
} else {
logger.warning("Invalid command format.");
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ public class Availability {

private static final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm");


public final String value;
private final DayOfWeek day;
private final LocalTime startTime;
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/staffconnect/model/meeting/MeetingDateTime.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,12 @@ public class MeetingDateTime {
/**
* Constructs a {@code MeetingDateTime}.
*
* @param date A valid date.
* @param dateTime A valid dateTime.
*/
public MeetingDateTime(String date) {
requireNonNull(date);
checkArgument(isValidMeetDateTime(date), MESSAGE_CONSTRAINTS);
value = parse(date);
public MeetingDateTime(String dateTime) {
requireNonNull(dateTime);
checkArgument(isValidMeetDateTime(dateTime), MESSAGE_CONSTRAINTS);
value = parse(dateTime);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package staffconnect.model.meeting.comparator;

import static staffconnect.model.meeting.comparator.MeetingDateComparator.MEETING_DATE_COMPARATOR;
import static staffconnect.model.meeting.comparator.MeetingDateTimeComparator.MEETING_DATE_COMPARATOR;
import static staffconnect.model.meeting.comparator.MeetingDescriptionComparator.MEETING_DESCRIPTION_COMPARATOR;

import java.util.Comparator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
/**
* Represents a Comparator for a Meeting's Date in the staff book.
*/
public class MeetingDateComparator implements Comparator<Meeting> {
public class MeetingDateTimeComparator implements Comparator<Meeting> {

public static final MeetingDateComparator MEETING_DATE_COMPARATOR = new MeetingDateComparator();
public static final MeetingDateTimeComparator MEETING_DATE_COMPARATOR = new MeetingDateTimeComparator();

@Override
public int compare(Meeting meet, Meeting otherMeet) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


/**
* Represents a Comparator for a Person's Venue in the staff book.
* Represents a Comparator for a Person's Faculty in the staff book.
*/
public class FacultyComparator implements Comparator<Person> {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package staffconnect.model.person.comparators;

import static staffconnect.model.meeting.comparator.MeetingDateThenDescriptionComparator.MEETING_DATE_THEN_DESCRIPTION_COMPARATOR;

import java.util.Comparator;

import staffconnect.model.meeting.Meeting;
import staffconnect.model.person.Person;


/**
* Represents a Comparator for a Person's Earliest Meeting followed by the ascending description in the staff book.
*/
public class MeetingListComparator implements Comparator<Person> {

public static final MeetingListComparator MEETING_LIST_COMPARATOR = new MeetingListComparator();

@Override
public int compare(Person p1, Person p2) {
Meeting earliestAlphanumericMeeting1 = p1.getMeetings().stream().min(MEETING_DATE_THEN_DESCRIPTION_COMPARATOR)
.orElse(null);
Meeting earliestAlphanumericMeeting2 = p2.getMeetings().stream().min(MEETING_DATE_THEN_DESCRIPTION_COMPARATOR)
.orElse(null);

if ((earliestAlphanumericMeeting1 == null) && (earliestAlphanumericMeeting2 == null)) {
return 0;
} else if (earliestAlphanumericMeeting1 == (null)) {
return 1;
} else if (earliestAlphanumericMeeting2 == (null)) {
return -1;
}

return MEETING_DATE_THEN_DESCRIPTION_COMPARATOR.compare(earliestAlphanumericMeeting1,
earliestAlphanumericMeeting2);
}

@Override
public String toString() {
return "Earliest Meeting, Ascending alphanumeric Description";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package staffconnect.model.person.comparators;

import static staffconnect.model.meeting.comparator.MeetingDateTimeComparator.MEETING_DATE_COMPARATOR;

import java.util.Comparator;

import staffconnect.model.meeting.Meeting;
import staffconnect.model.person.Person;



/**
* Represents a Comparator for a Person's Earliest Meeting in the staff book.
*/
public class MeetingListDateComparator implements Comparator<Person> {

public static final MeetingListDateComparator MEETING_LIST_DATE_COMPARATOR = new MeetingListDateComparator();

@Override
public int compare(Person p1, Person p2) {
Meeting earliestMeeting1 = p1.getMeetings().stream().min(MEETING_DATE_COMPARATOR).orElse(null);
Meeting earliestMeeting2 = p2.getMeetings().stream().min(MEETING_DATE_COMPARATOR).orElse(null);

if ((earliestMeeting1 == null) && (earliestMeeting2 == null)) {
return 0;
} else if (earliestMeeting1 == (null)) {
return 1;
} else if (earliestMeeting2 == (null)) {
return -1;
}

return MEETING_DATE_COMPARATOR.compare(earliestMeeting1, earliestMeeting2);
}

@Override
public String toString() {
return "Earliest Meeting";
}
}
Loading

0 comments on commit ec037cf

Please sign in to comment.