/*
* Copyright 2011 Tomas Zeman <tzeman@volny.cz>
*
* Licensed 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.
*/
package radview.snippet
import net.liftweb.common._
import net.liftweb.http._
import net.liftweb.http.{RewriteRequest => RReq, RewriteResponse => RResp, ParsePath => PP}
import net.liftweb.mapper._
import net.liftweb.sitemap._
import net.liftweb.sitemap.Loc._
import net.liftweb.util._
import net.liftweb.util.Helpers._
import net.tz.lift.snippet._
import net.tz.lift.util._
import org.joda.time.DateTime
import radview.model._
import scala.xml.{NodeSeq, Text}
object AsAccountImsi {
def unapply(in: String): Option[RadCheckSub] = RadCheckSub.byImsi(in)
}
abstract sealed class AccountLoc
case object AccountSearch extends AccountLoc
case class ViewAccount(acc: RadCheckSub) extends AccountLoc
case class SyslogAccount(acc: RadCheckSub) extends AccountLoc
case class CdrAccount(acc: RadCheckSub) extends AccountLoc
object AsAccount {
def unapply(loc: Box[AccountLoc]): Option[RadCheckSub] = loc flatMap { _ match {
case SyslogAccount(a) => Some(a)
case ViewAccount(a) => Some(a)
case CdrAccount(a) => Some(a)
case _ => None
}}
}
object AccountSnippet extends Loc[AccountLoc] with SnippetHelpers {
val name = "account"
val prefix = "account"
val tpl = "account"
val params = List(Hidden)
val defaultValue = Full(AccountSearch)
val link = new Link[AccountLoc](List(prefix), true) {
override def createPath(l: AccountLoc): String = l match {
case AccountSearch => prefix
case ViewAccount(a) => mkPath(prefix, "imsi", a.imsi.is)
case SyslogAccount(a) => mkPath(prefix, "imsi", a.imsi.is, "syslog")
case CdrAccount(a) => mkPath(prefix, "imsi", a.imsi.is, "cdr")
}
}
def url(l: AccountLoc) = link.createPath(l)
val text = LinkText[AccountLoc](l => Text(l match {
case AccountSearch => "Account Search"
case ViewAccount(a) => "Account " + a.imsi
case SyslogAccount(a) => "Syslog search for account " + a.imsi
case CdrAccount(a) => "CDR search for account " + a.imsi
}))
override def rewrite: LocRewrite = Full({
case RReq(PP("account" :: Nil, _,_,_), _,_) =>
(new RResp(PP(List(tpl, "search"), "", true, false), Map.empty,
true), AccountSearch)
case RReq(PP("account" :: "imsi" :: AsAccountImsi(a) :: xs, _,_,_), _,_) =>
val l = xs match {
case "syslog" :: xs =>
ActionLinks.append(A(url(ViewAccount(a)), "Back to account"))
SyslogAccount(a)
case "cdr" :: xs =>
ActionLinks.append(A(url(ViewAccount(a)), "Back to account"))
CdrAccount(a)
case _ =>
ActionLinks.append(A(url(SyslogAccount(a)), "Syslog search"))
ActionLinks.append(A(url(CdrAccount(a)), "CDR search"))
ViewAccount(a)
}
(RResp(List(tpl, "view")), l)
})
override def snippets: SnippetTest = {
case ("form", Full(AccountSearch)) => searchForm
case ("list", Full(AccountSearch)) => searchList
case ("sub", AsAccount(a)) => AccountSubPanel(a)
case ("if-nai", AsAccount(a)) if !a.isAdsl => PassThru
case ("nai", AsAccount(a)) => AccountNaiPanel(a)
case ("syslog-list", Full(SyslogAccount(a))) => syslogList(a)
case ("form", Full(SyslogAccount(a))) => syslogForm(a)
case ("cdr-list", Full(CdrAccount(a))) => cdrList(a)
case ("form", Full(CdrAccount(a))) => cdrForm(a)
case ("form", _) => ClearNodes
case ("syslog-list", _) => ClearNodes
case ("cdr-list", _) => ClearNodes
case ("sub", _) => ClearNodes
case ("if-nai", _) => ClearNodes
case ("nai", _) => ClearNodes
}
/* Shared in all searches. */
object mxCnt extends RequestVar[Int](100)
/* Search account vars. */
object searchApp extends RequestVar[String]("BOSS")
object searchFld extends RequestVar[Int](0)
object searchVal extends RequestVar[String]("")
val fldsRCS = List(RadCheckSub.msisdn, RadCheckSub.imsi)
val fldsRRS = List(RadReplySub.statIp)
val fldsRRN = List(RadReplyNai.esn)
val searchFlds: List[MappedField[String,_]] = List(
RadCheckSub.msisdn, RadCheckSub.imsi,
RadReplySub.statIp,
RadReplyNai.esn
)
def searchForm: (NodeSeq => NodeSeq) = {
import AttrRow._
SimpleForm(List(
formRow(Text(RadCheckSub.app.displayName),
SHtml.select(RadCheckSub.getApps map(i => (i.app.is, i.app.tr)),
Full(searchApp.is), searchApp(_))),
formRow(SHtml.selectObj[Int](
searchFlds.zipWithIndex.map(i => (i._2, i._1.displayName)),
Full(searchFld.is), {i => searchFld(i)}),
SHtml.textElem(searchVal)
),
formRow(Text("Max. results"), SHtml.selectObj[Int](
List(100, 200, 500) map { i => (i, i.toString) },
Full(mxCnt.is), mxCnt(_)))
), "Search")
}
def searchList: (NodeSeq => NodeSeq) = {
val rowsB = searchVal.is match {
case "" | null => Empty
case v =>
val mx = mxCnt.is
val fld = searchFlds(searchFld.is)
val n = fld.name
fld.fieldOwner match {
case o: RadCheckSub =>
o.fieldByName(n) map { f: MappedField[String, RadCheckSub] =>
RadCheckSub.findAll(Like(f, "%" + v + "%"),
By(RadCheckSub.app, searchApp.is),
OrderBy(RadCheckSub.imsi, Ascending), MaxRows(mx))
}
case o: RadReplySub =>
o.fieldByName(n) map { f: MappedField[String, RadReplySub] =>
val imsis = RadReplySub.findAllFields(List(RadReplySub.imsi),
Like(f, "%" + v + "%"),
By(RadReplySub.app, searchApp.is)) map (_.imsi.is)
RadCheckSub.findAll(ByList(RadCheckSub.imsi, imsis),
OrderBy(RadCheckSub.imsi, Ascending), MaxRows(mx))
}
case o: RadReplyNai =>
o.fieldByName(n) map { f: MappedField[String, RadReplyNai] =>
val imsis = RadReplyNai.findAllFields(List(RadReplyNai.value),
Like(f, "%" + v + "%"),
By(RadReplyNai.app, searchApp.is)) map (_.value.imsi)
RadCheckSub.findAll(ByList(RadCheckSub.imsi12, imsis),
OrderBy(RadCheckSub.imsi, Ascending), MaxRows(mx))
}
}
}
rowsB map { rcs =>
val rrs = RadReplySub.byImsiMap(rcs map {_.imsi.is})
val rrn = RadReplyNai.byImsi12map(rcs map {_.imsi12.is})
val rcn = RadCheckNai.byRrnMap(rrn.values.toSeq)
AccountTable(rcs, rrs, rrn, rcn)
} openOr ClearNodes
}
/* Vars common to syslog + cdr search */
object from extends RequestVar[DateTime]((new DateTime).minusDays(1))
object to extends RequestVar[DateTime](new DateTime)
object fromD extends RequestVar[String](AsDateMidnight(from))
object fromT extends RequestVar[String](AsTimePeriod(from))
object toD extends RequestVar[String](AsDateMidnight(to))
object toT extends RequestVar[String](AsTimePeriod(to))
private def setFromToDateTimes() = {
import AsDateMidnight._
AsDateMidnight(fromD) map { from(_) }
AsTimePeriod(fromT) map { p => from(from.plus(p)) }
AsDateMidnight(toD) map { to(_) }
AsTimePeriod(toT) map { p => to(to.plus(p)) }
()
}
/* Shared between syslogForm + syslogList */
object syslogQp extends RequestVar[Box[List[QueryParam[Syslog]]]](Empty)
def syslogForm(a: RadCheckSub): (NodeSeq => NodeSeq) = {
object byQp extends RequestVar[Box[QueryParam[Syslog]]](Empty)
val selectQp = List[(QueryParam[Syslog], String)](
(By(Syslog.imsi, asLong(a.imsi).openOr(0L)), a.imsi.displayName)) ++
a.replyNai.map { n => (By(Syslog.username, n.esn.esn), "ESN") }
def setD() = {
setFromToDateTimes()
import AsDateMidnight.dt2d
val qp = List[QueryParam[Syslog]](
By_<(Syslog.ts, to.plusMinutes(1)),
By_>(Syslog.ts, from.minusMinutes(1)),
OrderBy(Syslog.ts, Ascending)
) ++ byQp.is
syslogQp(Full(qp))
}
import AttrRow._
SimpleForm(List(
formRow(Text("Search by"), SHtml.selectObj[QueryParam[Syslog]](selectQp,
byQp, { qp => byQp(Full(qp)) } )),
formRow(Text("From"), SHtml.textElem(fromD, ("class", "date")) ++
SHtml.textElem(fromT)),
formRow(Text("To"), SHtml.textElem(toD, ("class", "date")) ++
SHtml.textElem(toT)),
formRow(Text("Max. results"), SHtml.selectObj[Int](
List(100, 200, 500) map { i => (i, i.toString) },
Full(mxCnt.is), mxCnt(_)))
), "Search", setD)
}
def syslogList(a: RadCheckSub): (NodeSeq => NodeSeq) = syslogQp map { qp =>
".num-rows-panel" #> Panel(List(
("Records found", Syslog.count(qp:_*)),
("Number of successful logins",
Syslog.count((qp:+By(Syslog.loginStatus, 1)):_*)),
("Number of failed logins",
Syslog.count((qp:+By(Syslog.loginStatus, 0)):_*))
) map { r => new AttrRow(Text(r._1), Text(r._2.toString),
"attr-name-wide", "attr-value") }) &
".list" #> SyslogTable(Syslog.findAll((qp:+MaxRows(mxCnt.is) ):_*),
qp.find { _ match {
case Cmp(f, _, _, _, _) if f.name == Syslog.imsi.name => true
case _ => false
}}.isDefined)
} openOr ClearNodes
/* Shared between cdrForm and cdrList. */
object cdrQp extends RequestVar[Box[List[QueryParam[Cdr]]]](Empty)
object byCdrQp extends RequestVar[Box[QueryParam[Cdr]]](Empty)
def cdrForm(a: RadCheckSub): (NodeSeq => NodeSeq) = {
val selectQp = List[(QueryParam[Cdr], String)](
(By(Cdr.callingNo, asLong(a.imsi).openOr(0L)), a.imsi.displayName)) ++
a.replySub.map { r => (By(Cdr.framedIp, r.statIp.is),
r.statIp.displayName) }
def setD() = {
setFromToDateTimes()
import AsDateMidnight.dt2d
val qp = List[QueryParam[Cdr]](
By_<(Cdr.ts, to.plusMinutes(1)),
By_>(Cdr.ts, from.minusMinutes(1)),
OrderBy(Cdr.ts, Ascending)
) ++ byCdrQp.is
cdrQp(Full(qp))
}
import AttrRow._
SimpleForm(List(
formRow(Text("Search by"), SHtml.selectObj[QueryParam[Cdr]](selectQp,
byCdrQp, { qp => byCdrQp(Full(qp)) } )),
formRow(Text("From"), SHtml.textElem(fromD, ("class", "date")) ++
SHtml.textElem(fromT)),
formRow(Text("To"), SHtml.textElem(toD, ("class", "date")) ++
SHtml.textElem(toT)),
formRow(Text("Max. results"), SHtml.selectObj[Int](
List(100, 200, 500, 1000, 2000) map { i => (i, i.toString) },
Full(mxCnt.is), mxCnt(_)))
), "Search", setD)
}
def cdrList(a: RadCheckSub): (NodeSeq => NodeSeq) = cdrQp map { qp =>
import AsDateMidnight.dt2d
val sess = byCdrQp map { _ match {
case Cmp(f, OprEnum.Eql, Full(v), _, _) =>
Cdr.sessions(from.is, to.is, f, v.toString)
case _ => Nil
}} openOr Nil
".num-rows-panel" #> Panel(List(
("Records found", Cdr.count(qp:_*)),
("No. of sessions", sess.length),
("Total bytes in/out [MB]",
Bytes.mb(sess map { _.inBytes.is } sum) ++ Text(" / ") ++
Bytes.mb(sess map { _.outBytes.is } sum))
) map { r => new AttrRow(Text(r._1), Text(r._2.toString),
"attr-name-wide", "attr-value") } ) &
".list" #> CdrTable(Cdr.findAll( (qp:+MaxRows(mxCnt.is) ):_*), a.isAdsl)
} openOr ClearNodes
}
object AccountTable {
def apply(rcs: Seq[RadCheckSub], rrs: Map[String, RadReplySub],
rrn: Map[String, RadReplyNai], rcn: Map[String, RadCheckNai]) = {
val tpl = rcs.headOption.getOrElse(RadCheckSub)
def valF[T<:Mapper[T]](f: MappedField[_, T])(i: T): NodeSeq =
f.actualField(i).asHtml
val imsiCol = Column[RadCheckSub](tpl.imsi.displayName, {
i: RadCheckSub => A(AccountSnippet.url(ViewAccount(i)), i.imsi.is)
})
val colsRcs = tpl.fieldsForList map { f =>
Column[RadCheckSub](f.displayName, valF(f) _)
}
val colsRrs = RadReplySub.fieldsForList map { f =>
Column[RadCheckSub](f.displayName, { i: RadCheckSub =>
rrs.get(i.imsi.is).map { r =>
f.actualField(r).asHtml } getOrElse NodeSeq.Empty })
}
val colsRrn = RadReplyNai.fieldsForList map { f =>
Column[RadCheckSub](f.displayName, { i: RadCheckSub =>
rrn.get(i.imsi12.is).map { r =>
f.actualField(r).asHtml } getOrElse NodeSeq.Empty })
}
val colsRcn = RadCheckNai.fieldsForList map { f =>
Column[RadCheckSub](f.displayName, { i: RadCheckSub =>
val o: Option[NodeSeq] = for {
r <- rrn.get(i.imsi12.is)
n <- rcn.get(r.esn.is)
} yield {
f.actualField(n).asHtml
}
o getOrElse NodeSeq.Empty
})
}
Table[RadCheckSub](List(imsiCol) ++ colsRcs ++ colsRrs ++
tpl.ifAdsl(Nil, colsRrn ++ colsRcn), rcs)
}
}
object AccountSubPanel {
def apply(a: RadCheckSub): (NodeSeq => NodeSeq) = {
val r = a.replySub.openOr(RadReplySub.create)
Panel(a.fieldsForDisplay.map(AttrRow(_)) ++
r.fieldsForDisplay.map(AttrRow(_)))
}
}
object AccountNaiPanel {
def apply(a: RadCheckSub): (NodeSeq => NodeSeq) = {
val r = a.replyNai.openOr(RadReplyNai.create)
val ch = a.checkNai.openOr(RadCheckNai.create)
Panel(ch.fieldsForDisplay.map(AttrRow(_)) ++
r.fieldsForDisplay.map(AttrRow(_)))
}
}
object SyslogTable {
def apply(rows: Iterable[Syslog], includeImsi: Boolean) = {
val imsiCol: List[MappedField[_, Syslog]] = includeImsi match {
case true => List(Syslog.imsi)
case false => Nil
}
Table[Syslog]((Syslog.fieldsForList ++ imsiCol).map { f =>
Column[Syslog](f.displayName, { i: Syslog => f.actualField(i).asHtml })
}, rows)
}
}
// vim: set ts=2 sw=2 et: