Author: vdichev
Date: Sun Aug 9 20:52:17 2009
New Revision: 802595
URL: http://svn.apache.org/viewvc?rev=802595&view=rev
Log:
ESME-81 Snippet for popular resent messages.
Added:
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/PopStatsActor.scala
(with props)
Modified:
incubator/esme/trunk/server/src/main/scala/bootstrap/liftweb/Boot.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/UserActor.scala
incubator/esme/trunk/server/src/main/scala/org/apache/esme/snippet/UserSnip.scala
incubator/esme/trunk/server/src/main/webapp/style/b-content.css
incubator/esme/trunk/server/src/main/webapp/templates-hidden/message.html
Modified:
incubator/esme/trunk/server/src/main/scala/bootstrap/liftweb/Boot.scala
URL:
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/bootstrap/liftweb/Boot.scala?rev=802595&r1=802594&r2=802595&view=diff
==============================================================================
--- incubator/esme/trunk/server/src/main/scala/bootstrap/liftweb/Boot.scala
(original)
+++ incubator/esme/trunk/server/src/main/scala/bootstrap/liftweb/Boot.scala Sun
Aug 9 20:52:17 2009
@@ -27,6 +27,8 @@
import net.liftweb.sitemap._
import net.liftweb.sitemap.Loc._
import Helpers._
+import TimeHelpers.intToTimeSpanBuilder
+import TimeHelpers.timeSpanToLong
import net.liftweb.mapper.{DB, ConnectionManager, Schemifier,
DefaultConnectionIdentifier, ConnectionIdentifier}
import java.sql.{Connection, DriverManager}
import org.apache.esme._
@@ -144,6 +146,8 @@
SchedulerActor.touch
MessagePullActor.touch
ScalaInterpreter.touch
+
+ PopStatsActor ! PopStatsActor.StartStats(ResendStat, 1 week, 1 hour)
Action.findAll(By(Action.disabled, false), By(Action.removed,
false)).foreach {
_.startActors
Added:
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/PopStatsActor.scala
URL:
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/PopStatsActor.scala?rev=802595&view=auto
==============================================================================
---
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/PopStatsActor.scala
(added)
+++
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/PopStatsActor.scala
Sun Aug 9 20:52:17 2009
@@ -0,0 +1,133 @@
+package org.apache.esme.actor
+
+/**
+ * Copyright 2008-2009 WorldWide Conferencing, LLC
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import scala.actors.Actor
+import scala.actors.TIMEOUT
+import scala.actors.Actor._
+import org.apache.esme.actor.Distributor.AddMessageToMailbox
+import org.apache.esme.model._
+import net.liftweb.http.ActorWatcher
+import net.liftweb.util.{Full,Empty,TimeHelpers}
+
+class PopStatsActor(val period: Long, val refreshInterval: Long) extends Actor
{
+
+ import PopStatsActor._
+
+ var queue: List[StatEvent] = List()
+ var stats: Map[Long,Int] = Map()
+ var refreshActor: Actor = _
+
+ def act {
+ loop {
+ react {
+ case StartUp =>
+ refreshActor = actor {
+ loop {
+ self.reactWithin(refreshInterval) {
+ case TIMEOUT => this ! Expire
+ case ByeBye => self.exit()
+ }
+ }
+ }
+ case ByeBye =>
+ refreshActor ! ByeBye
+ self.exit()
+ case Hit(id) =>
+ queue += StatEvent(id, now)
+ stats += (id -> (stats.getOrElse(id,0) + 1))
+ case Top(n) =>
+ val topList = stats.toList.sort{ (t1, t2) =>
+ val ((_,freq1),(_,freq2)) = (t1, t2)
+ freq2 < freq1
+ }.take(n)
+ reply(topList)
+ case Expire => {
+ queue = queue.dropWhile{ event =>
+ val expired_? = event.when < now
+ if (expired_?) stats -= event.id
+ expired_?
+ }.toList
+ }
+ }
+ }
+ }
+
+ case class StatEvent(id: Long, when: Long)
+}
+
+object PopStatsActor extends Actor {
+
+ // immutable maps of maps doesn't work well
+ import scala.collection.mutable.Map
+
+ val actors: Map[StatParam,Map[Long,PopStatsActor]] = Map()
+
+ def now = System.currentTimeMillis
+ def act = loop {
+ react {
+ case StartStats(what, period, refresh) =>
+ if (!actors.contains(what))
+ actors(what) = Map()
+ val stat = actors(what)
+ if(!stat.contains(period)) {
+ val statActor = new PopStatsActor(period, refresh)
+ stat(period) = statActor
+ statActor.start
+ statActor ! StartUp
+ }
+
+ case StopStats(what, period) => // TODO: not used
+ case TopStats(what, n, period) =>
+ (for (stat <- actors.get(what);
+ availableActor <- stat.get(period)) yield availableActor) match {
+ case Some(statActor) => statActor forward Top(n)
+ case _ => reply(Nil)
+ }
+ case IncrStats(what, hitItem) =>
+ for (stat <- actors.get(what);
+ statActor <- stat.values)
+ statActor ! Hit(hitItem)
+ }
+ }
+
+ start
+
+ // do nothing
+ def touch {
+ }
+
+ case object StartUp
+ case object ByeBye
+ case class Top(n: Int)
+ case class Hit(id: Long)
+ case object Expire
+
+ case class StartStats(what: StatParam, period: Long, refresh: Long)
+ case class StopStats(what: StatParam, period: Long)
+ case class TopStats(what: StatParam, n: Byte, period: Long)
+ case class IncrStats(what: StatParam, hitItem: Long)
+}
+
+sealed trait StatParam
+case object ResendStat extends StatParam
+case object LinkClickedStat extends StatParam
Propchange:
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/PopStatsActor.scala
------------------------------------------------------------------------------
svn:executable = *
Modified:
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/UserActor.scala
URL:
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/UserActor.scala?rev=802595&r1=802594&r2=802595&view=diff
==============================================================================
---
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/UserActor.scala
(original)
+++
incubator/esme/trunk/server/src/main/scala/org/apache/esme/actor/UserActor.scala
Sun Aug 9 20:52:17 2009
@@ -192,11 +192,13 @@
case AllowPool(poolId) => pools += poolId
case Resend(msgId) =>
- for (msg <- Message.find(msgId);
- id <- followers)
- Distributor !
- Distributor.AddMessageToMailbox(id, msg, ResendReason(userId))
-
+ for (msg <- Message.find(msgId)) {
+ if (!msg.pool.defined_?)
+ PopStatsActor ! PopStatsActor.IncrStats(ResendStat, msgId)
+ for (id <- followers)
+ Distributor !
+ Distributor.AddMessageToMailbox(id, msg, ResendReason(userId))
+ }
}
}
Modified:
incubator/esme/trunk/server/src/main/scala/org/apache/esme/snippet/UserSnip.scala
URL:
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/scala/org/apache/esme/snippet/UserSnip.scala?rev=802595&r1=802594&r2=802595&view=diff
==============================================================================
---
incubator/esme/trunk/server/src/main/scala/org/apache/esme/snippet/UserSnip.scala
(original)
+++
incubator/esme/trunk/server/src/main/scala/org/apache/esme/snippet/UserSnip.scala
Sun Aug 9 20:52:17 2009
@@ -32,6 +32,8 @@
import JE._
import util._
import Helpers._
+import TimeHelpers.intToTimeSpanBuilder
+import TimeHelpers.timeSpanToLong
import scala.xml.{NodeSeq, Text, Node}
@@ -87,7 +89,8 @@
"loginForm" -> loginForm _,
"loggedIn" -> loggedInFilter _,
"accessPools" -> accessPools _,
- "resendScript" -> resendScript _)
+ "resendScript" -> resendScript _,
+ "popular" -> popular _)
def loggedInFilter(in: NodeSeq): NodeSeq = {
val lookFor = if (User.loggedIn_?) "in" else "out"
@@ -165,4 +168,33 @@
}
</xml:group>
+ def popular(in: NodeSeq): NodeSeq =
+ <xml:group>
+ {PopStatsActor !? PopStatsActor.TopStats(ResendStat, 5, 1 week) match {
+ case l: List[Tuple2[Long,Int]] =>
+ <table>
+ <thead>
+ <tr> <th>Resent</th> <th>Message</th> </tr>
+ </thead>
+ <tbody>
+ {
+ val msgMap = Message.findMessages(l.map(_._1))
+ l.map{ stat =>
+ val (msgId, freq) = stat
+ (for (m <- msgMap.get(msgId)) yield {
+ <tr>
+ <td>{freq}</td>
+ <td>{m.author.obj.map(_.nickname.is).openOr("")}:
+ {m.digestedXHTML}
+ <!--{new
java.util.Date(m.when.toLong).toString}--></td>
+ </tr>
+ }).getOrElse(<br/>)
+ }
+ }
+ </tbody>
+ </table>
+ case _ => <br/>
+ }
+ }
+ </xml:group>
}
Modified: incubator/esme/trunk/server/src/main/webapp/style/b-content.css
URL:
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/webapp/style/b-content.css?rev=802595&r1=802594&r2=802595&view=diff
==============================================================================
--- incubator/esme/trunk/server/src/main/webapp/style/b-content.css (original)
+++ incubator/esme/trunk/server/src/main/webapp/style/b-content.css Sun Aug 9
20:52:17 2009
@@ -1,5 +1,5 @@
/* Content page (begin) */ /**/
-.b-cloud {
+.b-cloud, .b-stats {
margin-top: 30px;
/* background: #233e5e; /* url(../images/sh-r.png) repeat-y 100% 0; */
text-align: center;
@@ -8,7 +8,7 @@
border: 1px solid black;
}
-#tag-para, #word-para {
+#tag-para, #word-para, #stats-para {
font-size: 1.7em;
text-decoration: underline;
font-style: italic;
Modified:
incubator/esme/trunk/server/src/main/webapp/templates-hidden/message.html
URL:
http://svn.apache.org/viewvc/incubator/esme/trunk/server/src/main/webapp/templates-hidden/message.html?rev=802595&r1=802594&r2=802595&view=diff
==============================================================================
--- incubator/esme/trunk/server/src/main/webapp/templates-hidden/message.html
(original)
+++ incubator/esme/trunk/server/src/main/webapp/templates-hidden/message.html
Sun Aug 9 20:52:17 2009
@@ -308,6 +308,11 @@
<div class="b-cloud">
<lift:comet type="TagCloud"/>
</div>
+
+ <div class="b-stats">
+ <p id="stats-para">Popular messages</p>
+ <lift:UserSnip.popular/>
+ </div>
<lift:ignore>
<div class="bugs">