All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gu.facia.api.models.curatedcontent.scala Maven / Gradle / Ivy

The newest version!
package com.gu.facia.api.models

import com.gu.contentapi.client.model.v1.{Content, TagType}
import com.gu.contentapi.client.utils.CapiModelEnrichment.RenderingFormat
import com.gu.contentapi.client.utils.format._
import com.gu.facia.api.utils.ContentApiUtils._
import com.gu.facia.api.utils._
import com.gu.facia.client.models.{MetaDataCommonFields, SupportingItem, Trail, TrailMetaData}

sealed trait FaciaImage
case class Cutout(imageSrc: String, imageSrcWidth: Option[String], imageSrcHeight: Option[String]) extends FaciaImage
case class Replace(imageSrc: String, imageSrcWidth: String, imageSrcHeight: String, imageCaption: Option[String]) extends FaciaImage
case class ImageSlideshow(assets: List[Replace]) extends FaciaImage

object FaciaImage {

  def getFaciaImage(maybeContent: Option[Content], trailMeta: MetaDataCommonFields, resolvedMetadata: ResolvedMetaData): Option[FaciaImage] = {
    if (resolvedMetadata.imageHide)
      None
    else if (resolvedMetadata.imageSlideshowReplace)
      imageSlideshow(trailMeta, resolvedMetadata)
    else if (resolvedMetadata.imageReplace)
      imageReplace(trailMeta, resolvedMetadata)
    else if (resolvedMetadata.imageCutoutReplace)
      imageCutout(trailMeta) orElse maybeContent.flatMap(fromContentTags(_, trailMeta))
    else None}

  def fromContentTags(content: Content, trailMeta: MetaDataCommonFields): Option[FaciaImage] = {
    val contributorTags = content.tags.filter(_.`type` == TagType.Contributor)
    if (contributorTags.length == 1)
      for {
        tag <- contributorTags.find(_.bylineLargeImageUrl.isDefined)
        path <- tag.bylineLargeImageUrl
      } yield Cutout(path, None, None)
    else None
  }

  def imageCutout(trailMeta: MetaDataCommonFields): Option[FaciaImage] = for {
    src <- trailMeta.imageCutoutSrc
    width <- trailMeta.imageCutoutSrcWidth
    height <- trailMeta.imageCutoutSrcHeight
  } yield Cutout(src, Option(width), Option(height))

  def imageReplace(trailMeta: MetaDataCommonFields, resolvedMetaData: ResolvedMetaData): Option[FaciaImage] = {
    trailMeta.imageSource match {
      case Some(imageSource) =>
        Some(Replace(imageSource.src, imageSource.width, imageSource.height, None))
      case None =>
        for {
          src <- trailMeta.imageSrc
          width <- trailMeta.imageSrcWidth
          height <- trailMeta.imageSrcHeight
        } yield Replace(src, width, height, None)
    }
  }

  def imageSlideshow(trailMeta: MetaDataCommonFields, resolvedMetaData: ResolvedMetaData): Option[FaciaImage] =
    trailMeta.slideshow.map { assets =>
      val slideshowAssets = assets.map(asset => Replace(asset.src, asset.width, asset.height, asset.caption))
      ImageSlideshow(slideshowAssets)}

}

sealed trait FaciaContent {
  def brandingByEdition: BrandingByEdition = Map.empty
}

// This needs to be kept aligned with Frontend until it's pushed all the way upstream to Thrift
// https://github.com/guardian/frontend/blob/46ac997bbb6482bacbd59c3528ce3141623c8033/common/app/model/meta.scala#L220-L224

final case class ContentFormat(
  design: Design,
  theme: Theme,
  display: Display,
)

object ContentFormat {
  lazy val defaultContentFormat: ContentFormat = {
    ContentFormat(ArticleDesign, NewsPillar, StandardDisplay)
  }
  def apply(content: Content): ContentFormat = {
    ContentFormat(content.design, content.theme, content.display)
  }
}

object Snap {
  val LatestType = "latest"
  val LinkType = "link"
  val DefaultType = LinkType

  def maybeFromTrail(trail: Trail): Option[Snap] = maybeFromTrailAndBrandings(trail, Map.empty)

