Skip to content

Commit

Permalink
Add rudimentary name and amount functionality to FilterCommand
Browse files Browse the repository at this point in the history
  • Loading branch information
sp4ce-cowboy committed Oct 28, 2023
1 parent a589659 commit 896ed00
Show file tree
Hide file tree
Showing 14 changed files with 333 additions and 46 deletions.
86 changes: 86 additions & 0 deletions src/main/java/unicash/logic/commands/FilterCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package unicash.logic.commands;

import static java.util.Objects.requireNonNull;
import static unicash.logic.parser.CliSyntax.PREFIX_AMOUNT;
import static unicash.logic.parser.CliSyntax.PREFIX_CATEGORY;
import static unicash.logic.parser.CliSyntax.PREFIX_DATETIME;
import static unicash.logic.parser.CliSyntax.PREFIX_LOCATION;
import static unicash.logic.parser.CliSyntax.PREFIX_NAME;
import static unicash.logic.parser.CliSyntax.PREFIX_TYPE;

import unicash.commons.util.ToStringBuilder;
import unicash.logic.UniCashMessages;
import unicash.logic.commands.exceptions.CommandException;
import unicash.model.Model;
import unicash.model.transaction.predicates.TransactionContainsAllKeywordsPredicate;

/**
* Filters the displayed Transactions according to certain parameters
*/
public class FilterCommand extends Command {
public static final String COMMAND_WORD = "filter";

public static final String MESSAGE_USAGE = COMMAND_WORD + ": Filters the transactions in UniCa$h "
+ "according to the specified properties"
+ "All properties must match in order for the transaction to be displayed."
+ "Any combination of properties may be provided but at least one property must be provided."
+ "\n\n"
+ "[" + PREFIX_NAME + "NAME] "
+ "[" + PREFIX_TYPE + "TYPE] "
+ "[" + PREFIX_AMOUNT + "AMOUNT] "
+ "[" + PREFIX_DATETIME + "DATETIME] "
+ "[" + PREFIX_LOCATION + "LOCATION]"
+ "[" + PREFIX_CATEGORY + "CATEGORY]..."
+ "\n\n"
+ "Example: " + COMMAND_WORD + " "
+ PREFIX_NAME + "Buying groceries "
+ PREFIX_TYPE + "expense "
+ PREFIX_AMOUNT + "300 "
+ PREFIX_DATETIME + "18-08-2001 19:30 "
+ PREFIX_LOCATION + "ntuc"
+ PREFIX_CATEGORY + "household";

public static final String MESSAGE_SUCCESS = "Filtered %1$s Transactions";

private final TransactionContainsAllKeywordsPredicate predicate;

/**
* Creates an FilterCommand to filter the transactions list
*/
public FilterCommand(TransactionContainsAllKeywordsPredicate predicate) {
requireNonNull(predicate);
this.predicate = predicate;
}

@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
model.updateFilteredTransactionList(predicate);
int listSize = model.getFilteredTransactionList().size();
return new CommandResult(String.format(UniCashMessages.MESSAGE_TRANSACTIONS_LISTED_OVERVIEW,
listSize));
}

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

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

FilterCommand otherCommand = (FilterCommand) other;
return predicate.equals(otherCommand.predicate);

}

@Override
public String toString() {
return new ToStringBuilder(this)
.add("predicate", predicate)
.toString();
}
}
6 changes: 3 additions & 3 deletions src/main/java/unicash/logic/commands/FindCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import unicash.commons.util.ToStringBuilder;
import unicash.logic.UniCashMessages;
import unicash.model.Model;
import unicash.model.transaction.predicates.TransactionContainsKeywordsPredicate;
import unicash.model.transaction.predicates.TransactionContainsAnyKeywordsPredicate;

