Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add lead content tracking

  • Loading branch information...
commit e7001c98fcf693da0d133653114401ca68aa1bf8 1 parent 2821509
@tackley authored
View
6 app/controllers/Application.scala
@@ -38,7 +38,8 @@ object Application extends Controller {
c.safeFields.get("trailText"),
c.tags,
currentHits.get(c.webUrl),
- altTextOfMainImageFor(c)
+ altTextOfMainImageFor(c),
+ c.isLead.getOrElse(false)
)
}
}
@@ -63,7 +64,8 @@ case class PublishedContent(
trailText: Option[String],
tags: List[Tag],
hitReport: Option[HitReport],
- altText: Option[String]
+ altText: Option[String],
+ isLead: Boolean
) {
lazy val cpsCssClass = hitsPerSec match {
case "0" => "zero"
View
2  app/lib/Backend.scala
@@ -24,7 +24,7 @@ object Backend {
def start() {
system.scheduler.schedule(1 minute, 1 minute, listener, ClickStreamActor.TruncateClickStream)
system.scheduler.schedule(5 seconds, 5 seconds, listener, ClickStreamActor.SendClickStreamTo(calculator))
- system.scheduler.schedule(5 seconds, 15 seconds) { latestContent.refresh() }
+ system.scheduler.schedule(5 seconds, 30 seconds) { latestContent.refresh() }
spawn {
mqReader.start()
View
86 app/lib/LatestContent.scala
@@ -6,12 +6,26 @@ import org.joda.time.DateTime
import com.gu.openplatform.contentapi.Api
import akka.actor.ActorSystem
import akka.event.Logging
+import play.api.libs.ws.WS
+import play.api.libs.concurrent.Promise
+
+case class LiveDashboardContent(
+ content: Content,
+ isLead: Option[Boolean] = None
+)
+
+object LiveDashboardContent {
+ // this is a temporary hack :)
+ implicit def ourContentToApiContent(c: LiveDashboardContent): Content = c.content
+}
+
class LatestContent(implicit sys: ActorSystem) {
- Api.apiKey = Some("d7bd4fkrbgkmaehrfjsbcetu")
+ val apiKey = "d7bd4fkrbgkmaehrfjsbcetu"
+ Api.apiKey = Some(apiKey)
private val log = Logging(sys, this.getClass)
- val latest = Agent[List[Content]](Nil)
+ val latest = Agent[List[LiveDashboardContent]](Nil)
val editorialSections = "artanddesign | books | business | childrens-books-site | commentisfree | " +
"crosswords | culture | education | environment | fashion | film | football | theguardian | " +
@@ -19,8 +33,8 @@ class LatestContent(implicit sys: ActorSystem) {
"politics | science | society | sport | stage | technology | tv-and-radio | travel | uk | world";
def refresh() {
- // "sendOff" means this may be a slow operation, so
- // perform it not in one of the normal actor processing threads
+ // "sendOff" means this may be a slow operation, so don't
+ // perform it in one of the normal actor processing threads
latest sendOff { content =>
val lastDateTime = content.headOption.map(_.webPublicationDate) getOrElse (new DateTime().minusHours(4))
@@ -31,11 +45,15 @@ class LatestContent(implicit sys: ActorSystem) {
.orderBy("oldest").showFields("trailText")
.showMedia("picture")
.section(editorialSections)
- .pageSize(50).results.reverse
+ .pageSize(50).results
+ .reverse
// because of the way we handle dates we will always get at least one item of content repeated
// so remove stuff we've already got from the api list
- val newContent = apiNewContent.filterNot(c => content.exists(_.id == c.id))
+ val newContent = apiNewContent.filterNot(c => content.exists(_.id == c.id)).map(LiveDashboardContent(_))
+
+ log.info("New content size is " + newContent.size)
+ if (!newContent.isEmpty) latest.sendOff(findLeadContent _)
val result = newContent ::: content.filter(_.webPublicationDate.plusHours(4).isAfterNow)
@@ -44,4 +62,60 @@ class LatestContent(implicit sys: ActorSystem) {
result
}
}
+
+ private def findLeadContent(contentList: List[LiveDashboardContent]): List[LiveDashboardContent] = {
+ def leadContentForTag(tagId: String): Promise[Seq[String]] = {
+ WS.url("http://content.guardianapis.com/%s.json" format tagId)
+ .withQueryString("api-key" -> apiKey)
+ .get()
+ .map { r => if (r.body.startsWith("<")) { log.info("*** tagId = " + tagId + " *** " + r.body) }; r }
+ .map { r => (r.json \ "response" \ "leadContent" \\ "id").map(_.as[String]) }
+ }
+
+ val contentMissingLeadStatus = contentList.filter(_.isLead.isEmpty)
+ val leadSections = contentMissingLeadStatus.flatMap(_.sectionId).sorted.distinct
+
+ log.info("Need to find lead content status for " + leadSections)
+
+ val leadItemsPromise = for {
+ section <- leadSections
+ } yield {
+ val sectionTag = section + "/" + section
+ section -> leadContentForTag(sectionTag)
+ }
+
+ log.info("leadItemsPromise = " + leadItemsPromise)
+
+ // now redeem those promises
+ val leadItems = leadItemsPromise.flatMap {
+ case (section, promise) =>
+ promise.await.fold(
+ onError = { t =>
+ log.error(t, "error getting info for section %s" format section)
+ Nil
+ },
+ onSuccess = { r => List(section -> r) }
+ )
+ }.toMap
+
+ log.info("leadItems = " + leadItems)
+
+ val result = contentList.map {
+ case c if c.isLead.isDefined => c
+ case c if c.sectionId.isEmpty => c.copy(isLead = Some(false))
+ case c =>
+ val section = c.sectionId.get
+ val leadList = leadItems.get(section).getOrElse(Nil)
+
+ val isLead = leadList contains c.id
+
+ log.info("%s (%s) -> isLead = %s" format (c.id, section, isLead))
+ log.info("available lead content for this section: %s" format leadList.mkString("\t\n"))
+ c.copy(isLead = Some(isLead))
+
+ }
+
+
+ result
+ }
}
View
11 app/views/snippets/contentChart.scala.html
@@ -5,12 +5,21 @@
@for(c <- content) {
<tr class="@c.rowCssClass">
<td class="pub-date">@c.publicationDate.toString("d MMM HH:mm:ss")</td>
- <td class="cps"><span class="label percent-cps @c.cpsCssClass">@c.hitsPerSec</span></td>
+ <td class="cps">
+ <span class="label percent-cps @c.cpsCssClass" title="Average hits per second over the last 15 minutes">
+ @c.hitsPerSec
+ </span>
+ </td>
<td class="front-referral-status" title="@c.networkFrontTooltip">
@if(c.hasNetworkFrontReferrer) {
NF
}
</td>
+ <td class="lead-status">
+ @if(c.isLead) {
+ LEAD
+ }
+ </td>
<td>
@Html(c.section) <a href="@c.url" target="_blank">@Html(c.title)</a>
<div class="trail-text">@Html(c.trailText getOrElse "")</div>
View
17 test/HackingTest.scala
@@ -2,18 +2,21 @@ import akka.actor.ActorSystem
import org.specs2.matcher.EventuallyMatchers
import org.specs2.mutable.Specification
import akka.agent._
+import play.api.libs.ws.WS
class HackingTest extends Specification with EventuallyMatchers {
- "agents" should {
- "do what I think" in {
- implicit val s = ActorSystem("test")
+ "api call" should {
+ "work" in {
+ val p = WS.url("http://content.guardianapis.com/uk/uk.json").get().map { r =>
+ (r.json \ "response" \ "leadContent" \\ "id").map(_.as[String])
+ }
+
+ val theVal = p.await
- val agent = Agent(17)
+ println(theVal)
- agent send 6
-
- agent() must eventually(beEqualTo(6))
+ 1 + 1 must_==(3)
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.