app/model/editions/EditionsAppTemplates.scala (223 lines of code) (raw):

package model.editions import java.time.temporal.ChronoField import java.time.{LocalDate, ZoneId, ZoneOffset} import enumeratum.EnumEntry.{Hyphencase, Uncapitalised} import enumeratum.{EnumEntry, PlayEnum} import model.editions.PathType.{PrintSent, Search} import model.editions.templates.TemplateHelpers.Defaults import model.editions.templates._ import model.editions.templates.feast.{ FeastAppEdition, FeastNorthernHemisphere, FeastSouthernHemisphere } import org.postgresql.util.PGobject import play.api.libs.json.{Json, OFormat} import services.editions.prefills.CapiQueryTimeWindow object EditionsAppTemplates { val templates: Map[Edition, EditionsAppDefinitionWithTemplate] = Map( Edition.DailyEdition -> DailyEdition, Edition.AustralianEdition -> AustralianEdition, Edition.TrainingEdition -> TrainingEdition, Edition.TheDummyEdition -> TheDummyEdition, Edition.EditionEarth -> EditionEarth, Edition.EditionBooks -> EditionBooks, Edition.EditionWeWereThere -> EditionWeWereThere, Edition.EditionEurosSpecial -> EditionEurosSpecial, Edition.EditionOlympicLegends -> EditionOlympicLegends, Edition.EditionEndOfYear -> EditionEndOfYear, Edition.EditionWellbeing -> EditionWellbeing ) val getAvailableTemplates: List[EditionsAppDefinitionWithTemplate] = templates.values.toList /** Returns available Editons app templates as a Map which differentiates the * various classes of edition. This is used for grouping purposes in both the * UI and the publication logic * @return * a Map relating the edition class to a list of the relevant types. */ def getAvailableEditionsAppTemplates : Map[String, List[CuratedPlatformDefinition]] = { val allEditions = getAvailableTemplates val regionalEditions = allEditions.filter(e => e.editionType == EditionType.Regional) val specialEditions = allEditions.filter(e => e.editionType == EditionType.Special) val trainingEditions = allEditions.filter(e => e.editionType == EditionType.Training) Map( "regionalEditions" -> regionalEditions, "specialEditions" -> specialEditions, "trainingEditions" -> trainingEditions ) } } object FeastAppTemplates { val templates: Map[Edition, FeastAppEdition] = Map( Edition.FeastNorthernHemisphere -> FeastNorthernHemisphere, Edition.FeastSouthernHemisphere -> FeastSouthernHemisphere ) val getAvailableTemplates: List[CuratedPlatformWithTemplate] = templates.values.toList } object AllTemplates { val templates = EditionsAppTemplates.templates ++ FeastAppTemplates.templates } sealed trait CuratedPlatform extends EnumEntry with Uncapitalised object CuratedPlatform extends PlayEnum[CuratedPlatform] { case object Editions extends CuratedPlatform case object Feast extends CuratedPlatform override def values = findValues } case object WeekDay extends Enumeration(1) { implicit lazy val implicitConversions: languageFeature.implicitConversions = scala.language.implicitConversions type WeekDay = Value val Mon, Tues, Wed, Thurs, Fri, Sat, Sun = Value implicit def WeekDayToInt(weekDay: WeekDay): Int = weekDay.id } sealed abstract class Swatch extends EnumEntry with Uncapitalised object Swatch extends PlayEnum[Swatch] { case object Neutral extends Swatch case object News extends Swatch case object Opinion extends Swatch case object Culture extends Swatch case object Lifestyle extends Swatch case object Sport extends Swatch case object Special extends Swatch override def values = findValues } sealed abstract class Edition extends EnumEntry with Hyphencase object Edition extends PlayEnum[Edition] { case object DailyEdition extends Edition case object AustralianEdition extends Edition case object TrainingEdition extends Edition case object TheDummyEdition extends Edition case object EditionEarth extends Edition case object EditionBooks extends Edition case object EditionWeWereThere extends Edition case object EditionEurosSpecial extends Edition case object EditionOlympicLegends extends Edition case object EditionEndOfYear extends Edition case object EditionWellbeing extends Edition case object FeastSouthernHemisphere extends Edition case object FeastNorthernHemisphere extends Edition override def values = findValues } case class FrontPresentation(swatch: Swatch) { implicit def frontPresentationFormat: OFormat[FrontPresentation] = Json.format[FrontPresentation] } object FrontPresentation { implicit def frontPresentationFormat: OFormat[FrontPresentation] = Json.format[FrontPresentation] } case class CapiPrefillQuery(queryString: String, pathType: PathType) { def escapedQueryString(): String = queryString .replace(",", "%2C") .replace("|", "%7C") .replace("(", "%28") .replace(")", "%29") } object CapiPrefillQuery { implicit def format: OFormat[CapiPrefillQuery] = Json.format[CapiPrefillQuery] } import model.editions.WeekDay._ trait Periodicity { def isValid(date: LocalDate): Boolean } case class Daily() extends Periodicity { def isValid(date: LocalDate) = true } case class WeekDays(days: List[WeekDay]) extends Periodicity { def isValid(date: LocalDate) = days.exists( WeekDayToInt(_) == date.getDayOfWeek.get(ChronoField.DAY_OF_WEEK) ) } sealed abstract class PathType extends EnumEntry with Uncapitalised { def toPathSegment: String = { this match { case PathType.Search => "search" case PathType.PrintSent => "content/print-sent" } } } object PathType extends PlayEnum[PathType] { case object Search extends PathType case object PrintSent extends PathType override def values = findValues } private[editions] case class CollectionTemplate( name: String, maybeOphanPath: Option[String] = None, prefill: Option[CapiPrefillQuery], maybeTimeWindowConfig: Option[CapiTimeWindowConfigInDays] = None, hidden: Boolean = false, cardCap: Int = Defaults.defaultCollectionCardsCap ) { def hide = copy(hidden = true) def printSentPrefill(prefillQuery: String) = copy(prefill = Some(CapiPrefillQuery(prefillQuery, PrintSent))) def printSentAnyTag(tags: String*) = printSentPrefill( s"?tag=${tags.mkString("|")}" ) def printSentAllTags(tags: String*) = printSentPrefill( s"?tag=${tags.mkString(",")}" ) def searchPrefill(prefillQuery: String) = copy(prefill = Some(CapiPrefillQuery(prefillQuery, Search))) def withCardItemsCap(articleItemsCap: Int) = copy(cardCap = articleItemsCap) def withTimeWindowConfig( maybeTimeWindowConfig: Option[CapiTimeWindowConfigInDays] ) = copy(maybeTimeWindowConfig = maybeTimeWindowConfig) } case class FrontTemplate( name: String, collections: List[CollectionTemplate], presentation: FrontPresentation, maybeOphanPath: Option[String] = None, maybeTimeWindowConfig: Option[CapiTimeWindowConfigInDays] = None, isSpecial: Boolean = false, hidden: Boolean = false ) { def special = copy(isSpecial = true, hidden = true) def swatch(swatch: Swatch) = copy(presentation = FrontPresentation(swatch)) def withTimeWindowConfig( maybeTimeWindowConfig: Option[CapiTimeWindowConfigInDays] ) = copy(maybeTimeWindowConfig = maybeTimeWindowConfig) } sealed abstract class CapiDateQueryParam extends EnumEntry with Hyphencase with Uncapitalised object CapiDateQueryParam extends PlayEnum[CapiDateQueryParam] { case object NewspaperEdition extends CapiDateQueryParam case object Published extends CapiDateQueryParam override def values = findValues } trait BaseTimeWindowConfig { def startOffset: Int def endOffset: Int } case class TimeWindowConfigInDays(startOffset: Int, endOffset: Int) extends BaseTimeWindowConfig case class CapiTimeWindowConfigInDays(startOffset: Int, endOffset: Int) extends BaseTimeWindowConfig { def toCapiQueryTimeWindow(issueDate: LocalDate): CapiQueryTimeWindow = { val issueDateStart = issueDate.atStartOfDay() // Regarding UTC Hack because composer/capi/whoever doesn't worry about timezones in the newspaper-edition date val fromDateUTC = issueDateStart.plusDays(startOffset).toInstant(ZoneOffset.UTC) val toDateAsUTC = issueDateStart.plusDays(endOffset).toInstant(ZoneOffset.UTC) CapiQueryTimeWindow(fromDateUTC, toDateAsUTC) } } case class OphanQueryPrefillParams( apiKey: String, timeWindowConfig: TimeWindowConfigInDays ) case class EditionTemplate( fronts: List[(FrontTemplate, Periodicity)], timeWindowConfig: CapiTimeWindowConfigInDays, capiDateQueryParam: CapiDateQueryParam, zoneId: ZoneId, availability: Periodicity, maybeOphanPath: Option[String] = None, ophanQueryPrefillParams: Option[OphanQueryPrefillParams] )