/**
* Finds and lists all transactions in UniCa$h whose name contains any of the argument keywords.
Expand All @@ -22,9 +22,9 @@ public class FindCommand extends Command {
+ "\n"
+ "Example: " + COMMAND_WORD + " chicken rice";

private final TransactionContainsKeywordsPredicate predicate;
private final TransactionContainsAnyKeywordsPredicate predicate;

public FindCommand(TransactionContainsKeywordsPredicate predicate) {
public FindCommand(TransactionContainsAnyKeywordsPredicate predicate) {
this.predicate = predicate;
}

Expand Down
67 changes: 67 additions & 0 deletions src/main/java/unicash/logic/parser/FilterCommandParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package unicash.logic.parser;

import static java.util.Objects.requireNonNull;
import static unicash.logic.UniCashMessages.MESSAGE_INVALID_COMMAND_FORMAT;
import static unicash.logic.parser.CliSyntax.PREFIX_AMOUNT;
import static unicash.logic.parser.CliSyntax.PREFIX_CATEGORY;
import static unicash.logic.parser.CliSyntax.PREFIX_DATETIME;
import static unicash.logic.parser.CliSyntax.PREFIX_LOCATION;
import static unicash.logic.parser.CliSyntax.PREFIX_NAME;
import static unicash.logic.parser.CliSyntax.PREFIX_TYPE;

import java.util.List;

import unicash.logic.commands.FilterCommand;
import unicash.logic.parser.exceptions.ParseException;
import unicash.model.commons.Amount;
import unicash.model.transaction.Name;
import unicash.model.transaction.predicates.TransactionContainsAllKeywordsPredicate;

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

public static final String EMPTY_STRING = "";
public static final List<String> EMPTY_STRING_LIST = List.of(EMPTY_STRING);

private static TransactionContainsAllKeywordsPredicate filterPredicate =
new TransactionContainsAllKeywordsPredicate();

/**
* Parses the given {@code String} of arguments in the context of the FilterCommand
* and returns an FilterCommand object for execution.
*
* @throws ParseException if the user input does not conform the expected format
*/
public FilterCommand parse(String args) throws ParseException {
requireNonNull(args);
ArgumentMultimap argMultimap =
ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_TYPE, PREFIX_AMOUNT, PREFIX_DATETIME,
PREFIX_CATEGORY, PREFIX_LOCATION);

String trimmedArgs = args.trim();
if (trimmedArgs.isEmpty()) {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_USAGE));
}

argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_TYPE, PREFIX_AMOUNT, PREFIX_DATETIME,
PREFIX_LOCATION);


if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
Name transactionName = ParserUtil.parseTransactionName(argMultimap.getValue(PREFIX_NAME).get());
filterPredicate.setName(transactionName.toString());

}

if (argMultimap.getValue(PREFIX_AMOUNT).isPresent()) {
Amount transactionAmount = ParserUtil.parseAmount(argMultimap.getValue(PREFIX_AMOUNT).get());
filterPredicate.setAmount(Amount.amountToDecimalString(transactionAmount));
}

return new FilterCommand(filterPredicate);
}

}
4 changes: 2 additions & 2 deletions src/main/java/unicash/logic/parser/FindCommandParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import unicash.logic.commands.FindCommand;
import unicash.logic.parser.exceptions.ParseException;
import unicash.model.transaction.predicates.TransactionContainsKeywordsPredicate;
import unicash.model.transaction.predicates.TransactionContainsAnyKeywordsPredicate;

/**
* Parses input arguments and creates a new FindCommand object
Expand All @@ -27,7 +27,7 @@ public FindCommand parse(String args) throws ParseException {

String[] nameKeywords = trimmedArgs.split("\\s+");

return new FindCommand(new TransactionContainsKeywordsPredicate(
return new FindCommand(new TransactionContainsAnyKeywordsPredicate(
Arrays.asList(nameKeywords)));
}

Expand Down
4 changes: 4 additions & 0 deletions src/main/java/unicash/logic/parser/UniCashParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import unicash.logic.commands.DeleteTransactionCommand;
import unicash.logic.commands.EditTransactionCommand;
import unicash.logic.commands.ExitCommand;
import unicash.logic.commands.FilterCommand;
import unicash.logic.commands.FindCommand;
import unicash.logic.commands.GetCommand;
import unicash.logic.commands.GetTotalExpenditureCommand;
Expand Down Expand Up @@ -93,6 +94,9 @@ public Command parseCommand(String userInput) throws ParseException {
case SummaryCommand.COMMAND_WORD:
return new SummaryCommand();

case FilterCommand.COMMAND_WORD:
return new FilterCommandParser().parse(arguments);

default:
logger.finer("This user input caused a ParseException: " + userInput);
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package unicash.model.transaction.predicates;

import java.util.List;
import java.util.function.Predicate;

import unicash.commons.util.ToStringBuilder;
import unicash.model.transaction.Transaction;




/**
* Tests that any of a {@code Transactions}'s properties matches all the keywords given.
* Matching in this context either means a partial match of the keyword with the query, or
* a full match of the keyword, repeated for each keyword present in the list of keywords.
*
* </p> The matching depends on the property of the Transaction being matched, and the
* most appropriate matching is specified within the associated property predicate. This class
* simulates a composed predicate that represents a short-circuiting logical AND of all property
* predicates.
*/
public class TransactionContainsAllKeywordsPredicate
implements Predicate<Transaction> {
public static final String EMPTY_STRING = "";
public static final List<String> EMPTY_STRING_LIST = List.of(EMPTY_STRING);

private TransactionAmountContainsValuePredicate amountPredicate;
private boolean amountPredicateExists;

private TransactionCategoryContainsKeywordsPredicate categoryPredicate;
private TransactionDateTimeContainsValuePredicate dateTimePredicate;
private TransactionLocationContainsKeywordsPredicate locationPredicate;
private TransactionNameContainsKeywordsPredicate namePredicate;
private boolean namePredicateExists;

private TransactionTypeContainsValuePredicate typePredicate;
private Predicate<Transaction> composedTransactionPredicate;


/**
* Creates a composed predicate object.
*/
public TransactionContainsAllKeywordsPredicate() {
amountPredicate = new TransactionAmountContainsValuePredicate(EMPTY_STRING_LIST);
amountPredicateExists = false;

categoryPredicate = new TransactionCategoryContainsKeywordsPredicate(EMPTY_STRING_LIST);
dateTimePredicate = new TransactionDateTimeContainsValuePredicate(EMPTY_STRING_LIST);
locationPredicate = new TransactionLocationContainsKeywordsPredicate(EMPTY_STRING_LIST);

namePredicate = new TransactionNameContainsKeywordsPredicate(EMPTY_STRING_LIST);
namePredicateExists = false;

typePredicate = new TransactionTypeContainsValuePredicate(EMPTY_STRING_LIST);
composedTransactionPredicate = amountPredicate
.and(categoryPredicate)
.and(dateTimePredicate)
.and(locationPredicate)
.and(namePredicate)
.and(typePredicate);
}

@Override
public boolean test(Transaction transaction) {
Predicate<Transaction> composedPredicate = composeAllPredicates();
return composedPredicate.test(transaction);

}

/**
* Composes all predicates.
*
* @return a composed predicate
*/
public Predicate<Transaction> composeAllPredicates() {
Predicate<Transaction> composedPredicate = unused -> true;

if (amountPredicateExists) {
composedPredicate = composedPredicate.and(amountPredicate);
}

if (namePredicateExists) {
composedPredicate = composedPredicate.and(namePredicate);
}

return composedPredicate;
}

public void setAmount(String amount) {
amountPredicate = new TransactionAmountContainsValuePredicate(List.of(amount));
amountPredicateExists = true;
}

public void setName(String name) {
namePredicate = new TransactionNameContainsKeywordsPredicate(List.of(name));
namePredicateExists = true;
}


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

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

TransactionContainsAllKeywordsPredicate otherContainsKeywordsPredicate =
(TransactionContainsAllKeywordsPredicate) other;

return amountPredicate.equals(otherContainsKeywordsPredicate.amountPredicate)
&& namePredicate.equals(otherContainsKeywordsPredicate.namePredicate)
&& composedTransactionPredicate.equals(otherContainsKeywordsPredicate
.composedTransactionPredicate);

}

