diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index dba469b8318..5722cab9676 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -318,6 +318,43 @@ The following activity diagram summarizes what happens when a user executes a ne
_{Explain here how the data archiving feature will be implemented}_
+### Apply feature
+The apply feature makes use of existing structures to function, notably the `Parser`, `Model` and `Storage`
+
+The following sequence diagram shows how job applications are added to Jobby
+
+
+
+#### Design Considerations
+**Aspect: How to store applications**
+
+* **Actual: Applications are stored as a JSON array belonging to their respective organizations**
+ * Pros: Easy to implement
+ * Cons: Need to initialise a list of job applications from every organization every time on startup.
+
+* **Alternative 1: Applications are stored as a JSON array separate from the contacts**
+ * Pros: Applications can be loaded immediately into Jobby without waiting for organizations to be initialised.
+ * Cons: Can have complications on other features, such as identifying which applications belong to which organizations.
+
+**Aspect: How to show applications**
+
+* **Actual: Applications are shown on a separate list**
+ * Pros: Easy to implement, less command needed to switch view from split view.
+ * Cons: Requires syncing the list with organizations, since there is no guarantee that the applications in the UI list are the same as all the ones in organizations.
+
+* **Alternative 1: Use a command to switch list view**
+ * Pros: More compact, does not require larger screen size.
+ * Cons: More difficult to implement, requires a command that directly changes the UI.
+
+**Aspect: What should the command syntax be**
+
+* **Actual: Use a separate command for adding applications**
+ * Pros: Easier to type out the command, does not require a lot of typing.
+ * Cons: More implementation effort, to implement a new command with new parser.
+* **Alternative 1: Reuse add command**
+ * Pros: Easier to implement, can make use of existing structures surrounding the add command.
+ * Cons: Overloading the add command too much.
+
--------------------------------------------------------------------------------------------------------------------
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index b833ddaf39b..7c48d7e36f4 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -106,10 +106,6 @@ Acceptable Parameters:
* `ADDRESS` can accept any value. It designates the contact’s physical address.
-* `STATUS` must be one of _interested, applied, pending, offered, rejected, current_ (case-insensitive, prefix-only match allowed).
-
-* `POSITION` may be any value. It designates the position you intend to apply to.
-
* `TAG` can accept any value and may have multiple inputs.
@@ -122,7 +118,7 @@ Examples:
* `add --org --name Example --url www.organization.org --tag freelance`
-* `add --org --name Examinations NUS --phone 65166269 --email examinations@nus.edu.sg --url https://luminus.nus.edu.sg/ --stat pending`
+* `add --org --name Examinations NUS --phone 65166269 --email examinations@nus.edu.sg --url https://luminus.nus.edu.sg/`
#### Adding a recruiter contact: `add --rec`
@@ -240,20 +236,6 @@ Jobby data are saved automatically as a JSON file `[JAR file location]/data/jobb
If your changes to the data file makes its format invalid, Jobby will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
-### Applying to an organization: `Apply`
-
-Format: `add index/ id `
-
-
-App a contact to the address book of the given class type: Organization or Recruiter.
-
-Applying to a Organization by indicating it with the organization's index in the address book or the organization's unique id.
-
-This can be done by supplying the organization's `index` or `id` as the preamble.
-
-Details specifically will be explained the next sections.
-
-
#### Applying to an Organization: `apply`
Format: `apply INDEX/ID --title TITLE [--desc DESCRIPTION] [--by DEADLINE: DD-MM-YYYY] [--stage APPLICATION STAGE: resume | online assessment | interview] [--stat STATUS: pending | offered | accepted | turned down]`
@@ -306,24 +288,52 @@ Examples:
* `edit --application 1 --stat rejected`
* `edit --application 1 --stage interview`
-### Sort `sort`
+### Sorting contacts/job applications: `sort`
Sorts contacts or applications by the specified flag.
Format: `sort --FLAG_TO_SORT`
-* Currently only the following sorting functions are supported:
- * For contacts:
- * `--addr`
- * `--email`
- * `--name`
- * `--id`
- * `--phone`
- * `--url`
- * For job applications
- * `--stale`: Sorts by last updated applications
+The following sorting flags are supported:
+* For contacts:
+ * `--address`
+ * `--email`
+ * `--name`
+ * `--id`
+ * `--phone`
+ * `--url`
+* For job applications:
+ * `--deadline`: Sorts by application deadline
+ * `--stage`: Sorts by application stage
+ * `--stale`: Sorts by last updated applications
+ * `--status`: Sorts by application status
+ * `--title`: Sorts by job title
+* To reset the sorting arrangement:
+ * `--none`
+
+Supplying `--ascending` or `--descending` sorts the contacts or applications in the specified order.
+If not specified, the default order is used:
+* Chronological (for deadlines)
+* According to the stage/status order (for application stages and statuses)
+* Alphabetical (for the rest)
+Neither order flag may be supplied if `-none` is the specified sorting flag.
+Examples:
+* `sort --name`
+* `sort --deadline --descending`
+* `sort --title --ascending`
+* `sort --none`
+### Reminding about deadlines: `remind`
+Reminds the user of upcoming deadlines for job applications.
+Format: `remind --earliest/--latest`
+
+Specifying `--earliest` will list the application deadlines in order of urgency, from earliest to latest.
+Specifying `--latest` will list the application deadlines in order of reverse urgency, from latest to earliest.
+
+Examples:
+* `remind --earliest`
+* `remind --latest`
--------------------------------------------------------------------------------------------------------------------
## FAQ
@@ -343,7 +353,7 @@ Format: `sort --FLAG_TO_SORT`
Action | Format, Examples
----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- **Add Organization** | `add --org --name [--id ID] [--phone NUMBER] [--email EMAIL] [--url URL] [--addr ADDRESS] [--stat STATUS] [--pos POSITION] [--tag TAG]...`
e.g., `add --org --name NUS --phone 0123456789 --email example@nus.edu.sg --url https://www.nus.edu.sg/ --stat pending --pos Research`
+ **Add Organization** | `add --org --name [--id ID] [--phone NUMBER] [--email EMAIL] [--url URL] [--addr ADDRESS] [--tag TAG]...`
e.g., `add --org --name NUS --phone 0123456789 --email example@nus.edu.sg --url https://www.nus.edu.sg/`
**Add Recruiter** | `add --rec --name [--id ID] [--oid ORG_ID] [--phone NUMBER] [--email EMAIL] [--url URL] [--addr ADDRESS] [--tag TAG]...`
e.g., `add --rec --name John Doe --oid paypal-sg`
**Clear** | `clear`
**Delete** | `delete INDEX [--recursive]` or
`delete --id ID [--recursive]`
e.g., `delete 3`, `delete --id 55tg`
diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml
index 71d0de07bfa..5c27ccb8c0e 100644
--- a/docs/diagrams/ModelClassDiagram.puml
+++ b/docs/diagrams/ModelClassDiagram.puml
@@ -22,6 +22,9 @@ Class Url
Class Phone
Class Tag
+Class JobApplicationList
+Class JobApplication
+
Class I #FFFFFF
}
@@ -47,6 +50,9 @@ Contact *--> Url
Contact *--> Address
Contact *--> "*" Tag
+ModelManager *-down-> "1" JobApplicationList
+JobApplicationList -down-> "*" JobApplication
+
Contact -[hidden]up--> I
UniqueContactList -[hidden]right-> I
diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml
index e3fae960636..457b68ee498 100644
--- a/docs/diagrams/StorageClassDiagram.puml
+++ b/docs/diagrams/StorageClassDiagram.puml
@@ -19,6 +19,7 @@ Class "<>\nAddressBookStorage" as AddressBookStorage
Class JsonAddressBookStorage
Class JsonSerializableAddressBook
Class JsonAdaptedContact
+Class JsonAdaptedApplication
Class JsonAdaptedTag
}
@@ -39,5 +40,6 @@ JsonAddressBookStorage .up.|> AddressBookStorage
JsonAddressBookStorage ..> JsonSerializableAddressBook
JsonSerializableAddressBook --> "*" JsonAdaptedContact
JsonAdaptedContact --> "*" JsonAdaptedTag
+JsonAdaptedContact --> "*" JsonAdaptedApplication
@enduml
diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml
index 5bf258bfe82..cae6ee1098a 100644
--- a/docs/diagrams/UiClassDiagram.puml
+++ b/docs/diagrams/UiClassDiagram.puml
@@ -13,8 +13,11 @@ Class HelpWindow
Class ResultDisplay
Class ContactListPanel
Class ContactCard
+Class ApplicationListPanel
+Class ApplicationCard
Class StatusBarFooter
Class CommandBox
+Class AutoCompleteTextField
}
package Model <> {
@@ -33,12 +36,15 @@ UiManager -down-> "1" MainWindow
MainWindow *-down-> "1" CommandBox
MainWindow *-down-> "1" ResultDisplay
MainWindow *-down-> "1" ContactListPanel
+MainWindow *-down-> "1" ApplicationListPanel
MainWindow *-down-> "1" StatusBarFooter
MainWindow --> "0..1" HelpWindow
ContactListPanel -down-> "*" ContactCard
+ApplicationListPanel -down-> "*" ApplicationCard
+CommandBox -down-> "0..1" AutoCompleteTextField
-MainWindow -left-|> UiPart
+MainWindow --|> UiPart
ResultDisplay --|> UiPart
CommandBox --|> UiPart
@@ -46,12 +52,17 @@ ContactListPanel --|> UiPart
ContactCard --|> UiPart
StatusBarFooter --|> UiPart
HelpWindow --|> UiPart
+AutoCompleteTextField --|> UiPart
+ApplicationListPanel --|> UiPart
+ApplicationCard --|> UiPart
ContactCard ..> Model
+ApplicationCard ..> Model
UiManager -right-> Logic
MainWindow -left-> Logic
ContactListPanel -[hidden]left- HelpWindow
+ApplicationListPanel -[hidden]left- ContactListPanel
HelpWindow -[hidden]left- CommandBox
CommandBox -[hidden]left- ResultDisplay
ResultDisplay -[hidden]left- StatusBarFooter
diff --git a/docs/diagrams/apply-command/ApplyCommand.puml b/docs/diagrams/apply-command/ApplyCommand.puml
new file mode 100644
index 00000000000..e156fb81cbb
--- /dev/null
+++ b/docs/diagrams/apply-command/ApplyCommand.puml
@@ -0,0 +1,70 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AppParser" as AddressBookParser LOGIC_COLOR
+participant ":ApplyCommandParser" as ApplyCommandParser LOGIC_COLOR
+participant ":ApplyCommand" as ApplyCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("apply 1 --title SWE")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("apply 1 --title SWE")
+activate AddressBookParser
+
+create ApplyCommandParser
+AddressBookParser -> ApplyCommandParser
+activate ApplyCommandParser
+
+ApplyCommandParser --> AddressBookParser
+deactivate ApplyCommandParser
+
+AddressBookParser -> ApplyCommandParser : parse(" --title SWE")
+activate ApplyCommandParser
+
+create ApplyCommand
+ApplyCommandParser -> ApplyCommand
+activate ApplyCommand
+
+ApplyCommand --> ApplyCommandParser :
+deactivate ApplyCommand
+
+ApplyCommandParser --> AddressBookParser :
+deactivate ApplyCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+ApplyCommandParser -[hidden]-> AddressBookParser
+destroy ApplyCommandParser
+
+AddressBookParser --> LogicManager :
+deactivate AddressBookParser
+
+LogicManager -> ApplyCommand : execute()
+activate ApplyCommand
+
+ApplyCommand -> Model : addJobApplication()
+activate Model
+
+Model --> ApplyCommand
+deactivate Model
+
+create CommandResult
+ApplyCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> ApplyCommand
+deactivate CommandResult
+
+ApplyCommand --> LogicManager : result
+deactivate ApplyCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/apply-command/style.puml b/docs/diagrams/apply-command/style.puml
new file mode 100644
index 00000000000..f7d7347ae84
--- /dev/null
+++ b/docs/diagrams/apply-command/style.puml
@@ -0,0 +1,79 @@
+/'
+ 'Commonly used styles and colors across diagrams.
+ 'Refer to https://plantuml-documentation.readthedocs.io/en/latest for a more
+ 'comprehensive list of skinparams.
+ '/
+
+
+'T1 through T4 are shades of the original color from lightest to darkest
+
+!define UI_COLOR #1D8900
+!define UI_COLOR_T1 #83E769
+!define UI_COLOR_T2 #3FC71B
+!define UI_COLOR_T3 #166800
+!define UI_COLOR_T4 #0E4100
+
+!define LOGIC_COLOR #3333C4
+!define LOGIC_COLOR_T1 #C8C8FA
+!define LOGIC_COLOR_T2 #6A6ADC
+!define LOGIC_COLOR_T3 #1616B0
+!define LOGIC_COLOR_T4 #101086
+
+!define MODEL_COLOR #9D0012
+!define MODEL_COLOR_T1 #F97181
+!define MODEL_COLOR_T2 #E41F36
+!define MODEL_COLOR_T3 #7B000E
+!define MODEL_COLOR_T4 #51000A
+
+!define STORAGE_COLOR #A38300
+!define STORAGE_COLOR_T1 #FFE374
+!define STORAGE_COLOR_T2 #EDC520
+!define STORAGE_COLOR_T3 #806600
+!define STORAGE_COLOR_T2 #544400
+
+!define USER_COLOR #000000
+
+skinparam Package {
+ BackgroundColor #FFFFFF
+ BorderThickness 1
+ FontSize 16
+}
+
+skinparam Class {
+ FontColor #FFFFFF
+ FontSize 15
+ BorderThickness 1
+ BorderColor #FFFFFF
+ StereotypeFontColor #FFFFFF
+ FontName Arial
+}
+
+skinparam Actor {
+ BorderColor USER_COLOR
+ Color USER_COLOR
+ FontName Arial
+}
+
+skinparam Sequence {
+ MessageAlign center
+ BoxFontSize 15
+ BoxPadding 0
+ BoxFontColor #FFFFFF
+ FontName Arial
+}
+
+skinparam Participant {
+ FontColor #FFFFFFF
+ Padding 20
+}
+
+skinparam ArrowFontStyle bold
+skinparam MinClassWidth 50
+skinparam ParticipantPadding 10
+skinparam Shadowing false
+skinparam DefaultTextAlignment center
+skinparam packageStyle Rectangle
+
+hide footbox
+hide members
+hide circle
diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png
index 1176b8c30b8..79d1cd62d07 100644
Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ
diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png
index 8700f4b1ee4..c05d66160e4 100644
Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ
diff --git a/docs/images/Ui.png b/docs/images/Ui.png
index 886cccee92e..ef630310c43 100644
Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ
diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png
index 11f06d68671..68d6b78f9d8 100644
Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ
diff --git a/docs/images/apply-command/ApplyCommand.png b/docs/images/apply-command/ApplyCommand.png
new file mode 100644
index 00000000000..42c3bf1131b
Binary files /dev/null and b/docs/images/apply-command/ApplyCommand.png differ
diff --git a/src/main/java/seedu/address/logic/commands/ReminderCommand.java b/src/main/java/seedu/address/logic/commands/ReminderCommand.java
new file mode 100644
index 00000000000..22643c0b061
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ReminderCommand.java
@@ -0,0 +1,73 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.FLAG_EARLIEST;
+import static seedu.address.logic.parser.CliSyntax.FLAG_LATEST;
+
+import seedu.address.logic.autocomplete.AutocompleteSupplier;
+import seedu.address.logic.autocomplete.data.AutocompleteDataSet;
+import seedu.address.model.Model;
+import seedu.address.model.jobapplication.JobApplication;
+
+/**
+ * Reminds the user of urgent or stale applications, similar to the usage of {@code SortCommand}.
+ */
+public class ReminderCommand extends Command {
+ public static final String COMMAND_WORD = "remind";
+
+ public static final AutocompleteSupplier AUTOCOMPLETE_SUPPLIER = AutocompleteSupplier.from(
+ AutocompleteDataSet.oneAmongAllOf(
+ FLAG_EARLIEST, FLAG_LATEST
+ )
+ ).configureValueMap(map -> {
+ // Disable value autocompletion for:
+ map.put(null /* preamble */, null);
+ });
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Reminds the user of applications based on the specified flag.\n"
+ + "Parameters: " + FLAG_EARLIEST + "/" + FLAG_LATEST + "\n"
+ + "Example 1: " + COMMAND_WORD + " --earliest\n"
+ + "Example 2: " + COMMAND_WORD + " --latest\n";
+
+ public static final String MESSAGE_REMINDED_EARLIEST = "Reminded user of high priority applications";
+ public static final String MESSAGE_REMINDED_LATEST = "Reminded user of low priority applications";
+ private final Boolean isUrgent;
+
+ /**
+ * Creates a ReminderCommand sorting the {@code JobApplication} entries by deadline.
+ * @param isUrgent checks if the {@code ReminderCommand} should display urgent or stale applications.
+ */
+ public ReminderCommand(Boolean isUrgent) {
+ this.isUrgent = isUrgent;
+ }
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ if (isUrgent) {
+ model.updateSortedApplicationList(JobApplication.DEADLINE_COMPARATOR);
+ return new CommandResult(MESSAGE_REMINDED_EARLIEST);
+ }
+ model.updateSortedApplicationList(JobApplication.DEADLINE_COMPARATOR.reversed());
+ return new CommandResult(MESSAGE_REMINDED_LATEST);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof ReminderCommand)) {
+ return false;
+ }
+
+ ReminderCommand otherReminderCommand = (ReminderCommand) other;
+ if (this.isUrgent) {
+ return otherReminderCommand.isUrgent;
+ } else {
+ return !otherReminderCommand.isUrgent;
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/SortCommand.java b/src/main/java/seedu/address/logic/commands/SortCommand.java
index c8a589a675f..18c18cb1f2c 100644
--- a/src/main/java/seedu/address/logic/commands/SortCommand.java
+++ b/src/main/java/seedu/address/logic/commands/SortCommand.java
@@ -2,31 +2,48 @@
import static java.util.Objects.requireNonNull;
import static seedu.address.logic.parser.CliSyntax.FLAG_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.FLAG_ASCENDING;
+import static seedu.address.logic.parser.CliSyntax.FLAG_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.FLAG_DESCENDING;
import static seedu.address.logic.parser.CliSyntax.FLAG_EMAIL;
import static seedu.address.logic.parser.CliSyntax.FLAG_ID;
import static seedu.address.logic.parser.CliSyntax.FLAG_NAME;
+import static seedu.address.logic.parser.CliSyntax.FLAG_NONE;
import static seedu.address.logic.parser.CliSyntax.FLAG_PHONE;
+import static seedu.address.logic.parser.CliSyntax.FLAG_STAGE;
import static seedu.address.logic.parser.CliSyntax.FLAG_STALE;
+import static seedu.address.logic.parser.CliSyntax.FLAG_STATUS;
+import static seedu.address.logic.parser.CliSyntax.FLAG_TITLE;
import static seedu.address.logic.parser.CliSyntax.FLAG_URL;
-import static seedu.address.model.Model.PREDICATE_SHOW_ALL_CONTACTS;
import java.util.Comparator;
import seedu.address.logic.autocomplete.AutocompleteSupplier;
+import seedu.address.logic.autocomplete.data.AutocompleteConstraint;
import seedu.address.logic.autocomplete.data.AutocompleteDataSet;
import seedu.address.model.Model;
import seedu.address.model.contact.Contact;
import seedu.address.model.jobapplication.JobApplication;
/**
- * Sorts all contacts in the address book.
+ * Sorts contacts and job applications in the address book.
*/
public class SortCommand extends Command {
public static final String COMMAND_WORD = "sort";
public static final AutocompleteSupplier AUTOCOMPLETE_SUPPLIER = AutocompleteSupplier.from(
- AutocompleteDataSet.oneAmongAllOf(
- FLAG_NAME, FLAG_ID, FLAG_PHONE, FLAG_EMAIL, FLAG_ADDRESS, FLAG_URL, FLAG_STALE
+ AutocompleteDataSet.concat(
+ AutocompleteDataSet.oneAmongAllOf(
+ FLAG_NAME, FLAG_ID, FLAG_PHONE, FLAG_EMAIL, FLAG_ADDRESS, FLAG_URL,
+ FLAG_STALE, FLAG_STAGE, FLAG_STATUS, FLAG_DEADLINE, FLAG_TITLE,
+ FLAG_NONE
+ ), AutocompleteDataSet.oneAmongAllOf(
+ FLAG_ASCENDING, FLAG_DESCENDING
+ )
+ ).addConstraint(
+ AutocompleteConstraint.oneAmongAllOf(
+ FLAG_NONE, FLAG_ASCENDING, FLAG_DESCENDING
+ )
)
).configureValueMap(map -> {
// Disable value autocompletion for:
@@ -34,54 +51,57 @@ public class SortCommand extends Command {
});
public static final String MESSAGE_USAGE = COMMAND_WORD
- + ": Sorts all contacts based on the specified flag.\n"
+ + ": Sorts contacts or applications based on the specified flag.\n"
+ "Parameters: " + FLAG_NAME + "/" + FLAG_ID + "/"
+ FLAG_PHONE + "/" + FLAG_EMAIL + "/"
- + FLAG_ADDRESS + "/" + FLAG_URL + "\n"
+ + FLAG_ADDRESS + "/" + FLAG_URL + "/"
+ + FLAG_DEADLINE + "/" + FLAG_STATUS + "/"
+ + FLAG_STAGE + "/" + FLAG_STALE + "/"
+ + FLAG_TITLE + "/" + FLAG_NONE
+ + " [" + FLAG_ASCENDING + "/" + FLAG_DESCENDING + "]\n"
+ "Example 1: " + COMMAND_WORD + " --name\n"
- + "Example 2: " + COMMAND_WORD + " --id\n"
- + "Example 3: " + COMMAND_WORD + " --url\n";
+ + "Example 2: " + COMMAND_WORD + " --id" + " --descending\n"
+ + "Example 3: " + COMMAND_WORD + " --stale" + " --ascending\n";
- public static final String MESSAGE_SORTED_SUCCESS = "Sorted all contacts";
+ public static final String MESSAGE_SORTED_CONTACTS = "Sorted contacts as specified";
+ public static final String MESSAGE_SORTED_APPLICATIONS = "Sorted applications as specified";
+ public static final String MESSAGE_RESET_SORTING = "Sorting reset to default order";
- // TODO: Tech debt - bad implementation
- private final Comparator comparator;
+ private final Comparator comparatorContact;
private final Comparator comparatorApplication;
-
-
- /**
- * Creates a SortCommand sorting the {@code Contact} entries by the specified comparator.
- * @param comparator the comparator determining the sorting of {@code Contact} entries
- */
- public SortCommand(Comparator comparator) {
- requireNonNull(comparator);
- this.comparator = comparator;
- this.comparatorApplication = null;
- }
+ private final Boolean isReset;
/**
- * Creats a SortCommand sorting the {@code JobApplication} entries by the specified comparator.
- * @param comparator the comparator determining the sorting of the application
- * @param iAmLosingMyMind does nothing
+ * Creates a SortCommand sorting the {@code Contact} entries and the {@code JobApplication}
+ * entries by the specified comparators.
+ * @param contactComparator the comparator determining the sorting of {@code Contact} entries.
+ * May be null, should the SortCommand seek to sort applications instead.
+ * @param applicationComparator the comparator determining the sorting of {@code JobApplication} entries.
+ * May be null, should the SortCommand seek to sort contacts.
+ * @param isReset checks if the {@code SortCommand} is going to reset the sorting order
+ * to the original order.
*/
- public SortCommand(Comparator comparator, Boolean iAmLosingMyMind) {
- requireNonNull(comparator);
- this.comparatorApplication = comparator;
- this.comparator = null;
+ public SortCommand(Comparator contactComparator,
+ Comparator applicationComparator, Boolean isReset) {
+ this.comparatorContact = contactComparator;
+ this.comparatorApplication = applicationComparator;
+ this.isReset = isReset;
}
-
-
-
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
+ if (isReset) {
+ //both comparators should be null here
+ model.updateSortedContactList(comparatorContact);
+ model.updateSortedApplicationList(comparatorApplication);
+ return new CommandResult(MESSAGE_RESET_SORTING);
+ }
if (comparatorApplication != null) {
model.updateSortedApplicationList(comparatorApplication);
- return new CommandResult("Sorted by last updated application");
+ return new CommandResult(MESSAGE_SORTED_APPLICATIONS);
}
- model.updateFilteredContactList(PREDICATE_SHOW_ALL_CONTACTS);
- model.updateSortedContactList(comparator);
- return new CommandResult(MESSAGE_SORTED_SUCCESS);
+ model.updateSortedContactList(comparatorContact);
+ return new CommandResult(MESSAGE_SORTED_CONTACTS);
}
@Override
@@ -96,6 +116,11 @@ public boolean equals(Object other) {
}
SortCommand otherSortCommand = (SortCommand) other;
- return comparator.equals(otherSortCommand.comparator);
+ if (this.isReset) {
+ return otherSortCommand.isReset;
+ }
+ Boolean equalsContact = comparatorContact.equals(otherSortCommand.comparatorContact);
+ Boolean equalsApplication = comparatorApplication.equals(otherSortCommand.comparatorApplication);
+ return (equalsContact && equalsApplication && !otherSortCommand.isReset);
}
}
diff --git a/src/main/java/seedu/address/logic/parser/AppParser.java b/src/main/java/seedu/address/logic/parser/AppParser.java
index 1c9e16377b6..722c70e6006 100644
--- a/src/main/java/seedu/address/logic/parser/AppParser.java
+++ b/src/main/java/seedu/address/logic/parser/AppParser.java
@@ -120,6 +120,7 @@ public AutocompleteGenerator parseCompletionGenerator(String userInput) {
logger.finest("Preparing autocomplete: '" + userInput + "'");
+
// Case 1: There is no command name followed by a whitespace character.
// - The command name is incomplete - we're still typing the name.
// - Suggest available command names.
diff --git a/src/main/java/seedu/address/logic/parser/ClassMappings.java b/src/main/java/seedu/address/logic/parser/ClassMappings.java
index e97e1b44d53..859f13352ae 100644
--- a/src/main/java/seedu/address/logic/parser/ClassMappings.java
+++ b/src/main/java/seedu/address/logic/parser/ClassMappings.java
@@ -15,6 +15,7 @@
import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.ReminderCommand;
import seedu.address.logic.commands.SortCommand;
/**
@@ -44,6 +45,7 @@ private ClassMappings() { } // Should not be initialized.
orderedMap.put(ListCommand.class, Optional.of(ListCommandParser.class));
orderedMap.put(FindCommand.class, Optional.of(FindCommandParser.class));
orderedMap.put(SortCommand.class, Optional.of(SortCommandParser.class));
+ orderedMap.put(ReminderCommand.class, Optional.of(ReminderCommandParser.class));
orderedMap.put(HelpCommand.class, Optional.empty());
orderedMap.put(ClearCommand.class, Optional.empty());
diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java
index f718124d66e..6a00789a1f9 100644
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java
@@ -25,7 +25,12 @@ public class CliSyntax {
public static final Flag FLAG_STAGE = new Flag("stage");
public static final Flag FLAG_DESCRIPTION = new Flag("desc");
public static final Flag FLAG_NOT_APPLIED = new Flag("toapply");
+ public static final Flag FLAG_NONE = new Flag("none");
+ public static final Flag FLAG_ASCENDING = new Flag("ascending");
+ public static final Flag FLAG_DESCENDING = new Flag("descending");
public static final Flag FLAG_STALE = new Flag("stale");
+ public static final Flag FLAG_EARLIEST = new Flag("earliest");
+ public static final Flag FLAG_LATEST = new Flag("latest");
public static final Flag FLAG_APPLICATION = new Flag("application");
diff --git a/src/main/java/seedu/address/logic/parser/ReminderCommandParser.java b/src/main/java/seedu/address/logic/parser/ReminderCommandParser.java
new file mode 100644
index 00000000000..b9f0418d280
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ReminderCommandParser.java
@@ -0,0 +1,33 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.FLAG_EARLIEST;
+import static seedu.address.logic.parser.CliSyntax.FLAG_LATEST;
+
+import seedu.address.logic.commands.ReminderCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new {@code ReminderCommand} object
+ */
+public class ReminderCommandParser implements Parser {
+ // TODO: Tech debt - implement tests
+ /**
+ * Parses the given {@code String} of arguments in the context of the
+ * {@code ReminderCommand} and returns a {@code ReminderCommand} object for execution.
+ */
+ public ReminderCommand parse(String args) throws ParseException {
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args,
+ ReminderCommand.AUTOCOMPLETE_SUPPLIER.getAllPossibleFlags().toArray(Flag[]::new));
+
+ if (argMultimap.hasFlag(FLAG_EARLIEST)) {
+ return new ReminderCommand(true);
+ } else if (argMultimap.hasFlag(FLAG_LATEST)) {
+ return new ReminderCommand(false);
+ }
+
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, ReminderCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/SortCommandParser.java b/src/main/java/seedu/address/logic/parser/SortCommandParser.java
index 2297ea11e1c..408d4962e1f 100644
--- a/src/main/java/seedu/address/logic/parser/SortCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/SortCommandParser.java
@@ -2,21 +2,34 @@
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.FLAG_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.FLAG_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.FLAG_DESCENDING;
import static seedu.address.logic.parser.CliSyntax.FLAG_EMAIL;
import static seedu.address.logic.parser.CliSyntax.FLAG_ID;
import static seedu.address.logic.parser.CliSyntax.FLAG_NAME;
+import static seedu.address.logic.parser.CliSyntax.FLAG_NONE;
import static seedu.address.logic.parser.CliSyntax.FLAG_PHONE;
+import static seedu.address.logic.parser.CliSyntax.FLAG_STAGE;
import static seedu.address.logic.parser.CliSyntax.FLAG_STALE;
+import static seedu.address.logic.parser.CliSyntax.FLAG_STATUS;
+import static seedu.address.logic.parser.CliSyntax.FLAG_TITLE;
import static seedu.address.logic.parser.CliSyntax.FLAG_URL;
import static seedu.address.model.Model.COMPARATOR_ADDRESS;
+import static seedu.address.model.Model.COMPARATOR_ADDRESS_REVERSED;
import static seedu.address.model.Model.COMPARATOR_EMAIL;
+import static seedu.address.model.Model.COMPARATOR_EMAIL_REVERSED;
import static seedu.address.model.Model.COMPARATOR_ID;
import static seedu.address.model.Model.COMPARATOR_NAME;
import static seedu.address.model.Model.COMPARATOR_PHONE;
+import static seedu.address.model.Model.COMPARATOR_PHONE_REVERSED;
import static seedu.address.model.Model.COMPARATOR_URL;
+import static seedu.address.model.Model.COMPARATOR_URL_REVERSED;
+
+import java.util.Comparator;
import seedu.address.logic.commands.SortCommand;
import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.contact.Contact;
import seedu.address.model.jobapplication.JobApplication;
/**
@@ -24,7 +37,6 @@
*/
public class SortCommandParser implements Parser {
- //this is where i create the comparators
// TODO: Tech debt - implement tests
/**
@@ -36,20 +48,95 @@ public SortCommand parse(String args) throws ParseException {
ArgumentTokenizer.tokenize(args,
SortCommand.AUTOCOMPLETE_SUPPLIER.getAllPossibleFlags().toArray(Flag[]::new));
+ if (argMultimap.hasFlag(FLAG_NONE)) {
+ return new SortCommand(null, null, true);
+ }
+
+ Boolean isReverse = false;
+ Comparator contactComparator = null;
+ Comparator applicationComparator = null;
+
+ if (argMultimap.hasFlag(FLAG_DESCENDING)) {
+ isReverse = true;
+ }
+ //I feel there should be a more elegant way to write this.
if (argMultimap.hasFlag(FLAG_ADDRESS)) {
- return new SortCommand(COMPARATOR_ADDRESS);
+ if (isReverse) {
+ contactComparator = COMPARATOR_ADDRESS_REVERSED;
+ } else {
+ contactComparator = COMPARATOR_ADDRESS;
+ }
+ return new SortCommand(contactComparator, applicationComparator, false);
} else if (argMultimap.hasFlag(FLAG_EMAIL)) {
- return new SortCommand(COMPARATOR_EMAIL);
+ if (isReverse) {
+ contactComparator = COMPARATOR_EMAIL_REVERSED;
+ } else {
+ contactComparator = COMPARATOR_EMAIL;
+ }
+ return new SortCommand(contactComparator, applicationComparator, false);
} else if (argMultimap.hasFlag(FLAG_NAME)) {
- return new SortCommand(COMPARATOR_NAME);
+ if (isReverse) {
+ contactComparator = COMPARATOR_NAME.reversed();
+ } else {
+ contactComparator = COMPARATOR_NAME;
+ }
+ return new SortCommand(contactComparator, applicationComparator, false);
} else if (argMultimap.hasFlag(FLAG_ID)) {
- return new SortCommand(COMPARATOR_ID);
+ if (isReverse) {
+ contactComparator = COMPARATOR_ID.reversed();
+ } else {
+ contactComparator = COMPARATOR_ID;
+ }
+ return new SortCommand(contactComparator, applicationComparator, false);
} else if (argMultimap.hasFlag(FLAG_PHONE)) {
- return new SortCommand(COMPARATOR_PHONE);
+ if (isReverse) {
+ contactComparator = COMPARATOR_PHONE_REVERSED;
+ } else {
+ contactComparator = COMPARATOR_PHONE;
+ }
+ return new SortCommand(contactComparator, applicationComparator, false);
} else if (argMultimap.hasFlag(FLAG_URL)) {
- return new SortCommand(COMPARATOR_URL);
+ if (isReverse) {
+ contactComparator = COMPARATOR_URL_REVERSED;
+ } else {
+ contactComparator = COMPARATOR_URL;
+ }
+ return new SortCommand(contactComparator, applicationComparator, false);
} else if (argMultimap.hasFlag(FLAG_STALE)) {
- return new SortCommand(JobApplication.LAST_UPDATED_COMPARATOR, true);
+ if (isReverse) {
+ applicationComparator = JobApplication.LAST_UPDATED_COMPARATOR.reversed();
+ } else {
+ applicationComparator = JobApplication.LAST_UPDATED_COMPARATOR;
+ }
+ return new SortCommand(contactComparator, applicationComparator, false);
+ } else if (argMultimap.hasFlag(FLAG_STAGE)) {
+ if (isReverse) {
+ applicationComparator = JobApplication.STAGE_COMPARATOR.reversed();
+ } else {
+ applicationComparator = JobApplication.STAGE_COMPARATOR;
+ }
+ return new SortCommand(contactComparator, applicationComparator, false);
+ } else if (argMultimap.hasFlag(FLAG_STATUS)) {
+ if (isReverse) {
+ applicationComparator = JobApplication.STATUS_COMPARATOR.reversed();
+ } else {
+ applicationComparator = JobApplication.STATUS_COMPARATOR;
+ }
+ return new SortCommand(contactComparator, applicationComparator, false);
+ } else if (argMultimap.hasFlag(FLAG_DEADLINE)) {
+ if (isReverse) {
+ applicationComparator = JobApplication.DEADLINE_COMPARATOR.reversed();
+ } else {
+ applicationComparator = JobApplication.DEADLINE_COMPARATOR;
+ }
+ return new SortCommand(contactComparator, applicationComparator, false);
+ } else if (argMultimap.hasFlag(FLAG_TITLE)) {
+ if (isReverse) {
+ applicationComparator = JobApplication.JOB_TITLE_COMPARATOR.reversed();
+ } else {
+ applicationComparator = JobApplication.JOB_TITLE_COMPARATOR;
+ }
+ return new SortCommand(contactComparator, applicationComparator, false);
}
throw new ParseException(
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
index 5f7fc96eefc..f9efc0ce156 100644
--- a/src/main/java/seedu/address/model/Model.java
+++ b/src/main/java/seedu/address/model/Model.java
@@ -28,20 +28,36 @@ public interface Model {
Comparator COMPARATOR_ADDRESS = Comparator.comparing(contact ->
contact.getAddress().map(address -> address.value).orElse(null),
- Comparator.nullsLast(Comparator.naturalOrder()));
+ Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER));
+ Comparator COMPARATOR_ADDRESS_NULLS_FIRST = Comparator.comparing(contact ->
+ contact.getAddress().map(address -> address.value).orElse(null),
+ Comparator.nullsFirst(String.CASE_INSENSITIVE_ORDER));
+ Comparator COMPARATOR_ADDRESS_REVERSED = COMPARATOR_ADDRESS_NULLS_FIRST.reversed();
Comparator COMPARATOR_EMAIL = Comparator.comparing(contact ->
contact.getEmail().map(email -> email.value).orElse(null),
- Comparator.nullsLast(Comparator.naturalOrder()));
+ Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER));
+ Comparator COMPARATOR_EMAIL_NULLS_FIRST = Comparator.comparing(contact ->
+ contact.getEmail().map(email -> email.value).orElse(null),
+ Comparator.nullsFirst(String.CASE_INSENSITIVE_ORDER));
+ Comparator COMPARATOR_EMAIL_REVERSED = COMPARATOR_EMAIL_NULLS_FIRST.reversed();
Comparator COMPARATOR_ID = Comparator.comparing(contact ->
- contact.getId().value);
+ contact.getId().value, String.CASE_INSENSITIVE_ORDER);
Comparator COMPARATOR_NAME = Comparator.comparing(contact ->
- contact.getName().fullName);
+ contact.getName().fullName, String.CASE_INSENSITIVE_ORDER);
Comparator COMPARATOR_PHONE = Comparator.comparing(contact ->
contact.getPhone().map(phone -> phone.value).orElse(null),
- Comparator.nullsLast(Comparator.naturalOrder()));
+ Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER));
+ Comparator COMPARATOR_PHONE_NULLS_FIRST = Comparator.comparing(contact ->
+ contact.getPhone().map(phone -> phone.value).orElse(null),
+ Comparator.nullsFirst(String.CASE_INSENSITIVE_ORDER));
+ Comparator COMPARATOR_PHONE_REVERSED = COMPARATOR_PHONE_NULLS_FIRST.reversed();
Comparator COMPARATOR_URL = Comparator.comparing(contact ->
contact.getUrl().map(url -> url.value).orElse(null),
- Comparator.nullsLast(Comparator.naturalOrder()));
+ Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER));
+ Comparator COMPARATOR_URL_NULLS_FIRST = Comparator.comparing(contact ->
+ contact.getUrl().map(url -> url.value).orElse(null),
+ Comparator.nullsFirst(String.CASE_INSENSITIVE_ORDER));
+ Comparator COMPARATOR_URL_REVERSED = COMPARATOR_URL_NULLS_FIRST.reversed();
/**
* Replaces user prefs data with the data in {@code userPrefs}.
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
index 24acf757714..16d08cf9a60 100644
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ b/src/main/java/seedu/address/model/ModelManager.java
@@ -25,6 +25,7 @@
import seedu.address.model.contact.Organization;
import seedu.address.model.contact.Type;
import seedu.address.model.jobapplication.JobApplication;
+import seedu.address.model.jobapplication.JobApplicationList;
/**
* Represents the in-memory model of the address book data.
@@ -37,7 +38,7 @@ public class ModelManager implements Model {
private final FilteredList displayedContacts;
private final FilteredList filteredContacts;
private final SortedList sortedContacts;
- private final ObservableList applicationList;
+ private final JobApplicationList jobApplicationList;
private final SortedList sortedApplications;
private final FilteredList filteredApplications;
private final FilteredList displayedApplications;
@@ -55,12 +56,13 @@ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs
this.sortedContacts = new SortedList<>(this.addressBook.getContactList());
this.filteredContacts = new FilteredList<>(sortedContacts);
this.displayedContacts = filteredContacts;
- this.applicationList = FXCollections.observableArrayList(filteredContacts.stream()
+ ObservableList applicationList = FXCollections.observableArrayList(filteredContacts.stream()
.filter(c -> c.getType() == Type.ORGANIZATION)
.flatMap(c -> Arrays.stream(((Organization) c).getJobApplications()))
.sorted(JobApplication.LAST_UPDATED_COMPARATOR)
.collect(Collectors.toList()));
- this.sortedApplications = new SortedList<>(this.applicationList);
+ this.jobApplicationList = new JobApplicationList(applicationList);
+ this.sortedApplications = new SortedList<>(applicationList);
this.filteredApplications = new FilteredList<>(this.sortedApplications, s->true);
this.displayedApplications = filteredApplications;
}
@@ -127,7 +129,7 @@ public void deleteContact(Contact target) {
addressBook.removeContact(target);
if (target.getType() == Type.ORGANIZATION) {
for (JobApplication i: ((Organization) target).getJobApplications()) {
- applicationList.remove(i);
+ jobApplicationList.remove(i);
}
}
}
@@ -180,9 +182,9 @@ public Contact getContactByIdXorIndex(Id id, Index index) throws IllegalValueExc
@Override
public void replaceApplication(Index index, JobApplication newApplication) throws IllegalValueException {
- JobApplication oldApplication = this.applicationList.get(index.getZeroBased());
+ JobApplication oldApplication = jobApplicationList.get(index.getZeroBased());
- this.applicationList.set(index.getZeroBased(), newApplication);
+ jobApplicationList.set(index.getZeroBased(), newApplication);
Contact contact = getContactById(newApplication.getOrganizationId());
if (contact == null || contact.getType() != Type.ORGANIZATION) {
throw new IllegalValueException("Id field is invalid!");
@@ -200,13 +202,13 @@ public void deleteApplication(JobApplication application) throws IllegalValueExc
throw new IllegalValueException("Id field is invalid!");
}
Organization org = (Organization) contact;
- this.applicationList.remove(application);
+ this.jobApplicationList.remove(application);
org.deleteJobApplication(application);
}
@Override
public void addApplication(JobApplication application) {
- applicationList.add(application);
+ jobApplicationList.add(application);
// TODO: Tech debt - need separate declaration for the predicates
filteredApplications.setPredicate(c -> true);
}
diff --git a/src/main/java/seedu/address/model/jobapplication/JobApplication.java b/src/main/java/seedu/address/model/jobapplication/JobApplication.java
index 62a68298142..621fd657e1f 100644
--- a/src/main/java/seedu/address/model/jobapplication/JobApplication.java
+++ b/src/main/java/seedu/address/model/jobapplication/JobApplication.java
@@ -28,6 +28,9 @@ public class JobApplication {
public static final Comparator LAST_UPDATED_COMPARATOR = (a, b) ->
a.lastUpdatedTime.compareTo(b.lastUpdatedTime);
+ public static final Comparator JOB_TITLE_COMPARATOR = Comparator.comparing(
+ application -> application.getJobTitle().title, String.CASE_INSENSITIVE_ORDER);
+
private final Id oid;
private final Name orgName;
diff --git a/src/main/java/seedu/address/model/jobapplication/JobApplicationList.java b/src/main/java/seedu/address/model/jobapplication/JobApplicationList.java
new file mode 100644
index 00000000000..e352b3d3ed4
--- /dev/null
+++ b/src/main/java/seedu/address/model/jobapplication/JobApplicationList.java
@@ -0,0 +1,54 @@
+package seedu.address.model.jobapplication;
+
+
+import javafx.collections.ObservableList;
+
+/**
+ * A wrapper for ObservableList of {@link JobApplication}
+ */
+public class JobApplicationList {
+
+ private ObservableList applications;
+
+ /**
+ * Creates a wrapper for the observable list.
+ */
+ public JobApplicationList(ObservableList applications) {
+ this.applications = applications;
+ }
+
+ /**
+ * Gives the list of applications.
+ */
+ public ObservableList getApplications() {
+ return applications;
+ }
+
+ /**
+ * Removes application from the list.
+ */
+ public void remove(JobApplication application) {
+ applications.remove(application);
+ }
+
+ /**
+ * Adds application to list.
+ */
+ public void add(JobApplication jobApplication) {
+ applications.add(jobApplication);
+ }
+
+ /**
+ * Sets application at the specified index.
+ */
+ public void set(int index, JobApplication jobApplication) {
+ applications.set(index, jobApplication);
+ }
+
+ /**
+ * Gets the application at the specified index.
+ */
+ public JobApplication get(int index) {
+ return applications.get(index);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/SortCommandTest.java b/src/test/java/seedu/address/logic/commands/SortCommandTest.java
index 9ec494ef696..727a2033043 100644
--- a/src/test/java/seedu/address/logic/commands/SortCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/SortCommandTest.java
@@ -1,45 +1,37 @@
package seedu.address.logic.commands;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.Comparator;
import org.junit.jupiter.api.Test;
-import seedu.address.model.Model;
-import seedu.address.model.ModelManager;
import seedu.address.model.contact.Contact;
+import seedu.address.model.jobapplication.JobApplication;
class SortCommandTest {
// TODO: tech debt - lousy tests
- @Test
- public void constructor_null_throwsNullPtrException() {
- assertThrows(NullPointerException.class, () -> new SortCommand(null));
- }
-
- @Test
- public void constructor_validComparator_doesNotThrow() {
- assertDoesNotThrow(() -> new SortCommand((a, b) -> a.hashCode() - b.hashCode()));
- }
-
- @Test
- public void execute_validComparator_doesNotThrow() {
- Model model = new ModelManager();
- assertDoesNotThrow(() -> new SortCommand((a, b) -> a.getType().compareTo(b.getType())).execute(model));
- }
-
@Test
public void equals() {
Comparator a = (c, b) -> c.getType().compareTo(b.getType());
Comparator d = (c1, c2) -> (-c1.getType().compareTo(c2.getType()));
- SortCommand s = new SortCommand(a);
+ Comparator j = Comparator.comparing(application ->
+ application.getJobTitle().title, String.CASE_INSENSITIVE_ORDER);
+ Comparator j2 = Comparator.comparing(application ->
+ application.getDeadline().deadline);
+
+ SortCommand s = new SortCommand(a, j, false);
+ SortCommand s2 = new SortCommand(a, j, true);
+
assertEquals(s, s);
- assertEquals(s, new SortCommand(a));
- assertNotEquals(s, new SortCommand(d));
+ assertEquals(s, new SortCommand(a, j, false));
+ //it does not matter what the comparators are if the SortCommand is a reset
+ assertEquals(s2, new SortCommand(d, j2, true));
+ assertNotEquals(s, new SortCommand(a, j2, false));
+ assertNotEquals(s, new SortCommand(d, j, false));
+ assertNotEquals(s, new SortCommand(a, j, true));
}
}
diff --git a/src/test/java/seedu/address/logic/parser/AppParserTest.java b/src/test/java/seedu/address/logic/parser/AppParserTest.java
index 9fc158af37b..4b542add799 100644
--- a/src/test/java/seedu/address/logic/parser/AppParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/AppParserTest.java
@@ -176,6 +176,7 @@ public void parseCompletionGenerator_knownSubsequence_canGenerateCorrectSuggesti
"edit",
"exit",
"delete",
+ "remind",
"help",
"clear"
),
diff --git a/src/test/java/seedu/address/model/jobapplication/JobApplicationListTest.java b/src/test/java/seedu/address/model/jobapplication/JobApplicationListTest.java
new file mode 100644
index 00000000000..ec8807eaa9f
--- /dev/null
+++ b/src/test/java/seedu/address/model/jobapplication/JobApplicationListTest.java
@@ -0,0 +1,5 @@
+package seedu.address.model.jobapplication;
+
+class JobApplicationListTest {
+
+}
diff --git a/src/test/java/seedu/address/model/jobapplication/JobApplicationTest.java b/src/test/java/seedu/address/model/jobapplication/JobApplicationTest.java
index 7d3ff4b5e98..6a4295d2989 100644
--- a/src/test/java/seedu/address/model/jobapplication/JobApplicationTest.java
+++ b/src/test/java/seedu/address/model/jobapplication/JobApplicationTest.java
@@ -26,6 +26,7 @@ public class JobApplicationTest {
private Id id1 = new Id();
private Id id2 = new Id();
private JobTitle validTitle = new JobTitle("SWE");
+ private JobTitle validTitle2 = new JobTitle("Analyst");
private JobDescription validJobDescription = new JobDescription("Pay: $700/h");
private JobStatus validStatus = JobStatus.PENDING;
private ApplicationStage validApplicationStage = ApplicationStage.RESUME;
@@ -72,13 +73,13 @@ public void staticComparatorMethods_various_returnsExpected() {
JobApplication ja2;
ja1 = new JobApplication(NUS, validTitle, validJobDescription, deadline1, JobStatus.PENDING,
ApplicationStage.RESUME);
- ja2 = new JobApplication(NUS, validTitle, validJobDescription, deadline2,
+ ja2 = new JobApplication(NUS, validTitle2, validJobDescription, deadline2,
JobStatus.OFFERED, ApplicationStage.INTERVIEW);
assertTrue(JobApplication.DEADLINE_COMPARATOR.compare(ja1, ja2) > 0);
assertTrue(JobApplication.STAGE_COMPARATOR.compare(ja1, ja2) < 0);
assertTrue(JobApplication.STATUS_COMPARATOR.compare(ja1, ja2) < 0);
-
+ assertTrue(JobApplication.JOB_TITLE_COMPARATOR.compare(ja1, ja2) > 0);
}
@Test