  def maybeFromTrailAndBrandings(
    trail: Trail,
    brandingByEdition: BrandingByEdition
  ): Option[Snap] =
    trail.safeMeta.snapType match {
      case Some("latest") =>
        Option(LatestSnap.fromTrailAndContent(trail, None))
      case Some(snapType) =>
        val resolvedMetaData = ResolvedMetaData.fromTrailMetaData(trail.safeMeta)
        val contentProperties = ContentProperties.fromResolvedMetaData(resolvedMetaData)
        Option(LinkSnap(
          trail.id,
          Option(trail.frontPublicationDate),
          snapType,
          trail.safeMeta.snapUri,
          trail.safeMeta.snapCss,
          trail.safeMeta.atomId,
          trail.safeMeta.headline,
          trail.safeMeta.href,
          trail.safeMeta.trailText,
          trail.safeMeta.group.getOrElse("0"),
          FaciaImage.getFaciaImage(None, trail.safeMeta, resolvedMetaData),
          contentProperties,
          trail.safeMeta.byline,
          ItemKicker.fromTrailMetaData(trail.safeMeta),
          brandingByEdition
        ))
      case _ => None
    }

  def maybeFromSupportingItem(supportingItem: SupportingItem): Option[Snap] = supportingItem.safeMeta.snapType match {
    case Some("latest") =>
      Option(LatestSnap.fromSupportingItemAndContent(supportingItem, None))
    case Some(snapType) =>
      val resolvedMetaData = ResolvedMetaData.fromTrailMetaData(supportingItem.safeMeta)
      val contentProperties = ContentProperties.fromResolvedMetaData(resolvedMetaData)
      Option(LinkSnap(
        supportingItem.id,
        supportingItem.frontPublicationDate,
        snapType,
        supportingItem.safeMeta.snapUri,
        supportingItem.safeMeta.snapCss,
        supportingItem.safeMeta.atomId,
        supportingItem.safeMeta.headline,
        supportingItem.safeMeta.href,
        supportingItem.safeMeta.trailText,
        supportingItem.safeMeta.group.getOrElse("0"),
        FaciaImage.getFaciaImage(None, supportingItem.safeMeta, resolvedMetaData),
        contentProperties,
        supportingItem.safeMeta.byline,
        ItemKicker.fromTrailMetaData(supportingItem.safeMeta),
        Map.empty
      ))
    case _ => None
  }
}

sealed trait Snap extends FaciaContent
case class LinkSnap(
  id: String,
  maybeFrontPublicationDate: Option[Long],
  snapType: String,
  snapUri: Option[String],
  snapCss: Option[String],
  atomId: Option[String],
  headline: Option[String],
  href: Option[String],
  trailText: Option[String],
  group: String,
  image: Option[FaciaImage],
  properties: ContentProperties,
  byline: Option[String],
  kicker: Option[ItemKicker],
  override val brandingByEdition: BrandingByEdition
) extends Snap

case class LatestSnap(
  id: String,
  maybeFrontPublicationDate: Option[Long],
  cardStyle: CardStyle,
  format: ContentFormat,
  snapUri: Option[String],
  snapCss: Option[String],
  latestContent: Option[Content],
  headline: Option[String],
  href: Option[String],
  trailText: Option[String],
  group: String,
  image: Option[FaciaImage],
  properties: ContentProperties,
  byline: Option[String],
  kicker: Option[ItemKicker],
  override val brandingByEdition: BrandingByEdition
) extends Snap

