Skip to content
This repository was archived by the owner on Jan 10, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
package spending;

import java.util.Collection;

import spending.triggers.AnalyzesSpending;
import spending.triggers.FetchesRecentPayments;
import spending.triggers.WarnsBigSpenders;
import spending.values.BigSpend;
import spending.values.RecentPayments;

public class TriggersUnusualSpendingEmail {

FetchesRecentPayments fetchesRecentPayments = new FetchesRecentPayments();
AnalyzesSpending analyzesSpending = new AnalyzesSpending();
WarnsBigSpenders warnsBigSpenders = new WarnsBigSpenders();

public void trigger(long userId) {
// TODO: This is the entry point. Start with a test of this class
RecentPayments payments = fetchesRecentPayments.fetch(userId);
Collection<BigSpend> bigSpends = analyzesSpending.analyze(payments);
if (!bigSpends.isEmpty()) {
warnsBigSpenders.warn(userId, bigSpends);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package spending.triggers;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.Map;

import spending.triggers.analyzes.FiltersBigSpending;
import spending.triggers.analyzes.GroupsByCategory;
import spending.triggers.analyzes.SumsTotals;
import spending.values.BigSpend;
import spending.values.Category;
import spending.values.RecentPayments;

public class AnalyzesSpending {

GroupsByCategory groupsByCategory = new GroupsByCategory();
SumsTotals sumsTotals = new SumsTotals();
FiltersBigSpending filtersBigSpending = new FiltersBigSpending();

public Collection<BigSpend> analyze(RecentPayments payments) {
Map<Category, BigDecimal> currentTotals = sumsTotals.sum(groupsByCategory.group(payments.currentPayments));
Map<Category, BigDecimal> previousTotals = sumsTotals.sum(groupsByCategory.group(payments.previousPayments));
return filtersBigSpending.filter(currentTotals, previousTotals);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package spending.triggers;

import java.util.Set;

import spending.triggers.fetches.FetchesPaymentsWrapper;
import spending.triggers.fetches.FindsMonths;
import spending.values.Payment;
import spending.values.RecentPayments;

public class FetchesRecentPayments {

FindsMonths findsMonths = new FindsMonths();
FetchesPaymentsWrapper fetchesPaymentsWrapper = new FetchesPaymentsWrapper();

public RecentPayments fetch(long userId) {
Set<Payment> currentPayments = fetchesPaymentsWrapper.fetch(userId, findsMonths.thisMonth());
Set<Payment> previousPayments = fetchesPaymentsWrapper.fetch(userId, findsMonths.lastMonth());
return new RecentPayments(currentPayments, previousPayments);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package spending.triggers;

import java.util.Collection;

import spending.triggers.warns.ComposesBody;
import spending.triggers.warns.ComposesSubject;
import spending.triggers.warns.EmailsUserWrapper;
import spending.values.BigSpend;

public class WarnsBigSpenders {

ComposesSubject composesSubject = new ComposesSubject();
ComposesBody composesBody = new ComposesBody();
EmailsUserWrapper emailsUserWrapper = new EmailsUserWrapper();

public void warn(long userId, Collection<BigSpend> bigSpends) {
emailsUserWrapper.email(userId, composesSubject.compose(bigSpends), composesBody.compose(bigSpends));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package spending.triggers.analyzes;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

import spending.values.BigSpend;
import spending.values.Category;

public class FiltersBigSpending {

public Collection<BigSpend> filter(Map<Category, BigDecimal> currentTotals,
Map<Category, BigDecimal> previousTotals) {
Collection<BigSpend> bigSpends = new ArrayList<BigSpend>();
for (Map.Entry<Category, BigDecimal> currentEntry : currentTotals.entrySet()) {
BigDecimal currentTotal = currentEntry.getValue();
BigDecimal previousTotal = previousTotals.get(currentEntry.getKey());
if (previousTotal == null) {
bigSpends.add(new BigSpend(currentTotal, new BigDecimal("0"), currentEntry.getKey()));
} else if (isGreater(currentTotal, significantlyHigher(previousTotal))) {
bigSpends.add(new BigSpend(currentTotal, previousTotal, currentEntry.getKey()));
}
}
return bigSpends;
}

private boolean isGreater(BigDecimal isThisGreater, BigDecimal thanThis) {
return isThisGreater.compareTo(thanThis) == 1;
}

private BigDecimal significantlyHigher(BigDecimal amount) {
return amount.multiply(new BigDecimal("1.5"));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package spending.triggers.analyzes;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import spending.values.Category;
import spending.values.Payment;

public class GroupsByCategory {

public Map<Category, Set<Payment>> group(Set<Payment> payments) {
HashMap<Category, Set<Payment>> groupedPayments = new HashMap<Category, Set<Payment>>();
for (Payment payment : payments) {
if (!groupedPayments.containsKey(payment.category)) {
groupedPayments.put(payment.category, new HashSet<Payment>());
}
groupedPayments.get(payment.category).add(payment);
}
return groupedPayments;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package spending.triggers.analyzes;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import spending.values.Category;
import spending.values.Payment;

public class SumsTotals {

public Map<Category, BigDecimal> sum(Map<Category, Set<Payment>> payments) {
Map<Category, BigDecimal> totals = new HashMap<Category, BigDecimal>();
for (Map.Entry<Category, Set<Payment>> entry : payments.entrySet()) {
totals.put(entry.getKey(), sumOf(entry.getValue()));
}
return totals;
}

private BigDecimal sumOf(Set<Payment> payments) {
BigDecimal sum = new BigDecimal("0");
for (Payment payment : payments) {
sum = sum.add(payment.price);
}
return sum;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package spending.triggers.fetches;

import java.util.Set;

import spending.FetchesUserPaymentsByMonth;
import spending.values.Month;
import spending.values.Payment;

public class FetchesPaymentsWrapper {

FetchesUserPaymentsByMonth<Payment> fetchesUserPaymentsByMonth = FetchesUserPaymentsByMonth.getInstance();

public Set<Payment> fetch(long userId, Month month) {
return fetchesUserPaymentsByMonth.fetch(userId, month.year, month.month);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package spending.triggers.fetches;

import java.util.Calendar;
import java.util.GregorianCalendar;

import spending.values.Month;

public class FindsMonths {

public Month thisMonth() {
Calendar now = GregorianCalendar.getInstance();
return new Month(now.get(Calendar.YEAR), now.get(Calendar.MONTH));
}

public Month lastMonth() {
Calendar lastMonth = GregorianCalendar.getInstance();
lastMonth.add(Calendar.MONTH, -1);
return new Month(lastMonth.get(Calendar.YEAR), lastMonth.get(Calendar.MONTH));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package spending.triggers.warns;

import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.Collection;

import spending.values.BigSpend;

public class ComposesBody {

public String compose(Collection<BigSpend> bigSpends) {
StringBuilder sb = new StringBuilder();
sb.append("Hello card user!\n\n");
sb.append("We have detected unusually high spending on your card in these categories:\n\n");
for (BigSpend bigSpend : bigSpends) {
sb.append("* You spent ").append(toCurrency(bigSpend.actualSpending));
sb.append(" on ").append(bigSpend.category.toString().toLowerCase());
sb.append("\n");
}
sb.append("\nLove,\n\n");
sb.append("The Credit Card Company");
return sb.toString();
}

private String toCurrency(BigDecimal total) {
return NumberFormat.getCurrencyInstance().format(total);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package spending.triggers.warns;

import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.Collection;

import spending.values.BigSpend;

public class ComposesSubject {

public String compose(Collection<BigSpend> bigSpends) {
BigDecimal total = new BigDecimal("0");
for (BigSpend bigSpend : bigSpends) {
total = total.add(bigSpend.actualSpending);
}
return "Unusual spending of " + toCurrency(total) + " detected!";
}

private String toCurrency(BigDecimal total) {
return NumberFormat.getCurrencyInstance().format(total);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package spending.triggers.warns;

import spending.EmailsUser;

public class EmailsUserWrapper {

public void email(long userId, String subject, String body) {
EmailsUser.email(userId, subject, body);
}

}
17 changes: 17 additions & 0 deletions unusual-spending/src/main/java/spending/values/BigSpend.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package spending.values;

import java.math.BigDecimal;

public class BigSpend {

public final BigDecimal actualSpending;
public final BigDecimal usualSpending;
public final Category category;

public BigSpend(BigDecimal actualSpending, BigDecimal usualSpending, Category category) {
this.actualSpending = actualSpending;
this.usualSpending = usualSpending;
this.category = category;
}

}
5 changes: 5 additions & 0 deletions unusual-spending/src/main/java/spending/values/Category.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package spending.values;

public enum Category {
TV, GOLF, ENTERTAINMENT, RESTAURANTS, TRAINING_EVENTS, TRAVEL, GROCERIES
}
13 changes: 13 additions & 0 deletions unusual-spending/src/main/java/spending/values/Month.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package spending.values;

public class Month {

public final int year;
public final int month;

public Month(int year, int month) {
this.year = year;
this.month = month;
}

}
17 changes: 17 additions & 0 deletions unusual-spending/src/main/java/spending/values/Payment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package spending.values;

import java.math.BigDecimal;

public class Payment {

public final Category category;
public final BigDecimal price;
public final String description;

public Payment(Category category, BigDecimal price, String description) {
this.category = category;
this.price = price;
this.description = description;
}

}
15 changes: 15 additions & 0 deletions unusual-spending/src/main/java/spending/values/RecentPayments.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package spending.values;

import java.util.Set;

public class RecentPayments {

public final Set<Payment> currentPayments;
public final Set<Payment> previousPayments;

public RecentPayments(Set<Payment> currentPayments, Set<Payment> previousPayments) {
this.currentPayments = currentPayments;
this.previousPayments = previousPayments;
}

}
Loading