DSLUtils is now deprecated. Implemented the functionality of EffectivePriceUnit.scala...
authorProdromos Gerakios <pgerakios@grnet.gr>
Mon, 9 Jul 2012 13:11:33 +0000 (16:11 +0300)
committerProdromos Gerakios <pgerakios@grnet.gr>
Mon, 9 Jul 2012 13:11:33 +0000 (16:11 +0300)
src/main/scala/gr/grnet/aquarium/computation/TimeslotComputations.scala
src/main/scala/gr/grnet/aquarium/logic/accounting/dsl/DSLUtils.scala
src/main/scala/gr/grnet/aquarium/policy/CronSpec.scala [new file with mode: 0644]
src/main/scala/gr/grnet/aquarium/policy/EffectiveUnitPrice.scala
src/test/scala/gr/grnet/aquarium/logic/test/DSLTimeFrameTest.scala

index 33a1055..dbe7fe3 100644 (file)
@@ -43,8 +43,20 @@ import gr.grnet.aquarium.util.date.MutableDateCalc
 import gr.grnet.aquarium.{AquariumInternalError, AquariumException}
 import gr.grnet.aquarium.event.model.resource.ResourceEventModel
 import gr.grnet.aquarium.logic.accounting.algorithm.SimpleExecutableChargingBehaviorAlgorithm
-import gr.grnet.aquarium.logic.accounting.dsl.{Timeslot, DSLUtils}
-import gr.grnet.aquarium.policy.{EffectivePriceTable, PolicyModel, ResourceType, UserAgreementModel}
+import gr.grnet.aquarium.logic.accounting.dsl.{Timeslot}
+import gr.grnet.aquarium.policy._
+import collection.immutable
+import com.ckkloverdos.maybe.Just
+import gr.grnet.aquarium.computation.Chargeslot
+import scala.Some
+import com.ckkloverdos.maybe.Just
+import gr.grnet.aquarium.computation.Chargeslot
+import scala.Some
+import com.ckkloverdos.maybe.Just
+import gr.grnet.aquarium.policy.ResourceType
+import gr.grnet.aquarium.policy.EffectiveUnitPrice
+import gr.grnet.aquarium.computation.Chargeslot
+import scala.Some
 
 /**
  * Methods for converting accounting events to wallet entries.
@@ -54,7 +66,7 @@ import gr.grnet.aquarium.policy.{EffectivePriceTable, PolicyModel, ResourceType,
  */
 trait TimeslotComputations extends Loggable {
   // TODO: favour composition over inheritance until we decide what to do with DSLUtils (and TimeslotComputations).
-  protected val dslUtils = new DSLUtils {}
+  //protected val dslUtils = new DSLUtils {}
 
   /**
    * Breaks a reference timeslot (e.g. billing period) according to policies and agreements.
@@ -98,7 +110,8 @@ trait TimeslotComputations extends Loggable {
     val clog = ContextualLogger.fromOther(clogOpt, logger, "resolveEffectiveUnitPrices()")
 
     // Note that most of the code is taken from calcChangeChunks()
-    dslUtils.resolveEffectiveUnitPricesForTimeslot(alignedTimeslot, policy, agreement, resourceType)
+    val ret = resolveEffectiveUnitPricesForTimeslot(alignedTimeslot, policy, agreement, resourceType)
+    ret map {case (t,p) => (t,p.unitPrice)}
   }
 
   protected
@@ -299,4 +312,87 @@ trait TimeslotComputations extends Loggable {
       a.head :: alignTimeslots(safeTail(a), safeTail(b))
     }
   }
+
+    type PriceMap =  immutable.SortedMap[Timeslot, EffectiveUnitPrice]
+    type PriceList = List[EffectiveUnitPrice]
+    private def emptyMap = immutable.SortedMap[Timeslot,EffectiveUnitPrice]()
+
+    /**
+     * Resolves the effective price list for each chunk of the
+     * provided timeslot and returns it as a Map
+     */
+    private def resolveEffectiveUnitPricesForTimeslot(
+                                               alignedTimeslot: Timeslot,
+                                               policy: PolicyModel,
+                                               agreement: UserAgreementModel,
+                                               resourceType: ResourceType
+                                               ): PriceMap = {
+
+      val role = agreement.role
+      val fullPriceTable = agreement.fullPriceTableRef match {
+        case PolicyDefinedFullPriceTableRef ⇒
+          policy.roleMapping.get(role) match {
+            case Some(fullPriceTable) ⇒
+              fullPriceTable
+
+            case None ⇒
+              throw new AquariumInternalError("Unknown role %s".format(role))
+          }
+
+        case AdHocFullPriceTableRef(fullPriceTable) ⇒
+          fullPriceTable
+      }
+
+      val effectivePriceTable = fullPriceTable.perResource.get(resourceType.name) match {
+        case None ⇒
+          throw new AquariumInternalError("Unknown resource type %s".format(role))
+
+        case Some(effectivePriceTable) ⇒
+          effectivePriceTable
+      }
+
+      resolveEffective(alignedTimeslot, effectivePriceTable.priceOverrides)
+    }
+
+    private def printPriceList(p: PriceList) : Unit = {
+      Console.err.println("BEGIN PRICE LIST")
+      for { p1 <- p } Console.err.println(p1)
+      Console.err.println("END PRICE LIST")
+    }
+
+    private def printPriceMap(m: PriceMap) = {
+      Console.err.println("BEGIN PRICE MAP")
+      for { (t,p) <- m.toList } Console.err.println("Timeslot " + t + "\t\t" + p)
+      Console.err.println("END PRICE MAP")
+    }
+
+    private def resolveEffective(alignedTimeslot: Timeslot,p:PriceList): PriceMap = {
+      Console.err.println("\n\nInput timeslot: " + alignedTimeslot + "\n\n")
+      printPriceList(p)
+      val ret =  resolveEffective3(alignedTimeslot,p) //HERE
+      printPriceMap(ret)
+      ret
+    }
+
+
+    private def resolveEffective3(alignedTimeslot: Timeslot, effectiveUnitPrices: PriceList): PriceMap =
+      effectiveUnitPrices match {
+        case Nil =>
+          emptyMap
+        case hd::tl =>
+          val (satisfied,notSatisfied) = hd splitTimeslot alignedTimeslot
+          val satisfiedMap = satisfied.foldLeft (emptyMap)  {(map,t) =>
+          //Console.err.println("Adding timeslot" + t +
+          // " for policy " + policy.name)
+            map + ((t,hd))
+          }
+          val notSatisfiedMap = notSatisfied.foldLeft (emptyMap) {(map,t) =>
+            val otherMap = resolveEffective3(t,tl)
+            //Console.err.println("Residual timeslot: " + t)
+            val ret = map ++ otherMap
+            ret
+          }
+          val ret = satisfiedMap ++ notSatisfiedMap
+          ret
+      }
 }