object LatestSnap {
  def fromTrailAndContent(trail: Trail, maybeContent: Option[Content]): LatestSnap = {
    val cardStyle: CardStyle = maybeContent.map(CardStyle.apply(_, trail.safeMeta)).getOrElse(DefaultCardstyle)
    val contentFormat: ContentFormat = maybeContent.map(ContentFormat.apply).getOrElse(ContentFormat.defaultContentFormat)
    val resolvedMetaData: ResolvedMetaData =
      maybeContent.fold(ResolvedMetaData.fromTrailMetaData(trail.safeMeta))(ResolvedMetaData.fromContentAndTrailMetaData(_, trail.safeMeta, cardStyle))
    val brandingByEdition = maybeContent map (_.brandingByEdition) getOrElse Map.empty
    LatestSnap(
      trail.id,
      Option(trail.frontPublicationDate),
      cardStyle,
      contentFormat,
      trail.safeMeta.snapUri,
      trail.safeMeta.snapCss,
      maybeContent,
      trail.safeMeta.headline.orElse(maybeContent.flatMap(_.fields.flatMap(_.headline))).orElse(maybeContent.map(_.webTitle)),
      trail.safeMeta.href.orElse(maybeContent.map(_.id)),
      trail.safeMeta.trailText.orElse(maybeContent.flatMap(_.fields.flatMap(_.trailText))),
      trail.safeMeta.group.getOrElse("0"),
      FaciaImage.getFaciaImage(maybeContent, trail.safeMeta, resolvedMetaData),
      ContentProperties.fromResolvedMetaData(resolvedMetaData),
      trail.safeMeta.byline.orElse(maybeContent.flatMap(_.fields.flatMap(_.byline))),
      ItemKicker.fromMaybeContentTrailMetaAndResolvedMetaData(maybeContent, trail.safeMeta, resolvedMetaData),
      brandingByEdition
    )
  }

  def fromSupportingItemAndContent(supportingItem: SupportingItem, maybeContent: Option[Content]): LatestSnap = {
    val cardStyle: CardStyle = maybeContent.map(CardStyle.apply(_, supportingItem.safeMeta)).getOrElse(DefaultCardstyle)
    val contentFormat: ContentFormat = maybeContent.map(ContentFormat.apply).getOrElse(ContentFormat.defaultContentFormat)
    val resolvedMetaData: ResolvedMetaData =
      maybeContent.fold(ResolvedMetaData.fromTrailMetaData(supportingItem.safeMeta))(ResolvedMetaData.fromContentAndTrailMetaData(_, supportingItem.safeMeta, cardStyle))
    val brandingByEdition = maybeContent map (_.brandingByEdition) getOrElse Map.empty
    LatestSnap(
      supportingItem.id,
      supportingItem.frontPublicationDate,
      cardStyle,
      contentFormat,
      supportingItem.safeMeta.snapUri,
      supportingItem.safeMeta.snapCss,
      maybeContent,
      supportingItem.safeMeta.headline.orElse(maybeContent.flatMap(_.fields.flatMap(_.headline))).orElse(maybeContent.map(_.webTitle)),
      supportingItem.safeMeta.href,
      supportingItem.safeMeta.trailText.orElse(maybeContent.flatMap(_.fields.flatMap(_.trailText))),
      supportingItem.safeMeta.group.getOrElse("0"),
      FaciaImage.getFaciaImage(maybeContent, supportingItem.safeMeta, resolvedMetaData),
      ContentProperties.fromResolvedMetaData(resolvedMetaData),
      supportingItem.safeMeta.byline.orElse(maybeContent.flatMap(_.fields.flatMap(_.byline))),
      ItemKicker.fromMaybeContentTrailMetaAndResolvedMetaData(maybeContent, supportingItem.safeMeta, resolvedMetaData),
      brandingByEdition
    )
  }
}

case class CuratedContent(
  content: Content,
  maybeFrontPublicationDate: Option[Long],
  supportingContent: List[FaciaContent],
  cardStyle: CardStyle,
  format: ContentFormat,
  headline: String,
  href: Option[String],
  trailText: Option[String],
  group: String,
  image: Option[FaciaImage],
  properties: ContentProperties,
  byline: Option[String],
  kicker: Option[ItemKicker],
  embedType: Option[String],
  embedUri: Option[String],
  embedCss: Option[String],
  override val brandingByEdition: BrandingByEdition
) extends FaciaContent

case class SupportingCuratedContent(
  content: Content,
  maybeFrontPublicationDate: Option[Long],
  cardStyle: CardStyle,
  format: ContentFormat,
  headline: String,
  href: Option[String],
  trailText: Option[String],
  group: String,
  image: Option[FaciaImage],
  properties: ContentProperties,
  byline: Option[String],
  kicker: Option[ItemKicker]) extends FaciaContent