@Override
public String toString() {
return new ToStringBuilder(this)
.add("composedTransactionPredicate", composedTransactionPredicate)
.add("amountPredicate", amountPredicate)
.add("namePredicate", namePredicate)
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
* simulates a composed predicate that represents a short-circuiting logical OR of all property
* predicates.
*/
public class TransactionContainsKeywordsPredicate
public class TransactionContainsAnyKeywordsPredicate
implements Predicate<Transaction> {
private final List<String> keywords;

public TransactionContainsKeywordsPredicate(List<String> keywords) {
public TransactionContainsAnyKeywordsPredicate(List<String> keywords) {
this.keywords = keywords;
}

Expand All @@ -46,12 +46,12 @@ public boolean equals(Object other) {
}

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

TransactionContainsKeywordsPredicate otherNameContainsKeywordsPredicate =
(TransactionContainsKeywordsPredicate) other;
TransactionContainsAnyKeywordsPredicate otherNameContainsKeywordsPredicate =
(TransactionContainsAnyKeywordsPredicate) other;
return keywords.equals(otherNameContainsKeywordsPredicate.keywords);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
public class TransactionNameContainsKeywordsPredicate
implements Predicate<Transaction> {

private final List<String> keywords;

public TransactionNameContainsKeywordsPredicate(List<String> keywords) {
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/unicash/logic/commands/CommandTestUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import unicash.model.Model;
import unicash.model.UniCash;
import unicash.model.transaction.Transaction;
import unicash.model.transaction.predicates.TransactionContainsKeywordsPredicate;
import unicash.model.transaction.predicates.TransactionContainsAnyKeywordsPredicate;
import unicash.testutil.EditTransactionDescriptorBuilder;

/**
Expand Down Expand Up @@ -147,7 +147,7 @@ public static void showTransactionAtIndex(Model model, Index targetIndex) {
Transaction transaction = model.getFilteredTransactionList().get(targetIndex.getZeroBased());
final String[] splitName = transaction.getName().fullName.split("\\s+");
model.updateFilteredTransactionList(
new TransactionContainsKeywordsPredicate(Collections.singletonList(splitName[0])));
new TransactionContainsAnyKeywordsPredicate(Collections.singletonList(splitName[0])));

assertEquals(1, model.getFilteredTransactionList().size());
}
Expand Down
Loading

0 comments on commit 896ed00

Please sign in to comment.