ca1737768512bd36 Search by dynamic IP address, IP assignment history list
authorTomas Zeman <tzeman@volny.cz>
Wed, 30 Nov 2011 16:59:10 +0100
changeset 22 6b1a7f3429ca
parent 21 0f6859991f55
child 23 471fde4de798
ca1737768512bd36 Search by dynamic IP address, IP assignment history list
src/main/scala/bootstrap/liftweb/Boot.scala
src/main/scala/net/tz/lift/snippet/SnippetHelpers.scala
src/main/scala/radview/model/DynIp.scala
src/main/scala/radview/snippet/AccountSnippet.scala
src/main/scala/radview/snippet/DynIpSnippet.scala
src/main/webapp/account/view.html
src/main/webapp/dyn-ip.html
--- a/src/main/scala/bootstrap/liftweb/Boot.scala	Thu Apr 14 15:42:46 2011 +0200
+++ b/src/main/scala/bootstrap/liftweb/Boot.scala	Wed Nov 30 16:59:10 2011 +0100
@@ -17,11 +17,14 @@
 
 import net.liftweb.common._
 import net.liftweb.http._
+import net.liftweb.http.js.JsObj
+import net.liftweb.http.js.JE.JsObj
 import net.liftweb.mapper._
 import net.liftweb.sitemap._
 import net.liftweb.sitemap.Loc._
+import net.liftweb.widgets.menu.MenuStyle
+import net.liftweb.widgets.menu.{MenuWidget => LiftMenuWidget}
 import net.liftweb.util._
-import net.liftweb.widgets.menu.MenuWidget
 import net.tz.lift.snippet.ActionLinks
 import net.tz.lift.util.YmdDateTimeConverter
 import radview.model._
@@ -57,7 +60,9 @@
     LiftRules.snippetDispatch.append {
       case "Menubar" => new AnyRef with DispatchSnippet {
         def dispatch: DispatchIt = {
-          case _ => { xhtml => MenuWidget() }
+          case _ => { xhtml =>
+            new MenuWidget(MenuStyle.HORIZONTAL, JsObj(), Nil) render
+          }
         }
       }
       case "action-links" => ActionLinks
@@ -72,11 +77,12 @@
       Menu.i("Cells") / "cell",
       Menu(SessionSnippet),
       Menu(AccountSnippet),
-      Menu.i("Accounts") / "account"
+      Menu.i("Accounts") / "account",
+      DynIpSnippet.menu
     ))
 
     /* Menu widget */
-    MenuWidget.init()
+    LiftMenuWidget.init()
 
     /* Http conf */
     LiftRules.logServiceRequestTiming = false
@@ -94,4 +100,18 @@
   }
 }
 
+class MenuWidget(style: MenuStyle.Value, jsObj: JsObj, groups : List[String])
+  extends LiftMenuWidget(style, jsObj, groups) {
+
+  override def render: NodeSeq = { 
+    head ++ <div>{groups match {
+      case Nil =>
+        <lift:Menu.builder expandAll="true" linkToSelf="true" top:class={style.toString} />
+      case xs => groups.flatMap(group =>
+        <lift:Menu.builder expandAll="true" linkToSelf="true" top:class={style.toString} group={group} />)
+    }}</div>
+  }
+}
+
+
 // vim: set ts=2 sw=2 et:
--- a/src/main/scala/net/tz/lift/snippet/SnippetHelpers.scala	Thu Apr 14 15:42:46 2011 +0200
+++ b/src/main/scala/net/tz/lift/snippet/SnippetHelpers.scala	Wed Nov 30 16:59:10 2011 +0100
@@ -21,6 +21,8 @@
 trait SnippetHelpers {
   def mkPath(prefix: String, l: String*) =
     (prefix :: l.toList) mkString ("/", "/", "")
+
+  type CssTr = (NodeSeq => NodeSeq)
 }
 
 class A(href: => String) extends Function1[NodeSeq, NodeSeq] {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/radview/model/DynIp.scala	Wed Nov 30 16:59:10 2011 +0100
@@ -0,0 +1,72 @@
+/*
+ * 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.model
+
+import java.sql.Types
+import net.liftweb.common._
+import net.liftweb.mapper._
+import org.joda.time.DateMidnight
+import radview.snippet.SessionSnippet
+import scala.xml.Text
+
+object DynIp extends DynIp with MetaMapper[DynIp] {
+  override def dbTableName = "radacct"
+  override def dbDefaultConnectionIdentifier = RadAcctConnectionIdentifier
+
+  def byImsi(i: String, from: DateMidnight, to: DateMidnight) =
+  findAllByPreparedStatement { c =>
+    val sql = "SELECT MIN(%s) AS %s, %s, %s FROM %s" +
+      " WHERE %s = ? AND %s BETWEEN ? AND ? GROUP BY %s ORDER BY %s"
+    val pars = ((List(ts, ts, framedIp, callingNo) map (_.dbColumnName)) :+
+      dbTableName) ::: (List(callingNo, ts, framedIp, ts) map (_.dbColumnName))
+    val st = c.connection.prepareStatement(sql.format(pars :_*))
+    st.setString(1, i)
+    st.setObject(2, from.toDate, Types.DATE)
+    st.setObject(3, to.toDate, Types.DATE)
+    st
+  }
+
+  def byIp(i: String, from: DateMidnight, to: DateMidnight) =
+  findAllByPreparedStatement { c =>
+    val sql = "SELECT MIN(%s) AS %s, %s, %s FROM %s" +
+      " WHERE %s = ? AND %s BETWEEN ? AND ? GROUP BY %s ORDER BY %s"
+    val pars = ((List(ts, ts, framedIp, callingNo) map (_.dbColumnName)) :+
+      dbTableName) ::: (List(framedIp, ts, callingNo, ts) map (_.dbColumnName))
+    val st = c.connection.prepareStatement(sql.format(pars :_*))
+    st.setString(1, i)
+    st.setObject(2, from.toDate, Types.DATE)
+    st.setObject(3, to.toDate, Types.DATE)
+    st
+  }
+}
+
+class DynIp extends Mapper[DynIp] {
+  def getSingleton = DynIp
+
+  object framedIp extends MappedPoliteStringColName(this, 15,
+    "framedipaddress", "Framed IP")
+
+  object ts extends MappedDateTime(this) {
+    override def dbColumnName = "Event_Timestamp"
+    override def displayName = "Timestamp"
+  }
+
+  object callingNo extends MappedLongColName(this, "callingstationid",
+    "Calling No.")
+
+}
+
+// vim: set ts=2 sw=2 et:
--- a/src/main/scala/radview/snippet/AccountSnippet.scala	Thu Apr 14 15:42:46 2011 +0200
+++ b/src/main/scala/radview/snippet/AccountSnippet.scala	Wed Nov 30 16:59:10 2011 +0100
@@ -30,7 +30,8 @@
 import scala.xml.{NodeSeq, Text}
 
 object AsAccountImsi {
-  def unapply(in: String): Option[RadCheckSub] = RadCheckSub.byImsi(in)
+  def unapply(in: String) = apply(in)
+  def apply(in: String): Option[RadCheckSub] = RadCheckSub.byImsi(in)
 }
 
 abstract sealed class AccountLoc
@@ -38,12 +39,14 @@
 case class ViewAccount(acc: RadCheckSub) extends AccountLoc
 case class SyslogAccount(acc: RadCheckSub) extends AccountLoc
 case class CdrAccount(acc: RadCheckSub) extends AccountLoc
+case class DynIpAccount(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 DynIpAccount(a) => Some(a)
     case _ => None
   }}
 }
@@ -61,6 +64,7 @@
       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")
+      case DynIpAccount(a) => mkPath(prefix, "imsi", a.imsi.is, "dyn-ip")
     }
   }
 
@@ -71,6 +75,7 @@
     case ViewAccount(a) => "Account " + a.imsi
     case SyslogAccount(a) => "Syslog search for account " + a.imsi
     case CdrAccount(a) => "CDR search for account " + a.imsi
+    case DynIpAccount(a) => "Dynamic IP assignments for account " + a.imsi
   }))
 
   override def rewrite: LocRewrite = Full({
@@ -85,9 +90,13 @@
         case "cdr" :: xs =>
           ActionLinks.append(A(url(ViewAccount(a)), "Back to account"))
           CdrAccount(a)
+        case "dyn-ip" :: xs =>
+          ActionLinks.append(A(url(ViewAccount(a)), "Back to account"))
+          DynIpAccount(a)
         case _ =>
           ActionLinks.append(A(url(SyslogAccount(a)), "Syslog search"))
           ActionLinks.append(A(url(CdrAccount(a)), "CDR search"))
+          ActionLinks.append(A(url(DynIpAccount(a)), "Dynamic IP address assignments"))
           ViewAccount(a)
       }
       (RResp(List(tpl, "view")), l)
@@ -104,6 +113,7 @@
     case ("form", Full(SyslogAccount(a))) => syslogForm(a)
     case ("cdr-list", Full(CdrAccount(a))) => cdrList(a)
     case ("form", Full(CdrAccount(a))) => cdrForm(a)
+    case ("dyn-ip-list", Full(DynIpAccount(a))) => dynIpList(a)
 
     case ("form", _) => ClearNodes
     case ("syslog-list", _) => ClearNodes
@@ -111,6 +121,7 @@
     case ("sub", _) => ClearNodes
     case ("if-nai", _) => ClearNodes
     case ("nai", _) => ClearNodes
+    case ("dyn-ip-list", _) => ClearNodes
   }
 
   /* Shared in all searches. */
@@ -309,6 +320,20 @@
         "attr-name-wide", "attr-value") } ) &
     ".list" #> CdrTable(Cdr.findAll( (qp:+MaxRows(mxCnt.is) ):_*), a.isAdsl)
   } openOr ClearNodes
+
+  def dynIpList(a: RadCheckSub): CssTr = {
+    import DynIp._
+    val from = (S param "from") flatMap (AsDateMidnight(_)) openOr (
+      (new DateTime).minusDays(7).toDateMidnight)
+    val to = (S param "to") flatMap (AsDateMidnight(_)) openOr (
+      (new DateTime).toDateMidnight)
+
+    "#dyn-ip-from [value]" #> AsDateMidnight(from) &
+    "#dyn-ip-to [value]" #> AsDateMidnight(to) &
+    ".list" #> Table[DynIp](List(framedIp, ts) map { f =>
+        Column[DynIp](f.displayName, { v: DynIp => f.actualField(v).asHtml })
+      }, byImsi(a.imsi, from, to.plusDays(1)))
+  }
 }
 
 object AccountTable {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/radview/snippet/DynIpSnippet.scala	Wed Nov 30 16:59:10 2011 +0100
@@ -0,0 +1,65 @@
+/*
+ * 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.sitemap._
+import net.liftweb.sitemap.Loc._
+import net.liftweb.util.ClearNodes
+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 DynIpSnippet extends SnippetHelpers {
+
+  val menu = Menu("Dynamic IP Search") / "dyn-ip" >>
+    Snippet("form", form) >> Snippet("res", res)
+
+  def from = (S param "from") flatMap (AsDateMidnight(_)) openOr (
+    (new DateTime).minusDays(7).toDateMidnight)
+  def to = (S param "to") flatMap (AsDateMidnight(_)) openOr (
+    (new DateTime).toDateMidnight)
+
+  def form: CssTr = {
+    "#ip [value]" #> (S param "ip") &
+    "#from [value]" #> AsDateMidnight(from) &
+    "#to [value]" #> AsDateMidnight(to)
+  }
+
+  def res: CssTr = {
+    import DynIp._
+    "*" #> (for {
+      ip <- S param "ip"
+    } yield {
+      Table[DynIp](List(
+        Column[DynIp](callingNo.displayName, { v: DynIp =>
+          val n = v.callingNo.is.toString
+          val h = v.callingNo.asHtml
+          AsAccountImsi(n) map { a =>
+            <a href={AccountSnippet.url(ViewAccount(a))}>{h}</a>
+          } getOrElse h
+        }),
+        Column[DynIp](ts.displayName, { v: DynIp => v.ts.asHtml })
+      ), byIp(ip, from, to.plusDays(1)))
+    })
+  }
+}
+
+// vim: set ts=2 sw=2 et:
--- a/src/main/webapp/account/view.html	Thu Apr 14 15:42:46 2011 +0200
+++ b/src/main/webapp/account/view.html	Wed Nov 30 16:59:10 2011 +0100
@@ -47,6 +47,32 @@
         <span class="num-rows-panel"/><br/>
         <span class="list"/><br/>
       </div>
+      <div class="span-24 last lift:dyn-ip-list">
+        <form method="get">
+          <table>
+            <tr>
+              <td class="form-name">
+                <label for="dyn-ip-from" accesskey="F">From</label>
+              </td>
+              <td class="form-value">
+                <input type="text" id="dyn-ip-from" name="from" class="date"/>
+              </td>
+            </tr>
+            <tr>
+              <td class="form-name">
+                <label for="dyn-ip-to" accesskey="T">To</label>
+              </td>
+              <td class="form-value">
+                <input type="text" id="dyn-ip-to" name="to" class="date"/>
+              </td>
+            </tr>
+            <tr>
+              <td colspan="2"><input type="submit"/></td>
+            </tr>
+          </table>
+        </form>
+        <span class="list"/><br/>
+      </div>
     </div>
   </body>
 </html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/webapp/dyn-ip.html	Wed Nov 30 16:59:10 2011 +0100
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta content="text/html; charset=UTF-8" http-equiv="content-type" />
+    <title>Dyn IP Search</title>
+  </head>
+  <body class="lift:content_id=main">
+    <div id="main" class="lift:surround?with=default;at=content">
+      <head>
+        <script type="text/javascript" src="/js/jquery-ui-1.8.9.custom.min.js"></script>
+        <script type="text/javascript">
+          $(document).ready(function() {
+            $.datepicker.setDefaults({
+              dateFormat: "yy-mm-dd"
+            });
+            $(".date").datepicker();
+          });
+        </script>
+        <link type="text/css" href="/css/jquery-ui-1.8.9.custom.css" rel="stylesheet" />
+      </head>
+      <div class="span-24 last">
+        <h2><span class="lift:Menu.title"/></h2>
+      </div>
+      <div class="span-24 last lift:form">
+        <form method="get">
+          <table>
+            <tr>
+              <td class="form-name">
+                <label for="ip" accesskey="I">IP (exact match)</label>
+              </td>
+              <td class="form-value">
+                <input type="text" id="ip" name="ip"/>
+              </td>
+            </tr>
+            <tr>
+              <td class="form-name">
+                <label for="from" accesskey="F">From</label>
+              </td>
+              <td class="form-value">
+                <input type="text" id="from" name="from" class="date"/>
+              </td>
+            </tr>
+            <tr>
+              <td class="form-name">
+                <label for="to" accesskey="T">To</label>
+              </td>
+              <td class="form-value">
+                <input type="text" id="to" name="to" class="date"/>
+              </td>
+            </tr>
+            <tr>
+              <td colspan="2"><input type="submit"/></td>
+            </tr>
+          </table>
+        </form>
+      </div>
+      <div class="span-24 last">
+        <span class="lift:res"/>
+      </div>
+    </div>
+  </body>
+ </html>