index 3a72eec..1251885 100644 (file)
@@ -47,198 +47,7 @@ import gr.grnet.aquarium.AquariumInternalError
  */
 
 trait DSLUtils {
-  /**
-   * Resolves the effective price list for each chunk of the
-   * provided timeslot and returns it as a Map
-   */
-  def resolveEffectiveUnitPricesForTimeslot(
-      alignedTimeslot: Timeslot,
-      policy: PolicyModel,
-      agreement: UserAgreementModel,
-      resourceType: ResourceType
-  ): immutable.SortedMap[Timeslot, Double] = {
 
-    val role = agreement.role
-    val fullPriceTable = agreement.fullPriceTableRef match {
-      case PolicyDefinedFullPriceTableRef ⇒
-        policy.roleMapping.get(role) match {
-          case Some(fullPriceTable) ⇒
-            fullPriceTable
+ //TODO pgerakios: REMOVE THIS FILE
 
-          case None ⇒
-            throw new AquariumInternalError("Unknown role %s".format(role))
-        }
-
-      case AdHocFullPriceTableRef(fullPriceTable) ⇒
-        fullPriceTable
-    }
-
-    val effectivePriceTable = fullPriceTable.perResource.get(resourceType.name) match {
-      case None ⇒
-        throw new AquariumInternalError("Unknown resource type %s".format(role))
-
-      case Some(effectivePriceTable) ⇒
-        effectivePriceTable
-    }
-
-    resolveEffective(alignedTimeslot, effectivePriceTable)
-  }
-
-  /*private def printPolicy[T <: DSLTimeBoundedItem[T]](t : T) : Unit = {
-      Console.err.println("Policy " + t.name + " " + t.toTimeslot + " DETAIL : " + t.effective)
-      t.overrides match {
-        case None => Console.println
-        case Some(t) => printPolicy(t)
-      }
-    }
-
-    private def printMap[T <: DSLTimeBoundedItem[T]](m: immutable.SortedMap[Timeslot, T]) = {
-      Console.err.println("BEGIN MAP: ")
-      for { (t,p) <- m.toList } Console.err.println("Timeslot " + t + "\t\t" + p.name)
-      Console.err.println("END MAP")
-    } */
-
-  def resolveEffective[T <: DSLTimeBoundedItem[T]](timeslot0: Timeslot,policy: T):
-    immutable.SortedMap[Timeslot, T] = {
-      //Console.err.println("\n\nInput timeslot: " + timeslot0 + "\n\n")
-      ///printPolicy(policy)
-      val ret =  resolveEffective3(timeslot0,policy) //HERE
-      //printMap(ret)
-      ret
-    }
-
-
-  def resolveEffective3(
-      alignedTimeslot: Timeslot,
-      effectivePriceTable: EffectivePriceTable
-  ): immutable.SortedMap[Timeslot, Double/*unit price*/] = {
-//    assert(policy.toTimeslot contains timeslot0,"Policy does not contain timeslot")
-    val timeslot = alignedTimeslot //TODO: timeslot0.align(5000)
-    val subtimeslots_of_this_policy = Timeslot.mergeOverlaps(policy.effective intervalsOf timeslot)
-    val subtimeslots_NOT_IN_this_policy = Timeslot.mergeOverlaps(timeslot.nonOverlappingTimeslots
-                                                                                          (subtimeslots_of_this_policy))
-    val policy_map =  subtimeslots_of_this_policy.foldLeft  (immutable.SortedMap[Timeslot, T]())
-                                                            {(map,t) =>
-                                                                  //Console.err.println("Adding timeslot" + t + " for policy " + policy.name)
-                                                                  map + ((t,policy))
-                                                            }
-    val other_policy_map = policy.overrides match {
-                                              case None =>
-                                                 immutable.SortedMap[Timeslot, T]()
-                                              case Some(parent_policy)=>
-                                                  subtimeslots_NOT_IN_this_policy.foldLeft (
-                                                      (immutable.SortedMap[Timeslot, T]()))
-                                                      {(map,t) =>
-                                                        //Console.err.println("Residual timeslot: " + t)
-                                                        map ++ resolveEffective3(t,parent_policy)
-                                                      }
-                                            }
-    val final_map = policy_map ++ other_policy_map
-    final_map
-  }
-
-  /*def resolveEffective2[T <: DSLTimeBoundedItem[T]](timeslot0: Timeslot,policy: T):
-  immutable.SortedMap[Timeslot, T] = {
-    assert(policy.toTimeslot contains timeslot0,"Policy does not contain timeslot")
-
-    /* generate mappings from timeslots -> policies
-     * Algorithm: find next valid date (starting from timeslot.start) in this policy
-     */
-    val timeslot = timeslot0 //TODO: timeslot0.align(5000)
-    def nextDate(d:Date,p:T) : Option[(Date,T,Boolean)] =
-      (p.effective nextValidAfter d,p.overrides) match {
-        case (None,None) => None
-        case (None,Some(parent_policy)) =>
-          val d1 = nextDate(d,parent_policy)
-          d1
-        case (Some(d1),_) => /* the next valid date cannot occur after the end of timeslot*/
-           if (d1.before(timeslot.to)) Some((d1,p,true)) else Some((timeslot.to,p,false))
-      }
-    def genMap(map: immutable.SortedMap[Timeslot, T],d:Date) : immutable.SortedMap[Timeslot, T] = {
-        val step = 1000L
-        nextDate(d,policy) match {
-        case None => map
-        case Some((d1,policy,cont)) =>
-          val t = Timeslot(d,d1)
-          val map1 = map + (t -> policy)
-          if(cont) genMap(map1,new Date(d1.getTime + step)) // 1 second after d1
-          else map1 /* done */
-      }
-    }
-    val map = genMap(immutable.SortedMap[Timeslot, T](),timeslot.from)
-    map
-  }
-
-  /**
-   * Splits the provided timeslot into chunks according to the validity
-   * timeslots specified by the provided time bounded item. It
-   * returns a map whose keys are the timeslot chunks and the values
-   * correspond to the effective time bounded item (algorithm or pricelist).
-   */
-  def resolveEffective1[T <: DSLTimeBoundedItem[T]](timeslot: Timeslot,policy: T):
-  immutable.SortedMap[Timeslot, T] = {
-      assert(policy.toTimeslot contains timeslot,"Policy does not contain timeslot")
-
-     /* Get a list of all effective/expanded policy timeslots within
-        a timeslot specified by "variable timeslot".
-      * NOTICE: The returned timeslots may be slightly out of "timeslot" bounds
-      *         so we need to invoke overlappingTimeslots and nonOverlapping timeslots
-      */
-     val all_timeslots = allEffectiveTimeslots(policy.effective,timeslot)
-     val timeslots_IN_policy = timeslot.overlappingTimeslots(all_timeslots)
-     val timeslots_NOT_IN_policy = timeslot.nonOverlappingTimeslots(all_timeslots)
-
-     val ret =  immutable.SortedMap[Timeslot, T]() ++
-      /*add [timeslots -> policy] covered by this policy*/
-      timeslots_IN_policy.flatMap {
-         effective_timeslot =>  Map(effective_timeslot -> policy)
-      } ++
-      /*add [timeslots -> policy] covered by parent policies */
-      timeslots_NOT_IN_policy.flatMap { /* search the policy hierarchy for effective timeslots not covered by this policy.*/
-        not_effective_timeslot => policy.overrides match {
-          case None => immutable.SortedMap[Timeslot, T]() /*Nothing to do. TODO: throw exception ?*/
-          case Some(parent_policy) => resolveEffective1(not_effective_timeslot,parent_policy) /* search the policy hierarchy*/
-        }
-      }
-    ret
-  }*/
-
-   /*
-  /*
-   * Get a list of all timeslots within which a timeframe
-   * is effective, whithin the provided time bounds.
-   */
-  def allEffectiveTimeslots(spec: DSLTimeFrame, t: Timeslot):
-  List[Timeslot] =
-    if (spec.repeat.isEmpty) { //A  simple timeframe with no repetition defined
-      val fromDate = if (spec.from.before(t.from)) t.from else spec.from
-      val toDate = if (spec.to.getOrElse(t.to).after(t.to)) t.to else spec.to.getOrElse(t.to)
-      List(Timeslot(fromDate, toDate))
-    } /* otherwise for all repetitions determine timeslots*/
-    else {
-        val all =  for { r <- spec.repeat
-                         ts <- effectiveTimeslots(r, t.from, t.to)
-                      }  yield ts
-      //for{ i <- all} Console.err.println(i)
-        mergeOverlaps(all)
-    }
-  */
-
-
-
-  /**
-   * Merges overlapping timeslots.
-   */
-
-  /*/**
-   * Get a list of all timeslots within which a time frame is active.
-     The result is returned sorted by timeframe start date.
-   */
-  def effectiveTimeslots(spec: DSLTimeFrameRepeat, from: Date, to : Date):
-    List[Timeslot] =
-      for { (h1,h2) <- spec.start zip spec.end
-            (d1,d2) <- DSLTimeSpec.expandTimeSpec(h1,h2,from,to)
-          }
-      yield Timeslot(d1,d2)
-      */
 }