object CuratedContent {

  def fromTrailAndContentWithSupporting(content: Content,
                                        trailMetaData: TrailMetaData,
                                        maybeFrontPublicationDate: Option[Long],
                                        supportingContent: List[FaciaContent],
                                        collectionConfig: CollectionConfig) = {
    val cardStyle = CardStyle(content, trailMetaData)
    val resolvedMetaData = ResolvedMetaData.fromContentAndTrailMetaData(content, trailMetaData, cardStyle)

    CuratedContent(
      content,
      maybeFrontPublicationDate,
      supportingContent,
      cardStyle,
      ContentFormat(content),
      trailMetaData.headline.orElse(content.fields.flatMap(_.headline)).getOrElse(content.webTitle),
      trailMetaData.href,
      trailMetaData.trailText.orElse(content.fields.flatMap(_.trailText)),
      trailMetaData.group.getOrElse("0"),
      FaciaImage.getFaciaImage(Some(content), trailMetaData, resolvedMetaData),
      ContentProperties.fromResolvedMetaData(resolvedMetaData),
      trailMetaData.byline.orElse(content.fields.flatMap(_.byline)),
      ItemKicker.fromContentAndTrail(Option(content), trailMetaData, resolvedMetaData, Some(collectionConfig)),
      embedType = trailMetaData.snapType,
      embedUri = trailMetaData.snapUri,
      embedCss = trailMetaData.snapCss,
      brandingByEdition = content.brandingByEdition
    )
  }

  def fromTrailAndContent(content: Content,
                          trailMetaData: MetaDataCommonFields,
                          maybeFrontPublicationDate: Option[Long],
                          collectionConfig: CollectionConfig): CuratedContent = {

    val cardStyle = CardStyle(content, trailMetaData)
    val resolvedMetaData = ResolvedMetaData.fromContentAndTrailMetaData(content, trailMetaData, cardStyle)

    CuratedContent(
      content,
      maybeFrontPublicationDate,
      supportingContent = Nil,
      cardStyle = cardStyle,
      ContentFormat(content),

      trailMetaData.headline.orElse(content.fields.flatMap(_.headline)).getOrElse(content.webTitle),
      trailMetaData.href,
      trailMetaData.trailText.orElse(content.fields.flatMap(_.trailText)),
      trailMetaData.group.getOrElse("0"),
      FaciaImage.getFaciaImage(Some(content), trailMetaData, resolvedMetaData),
      ContentProperties.fromResolvedMetaData(resolvedMetaData),
      trailMetaData.byline.orElse(content.fields.flatMap(_.byline)),
      ItemKicker.fromContentAndTrail(Option(content), trailMetaData, resolvedMetaData, Some(collectionConfig)),
      embedType = trailMetaData.snapType,
      embedUri = trailMetaData.snapUri,
      embedCss = trailMetaData.snapCss,
      brandingByEdition = content.brandingByEdition
    )}
}

object SupportingCuratedContent {
  def fromTrailAndContent(content: Content,
                          trailMetaData: MetaDataCommonFields,
                          maybeFrontPublicationDate: Option[Long],
                          collectionConfig: CollectionConfig): SupportingCuratedContent = {
    val cardStyle = CardStyle(content, trailMetaData)
    val resolvedMetaData = ResolvedMetaData.fromContentAndTrailMetaData(content, trailMetaData, cardStyle)

    SupportingCuratedContent(
      content,
      maybeFrontPublicationDate,
      cardStyle,
      ContentFormat(content),
      trailMetaData.headline.orElse(content.fields.flatMap(_.headline)).getOrElse(content.webTitle),
      trailMetaData.href,
      trailMetaData.trailText.orElse(content.fields.flatMap(_.trailText)),
      trailMetaData.group.getOrElse("0"),
      FaciaImage.getFaciaImage(Some(content), trailMetaData, resolvedMetaData),
      ContentProperties.fromResolvedMetaData(resolvedMetaData),
      trailMetaData.byline.orElse(content.fields.flatMap(_.byline)),
      ItemKicker.fromContentAndTrail(Option(content), trailMetaData, resolvedMetaData, None)
    )
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy