Skip to content

Commit

Permalink
Merge 70b4ed6 into 1740273
Browse files Browse the repository at this point in the history
  • Loading branch information
Mario Galic committed Aug 8, 2019
2 parents 1740273 + 70b4ed6 commit 0da946a
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 77 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package com.gu.holiday_stops

import java.time.temporal.ChronoUnit
import java.time.temporal.{ChronoUnit, TemporalAdjusters}

import com.gu.salesforce.holiday_stops.SalesforceHolidayStopRequestsDetail.ProductName
import java.time.{DayOfWeek, LocalDate}
import java.time.temporal.ChronoUnit.DAYS

case class ProductSpecifics(
firstAvailableDate: LocalDate,
Expand Down Expand Up @@ -44,7 +45,7 @@ object ActionCalculator {
val maxDaysBetweenTodayAndFirstAvailableDate: Int,
val firstAvailableDateDayOfWeek: DayOfWeek
) {
def verify(firstAvailableDate: LocalDate, today: LocalDate): Unit
def firstAvailableDate(today: LocalDate): LocalDate
}

case object GuardianWeeklySuspensionConstants extends ProductSuspensionConstants(
Expand All @@ -56,7 +57,24 @@ object ActionCalculator {
firstAvailableDateDayOfWeek = DayOfWeek.SATURDAY
) {

def verify(firstAvailableDate: LocalDate, today: LocalDate): Unit = {
/**
* If there are less than 5 days between today and the day after next publication day,
* then Saturday after next (i.e., next-next Saturday),
* otherwise next Saturday
*/
def firstAvailableDate(today: LocalDate): LocalDate = {
val dayAfterNextPublicationDay = TemporalAdjusters.next(issueDayOfWeek.plus(1)) // Saturday because GW is published on Friday, https://stackoverflow.com/a/29010338/5205022
val firstAvailableDate =
if (DAYS.between(today, today `with` dayAfterNextPublicationDay) < minDaysBetweenTodayAndFirstAvailableDate)
(today `with` dayAfterNextPublicationDay `with` dayAfterNextPublicationDay) // Saturday after next
else
(today `with` dayAfterNextPublicationDay) // next Saturday

verify(firstAvailableDate, today)
firstAvailableDate
}

private def verify(firstAvailableDate: LocalDate, today: LocalDate): Unit = {
val daysBetweenTodayAndFirstAvailableDate = ChronoUnit.DAYS.between(today, firstAvailableDate)
require(
(daysBetweenTodayAndFirstAvailableDate >= minDaysBetweenTodayAndFirstAvailableDate) &&
Expand All @@ -73,48 +91,10 @@ object ActionCalculator {
case _ => throw new RuntimeException(s"Failed to determine ProductSuspensionConstants because of unexpected productName: $productName ")
}

def findNextTargetDayOfWeek(start: LocalDate, targetDayOfWeek: DayOfWeek): LocalDate =
if (start.getDayOfWeek.getValue >= targetDayOfWeek.getValue)
start.plusWeeks(1) `with` targetDayOfWeek
else
start `with` targetDayOfWeek

/**
* Main business logic for calculating first available date per product.
*
* WARNING: Refactor with care. When adding a new product set debug flag to determine constants such as
* daysBetweenTodayAndFirstAvailableDate, firstAvailableDate.getDayOfWeek, etc.
*/
def getProductSpecifics(
productNamePrefix: ProductName,
today: LocalDate = LocalDate.now(),
debug: Boolean = false // flag used to learn how the algorithm works and determine constants for new products
): ProductSpecifics = {

def getProductSpecifics(productNamePrefix: ProductName, today: LocalDate = LocalDate.now()): ProductSpecifics = {
val productSuspensionConstants = suspensionConstantsByProduct(productNamePrefix)
val issueDayOfWeek = productSuspensionConstants.issueDayOfWeek // Friday for GW
val timezoneBluntAdjustmentDays = 1 // Salesforce and client-side might be in different timezones than AWS
val cutoffDate = today.plusDays(productSuspensionConstants.processorRunLeadTimeDays + timezoneBluntAdjustmentDays)
val nextIssueDayAfterCutoffDate = findNextTargetDayOfWeek(cutoffDate, issueDayOfWeek)
val firstAvailableDate = nextIssueDayAfterCutoffDate.minusWeeks(1).plusDays(1) // dayAfterNextPreventableIssue

productSuspensionConstants.verify(firstAvailableDate, today)

if (debug) {
println()
println(s"today=$today")
println(s"cutoffDate=$cutoffDate")
println(s"nextIssueDayAfterCutoffDate=$nextIssueDayAfterCutoffDate")
println(s"firstAvailableDate=$firstAvailableDate")
val daysBetweenTodayAndFirstAvailableDate = ChronoUnit.DAYS.between(today, firstAvailableDate)
println(s"daysBetweenTodayAndFirstAvailableDate=$daysBetweenTodayAndFirstAvailableDate")
}

ProductSpecifics(
firstAvailableDate = firstAvailableDate,
issueDayOfWeek.getValue,
productSuspensionConstants.annualIssueLimit
)
import productSuspensionConstants._
ProductSpecifics(firstAvailableDate(today), issueDayOfWeek.getValue, annualIssueLimit)
}

def publicationDatesToBeStopped(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ class ActionCalculatorTest extends FlatSpec with Matchers {
type Today = LocalDate
type FirstAvailableDate = LocalDate
val gwTodayToFirstAvailableDate = ListMap[Today, FirstAvailableDate](
LocalDate.of(2019, 6, 1) -> LocalDate.of(2019, 6, 8), // first available on Sun
LocalDate.of(2019, 6, 2) -> LocalDate.of(2019, 6, 8), // first available on Sun
LocalDate.of(2019, 6, 3) -> LocalDate.of(2019, 6, 8), // first available on Sun
LocalDate.of(2019, 6, 4) -> LocalDate.of(2019, 6, 15), // jump on Tue, a day before processor run
LocalDate.of(2019, 6, 5) -> LocalDate.of(2019, 6, 15), // first available on Sun
LocalDate.of(2019, 6, 6) -> LocalDate.of(2019, 6, 15), // first available on Sun
LocalDate.of(2019, 6, 7) -> LocalDate.of(2019, 6, 15), // first available on Sun
LocalDate.of(2019, 6, 8) -> LocalDate.of(2019, 6, 15), // first available on Sun
LocalDate.of(2019, 6, 9) -> LocalDate.of(2019, 6, 15), // first available on Sun
LocalDate.of(2019, 6, 1) -> LocalDate.of(2019, 6, 8), // first available on Sun
LocalDate.of(2019, 6, 2) -> LocalDate.of(2019, 6, 8), // first available on Sun
LocalDate.of(2019, 6, 3) -> LocalDate.of(2019, 6, 8), // first available on Sun
LocalDate.of(2019, 6, 4) -> LocalDate.of(2019, 6, 15), // jump on Tue, a day before processor run
LocalDate.of(2019, 6, 5) -> LocalDate.of(2019, 6, 15), // first available on Sun
LocalDate.of(2019, 6, 6) -> LocalDate.of(2019, 6, 15), // first available on Sun
LocalDate.of(2019, 6, 7) -> LocalDate.of(2019, 6, 15), // first available on Sun
LocalDate.of(2019, 6, 8) -> LocalDate.of(2019, 6, 15), // first available on Sun
LocalDate.of(2019, 6, 9) -> LocalDate.of(2019, 6, 15), // first available on Sun
LocalDate.of(2019, 6, 10) -> LocalDate.of(2019, 6, 15), // first available on Sun
LocalDate.of(2019, 6, 11) -> LocalDate.of(2019, 6, 22), // jump on Tue, a day before processor run
LocalDate.of(2019, 6, 12) -> LocalDate.of(2019, 6, 22), // first available on Sun
Expand All @@ -54,41 +54,26 @@ class ActionCalculatorTest extends FlatSpec with Matchers {
LocalDate.of(2019, 6, 22) -> LocalDate.of(2019, 6, 29), // first available on Sun
LocalDate.of(2019, 6, 23) -> LocalDate.of(2019, 6, 29), // first available on Sun
LocalDate.of(2019, 6, 24) -> LocalDate.of(2019, 6, 29), // first available on Sun
LocalDate.of(2019, 6, 25) -> LocalDate.of(2019, 7, 6), // jump on Tue, a day before processor run
LocalDate.of(2019, 6, 26) -> LocalDate.of(2019, 7, 6), // first available on Sun
LocalDate.of(2019, 6, 27) -> LocalDate.of(2019, 7, 6), // first available on Sun
LocalDate.of(2019, 6, 28) -> LocalDate.of(2019, 7, 6), // first available on Sun
LocalDate.of(2019, 6, 29) -> LocalDate.of(2019, 7, 6), // first available on Sun
LocalDate.of(2019, 6, 30) -> LocalDate.of(2019, 7, 6), // first available on Sun
LocalDate.of(2019, 7, 1) -> LocalDate.of(2019, 7, 6), // first available on Sun
LocalDate.of(2019, 7, 2) -> LocalDate.of(2019, 7, 13) // jump on Tue, a day before processor run
LocalDate.of(2019, 6, 25) -> LocalDate.of(2019, 7, 6), // jump on Tue, a day before processor run
LocalDate.of(2019, 6, 26) -> LocalDate.of(2019, 7, 6), // first available on Sun
LocalDate.of(2019, 6, 27) -> LocalDate.of(2019, 7, 6), // first available on Sun
LocalDate.of(2019, 6, 28) -> LocalDate.of(2019, 7, 6), // first available on Sun
LocalDate.of(2019, 6, 29) -> LocalDate.of(2019, 7, 6), // first available on Sun
LocalDate.of(2019, 6, 30) -> LocalDate.of(2019, 7, 6), // first available on Sun
LocalDate.of(2019, 7, 1) -> LocalDate.of(2019, 7, 6), // first available on Sun
LocalDate.of(2019, 7, 2) -> LocalDate.of(2019, 7, 13) // jump on Tue, a day before processor run

)

gwTodayToFirstAvailableDate foreach {
case (today, expected) =>
ActionCalculator
.getProductSpecifics(gwProductName, today, debug = false)
.getProductSpecifics(gwProductName, today)
.firstAvailableDate shouldEqual expected
}

}

it should "calculate the next target day of the week given a date" in {

val gwInputsAndExpected = Map(
DayOfWeek.THURSDAY -> LocalDate.of(2019, 6, 20),
DayOfWeek.FRIDAY -> LocalDate.of(2019, 6, 21),
DayOfWeek.SATURDAY -> LocalDate.of(2019, 6, 15),
)

gwInputsAndExpected foreach {
case (dayOfWeek, expected) =>
ActionCalculator.findNextTargetDayOfWeek(LocalDate.of(2019, 6, 14), dayOfWeek) shouldEqual expected
}

}

it should "correctly list the action dates for given Holiday Stop Request" in {

ActionCalculator.publicationDatesToBeStopped(
Expand Down

0 comments on commit 0da946a

Please sign in to comment.