diff --git a/src/main/scala/gr/grnet/aquarium/policy/CronSpec.scala b/src/main/scala/gr/grnet/aquarium/policy/CronSpec.scala
new file mode 100644 (file)
index 0000000..13e8333
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials
+ *      provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and
+ * documentation are those of the authors and should not be
+ * interpreted as representing official policies, either expressed
+ * or implied, of GRNET S.A.
+ */
+package gr.grnet.aquarium.policy
+
+import org.quartz.CronExpression
+import java.util.Date
+import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
+
+/**
+ * @author Prodromos Gerakios <pgerakios@grnet.gr>
+ */
+
+
+case class CronSpec(cronSpec: String) {
+
+  private val cronExpr = {
+    val e = "00 " + cronSpec.trim //IMPORTANT: WE DO NOT CARE ABOUT SECONDS!!!
+    val l = e.split(" ")
+    assert(l.size == 6,"Invalid cron specification")
+    //for {ll <- l } Console.err.println(ll)
+    (l(3),l(5))  match {
+      case ("?",_) | (_,"?") => ()
+      case (_,"*") => l.update(5,"?")
+      case ("*",_) => l.update(3,"?")
+    }
+    val e1 = l.foldLeft("") { (s,elt) => s + " " + elt}
+    //Console.err.println("e = " + e + " and e1 = " + e1)
+    new CronExpression(e1)
+  }
+
+  def includes(d:Date) : Boolean =
+    cronExpr isSatisfiedBy d
+
+  /* the next valid date cannot outlive (min,max)*/
+  def nextValidDate(t:Timeslot,d:Date) : Option[Date] =
+    (cronExpr getNextValidTimeAfter d) match {
+      case null =>
+        None
+      case d1 =>
+        val (min0,max0) = (t.from,t.to)
+        val (min,max,e) = (min0.getTime,max0.getTime,d1.getTime)
+        if(e < min || e>max)
+          None
+        else
+          Some({assert(d1.getTime>=d.getTime);d1})
+    }
+
+  override def toString : String = cronSpec
+}
\ No newline at end of file
index 85a1279..7bc0807 100644 (file)
 
 package gr.grnet.aquarium.policy
 
-import gr.grnet.aquarium.logic.accounting.dsl.DSLTimeFrame
+import gr.grnet.aquarium.logic.accounting.dsl.{Timeslot, DSLCronSpec, DSLTimeFrame}
+import collection.mutable
 
 /**
  *
  * @author Christos KK Loverdos <loverdos@gmail.com>
+ * @author Prodromos Gerakios <pgerakios@grnet.gr>
  */
 
-case class EffectiveUnitPrice(unitPrice: Double, when: DSLTimeFrame) // TODO: use DSLTimeframe
+case class EffectiveUnitPrice(unitPrice: Double, when: Option[(CronSpec,CronSpec)]) { // TODO: use DSLTimeframe
+
+  /* Split a timeslot T into two *sets* S and S2 consisting of timeslots such that
+   *  (a) each element in S1,S2 is contained in T
+   *  (b) for all x in S1 and y in S2 there is no overlap between x and y.
+   *  (c) the union of all x in S1 and y S2 is T
+   *  (d) the elements of S1 satisfy the cron spec ``when''
+   *  (e) the elements of S2 do NOT satisfy the cron spec ``when''
+   */
+  def splitTimeslot(t:Timeslot) : (List[Timeslot],List[Timeslot])=
+     when match {
+       case None =>
+         (List(t),Nil)
+       case Some((start,end)) =>
+         val result = new mutable.ListBuffer[Timeslot]()
+         var offset = t.from
+         while(start.nextValidDate(t,offset) match {
+           case None =>
+             false
+           case Some(d_start) =>
+             end.nextValidDate(t,d_start) match {
+               case None =>
+                 result += Timeslot(d_start,t.to)
+                 false
+               case Some(d_end) =>
+                 result += Timeslot(d_start,d_end)
+                 offset = d_end
+                 d_end.before(t.to)
+             }
+         }) ()
+         val l = result.toList
+         val l1 = Timeslot mergeOverlaps l
+         val l2 = t nonOverlappingTimeslots l1
+         val l3 = Timeslot mergeOverlaps l2
+         (l1,l3)
+     }
+
+  private def stringOfStartCron = when match {
+    case None => "? ? ? ? ?"
+    case Some((s,_)) => s.toString
+  }
+  private def stringOfEndCron = when match {
+    case None => "? ? ? ? ?"
+    case Some((_,s)) => s.toString
+  }
+  override def toString : String = "EffectiveUnitPrice(%d,%s,%s)".
+                          format(unitPrice,stringOfStartCron,stringOfEndCron)
+ }
index 150af8a..9fe7030 100644 (file)
@@ -35,7 +35,7 @@
 package gr.grnet.aquarium.logic.test
 
 import org.junit.Test
-import gr.grnet.aquarium.logic.accounting.dsl.{Timeslot, DSLTimeFrameRepeat, DSLTimeSpec, DSLTimeFrame}
+import gr.grnet.aquarium.logic.accounting.dsl.{Timeslot, DSLTimeFrameRepeat,DSLTimeFrame}
 import gr.grnet.aquarium.util.TestMethods
 import java.util.Date
 import scala._
@@ -45,7 +45,7 @@ import scala.Some
 * Represents an effectivity timeframe.
 *
 * @author Prodromos Gerakios <pgerakios@grnet.gr>
-*/
+/**/
 class DSLTimeFrameTest extends DSLTestBase with TestMethods {
 
   private def makeRepeat(startCron:String,endCron:String): DSLTimeFrameRepeat={
@@ -88,4 +88,4 @@ class DSLTimeFrameTest extends DSLTestBase with TestMethods {
 
     //TODO: add timeslot contained in big timeslot
   }
-}
\ No newline at end of file
+}  */
\ No newline at end of file