Calculations for synthetic stuff
authorChristos KK Loverdos <loverdos@gmail.com>
Tue, 20 Mar 2012 13:28:34 +0000 (15:28 +0200)
committerChristos KK Loverdos <loverdos@gmail.com>
Tue, 20 Mar 2012 13:31:20 +0000 (15:31 +0200)
src/main/scala/gr/grnet/aquarium/logic/events/NewWalletEntry.scala
src/main/scala/gr/grnet/aquarium/logic/events/ResourceEvent.scala
src/main/scala/gr/grnet/aquarium/user/UserDataSnapshot.scala
src/main/scala/gr/grnet/aquarium/user/UserStateComputations.scala

index 1cce544..19980c0 100644 (file)
@@ -75,7 +75,8 @@ case class NewWalletEntry(userId: String,
   def chargslotCount = chargeslots.length
   def isOutOfSync = currentResourceEvent.isOutOfSyncForBillingMonth(yearOfBillingMonth, billingMonth)
 
-  def toDebugString = "%s(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)".format(
+  def toDebugString = "%s%s(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)".format(
+    if(isSynthetic) "*" else "",
     gr.grnet.aquarium.util.shortClassNameOf(this),
     userId,
     referenceTimeslot,
@@ -87,8 +88,7 @@ case class NewWalletEntry(userId: String,
     billingMonth,
     resourceEvents,
     chargeslots,
-    resourceDef,
-    if(isSynthetic) "SYNTHETIC" else ""
+    resourceDef
   )
 }
 
index 1207dab..96babd6 100644 (file)
@@ -130,7 +130,8 @@ case class ResourceEvent(
     val instanceInfo = if(useOnlyInstanceId) instanceId else "%s::%s".format(resource, instanceId)
     val occurredFormatted = new MutableDateCalc(occurredMillis).toYYYYMMDDHHMMSS
     if(occurredMillis == receivedMillis) {
-      "EVENT(%s, [%s], %s, %s, %s, %s, %s)".format(
+      "%sEVENT(%s, [%s], %s, %s, %s, %s, %s)".format(
+        if(isSynthetic) "*" else "",
         id,
         occurredFormatted,
         value,
@@ -140,7 +141,8 @@ case class ResourceEvent(
         clientId
       )
     } else {
-      "EVENT(%s, [%s], [%s], %s, %s, %s, %s, %s)".format(
+      "%sEVENT(%s, [%s], [%s], %s, %s, %s, %s, %s)".format(
+        if(isSynthetic) "*" else "",
         id,
         occurredFormatted,
         new MutableDateCalc(receivedMillis),
index 8e74097..9fbd070 100644 (file)
@@ -304,6 +304,17 @@ case class LatestResourceEventsWorker(latestEventsMap: FullMutableResourceTypeMa
   }
 }
 
+object LatestResourceEventsWorker {
+  final val Empty = LatestResourceEventsWorker(scala.collection.mutable.Map())
+
+  /**
+   * Helper factory to construct a worker from a list of events.
+   */
+  def fromList(latestEventsList: List[ResourceEvent]): LatestResourceEventsWorker = {
+    LatestResourceEventsSnapshot(latestEventsList, 0L).toMutableWorker
+  }
+}
+
 /**
  * Keeps the implicit OFF events when a billing period ends.
  * This is normally recorded in the [[gr.grnet.aquarium.user.UserState]].
@@ -357,6 +368,10 @@ case class ImplicitlyIssuedResourceEventsWorker(implicitlyIssuedEventsMap: FullM
   }
 }
 
+object ImplicitlyIssuedResourceEventsWorker {
+  final val Empty = ImplicitlyIssuedResourceEventsWorker(scala.collection.mutable.Map())
+}
+
 /**
  *
  * @author Christos KK Loverdos <loverdos@gmail.com>
index a481a8f..a87c476 100644 (file)
@@ -44,7 +44,7 @@ import gr.grnet.aquarium.util.date.{TimeHelpers, MutableDateCalc}
 import gr.grnet.aquarium.logic.accounting.dsl.{DSLAgreement, DSLCostPolicy, DSLResourcesMap, DSLPolicy}
 import gr.grnet.aquarium.store.{RecordID, StoreProvider, PolicyStore, UserStateStore, ResourceEventStore}
 import gr.grnet.aquarium.logic.accounting.{Chargeslot, Accounting}
-import collection.mutable.{Buffer, ListBuffer}
+import scala.collection.mutable
 import gr.grnet.aquarium.logic.events.ResourceEvent._
 
 /**
@@ -224,7 +224,7 @@ class UserStateComputations extends Loggable {
                            policyStore: PolicyStore,
                            stateChangeReason: UserStateChangeReason,
                            billingMonthInfo: BillingMonthInfo,
-                           walletEntriesBuffer: Buffer[NewWalletEntry],
+                           walletEntriesBuffer: mutable.Buffer[NewWalletEntry],
                            clogM: Maybe[ContextualLogger] = NoVal): UserState = {
 
     val clog = ContextualLogger.fromOther(clogM, logger, "walletEntriesForResourceEvent(%s)", currentResourceEvent.id)
@@ -372,7 +372,7 @@ class UserStateComputations extends Loggable {
                             policyStore: PolicyStore,
                             stateChangeReason: UserStateChangeReason,
                             billingMonthInfo: BillingMonthInfo,
-                            walletEntriesBuffer: Buffer[NewWalletEntry],
+                            walletEntriesBuffer: mutable.Buffer[NewWalletEntry],
                             clogM: Maybe[ContextualLogger] = NoVal): UserState = {
 
     var _workingUserState = startingUserState
@@ -457,6 +457,7 @@ class UserStateComputations extends Loggable {
 
     val newWalletEntries = scala.collection.mutable.ListBuffer[NewWalletEntry]()
 
+    clog.debug("")
     clog.debug("Process all occurred events")
     _workingUserState = processResourceEvents(
       allResourceEventsForMonth,
@@ -469,26 +470,30 @@ class UserStateComputations extends Loggable {
       clogJ
     )
 
-    // Second, for the remaining events which must contribute an implicit OFF, we collect and process
-    // those OFFs and generate an implicit ON
-    val allEndEventsBuffer = ListBuffer[ResourceEvent]()
-    for {
-      aPreviousEvent <- userStateWorker.allPreviousAndAllImplicitlyStarted
-      dslResource    <- defaultResourcesMap.findResource(aPreviousEvent.safeResource)
-      costPolicy     =  dslResource.costPolicy
-    } {
-      if(costPolicy.supportsImplicitEvents) {
-        if(costPolicy.mustConstructImplicitEndEventFor(aPreviousEvent)) {
-          allEndEventsBuffer append costPolicy.constructImplicitEndEventFor(aPreviousEvent, billingMonthEndMillis)
-        }
-      }
-    }
-
+    clog.debug("")
     clog.debug("Process implicitly issued events")
+    // Second, for the remaining events which must contribute an implicit OFF, we collect those OFFs
+    // ... in order to generate an implicit ON later
+    val (specialEvents, theirImplicitEnds) = userStateWorker.
+      findAndRemoveGeneratorsOfImplicitEndEvents(billingMonthEndMillis)
+    clog.debugSeq("specialEvents", specialEvents, 0)
+    clog.debugSeq("theirImplicitEnds", theirImplicitEnds, 0)
+
+    // Now, the previous and implicitly started must be our base for the following computation, so we create an
+    // appropriate worker
+    val specialUserStateWorker = UserStateWorker(
+      userStateWorker.userId,
+      LatestResourceEventsWorker.fromList(specialEvents),
+      ImplicitlyIssuedResourceEventsWorker.Empty,
+      IgnoredFirstResourceEventsWorker.Empty,
+      userStateWorker.accounting,
+      userStateWorker.resourcesMap
+    )
+
     _workingUserState = processResourceEvents(
-      allEndEventsBuffer.toList,
+      theirImplicitEnds,
       _workingUserState,
-      userStateWorker,
+      specialUserStateWorker,
       policyStore,
       calculationReason,
       billingMonthInfo,
@@ -496,6 +501,8 @@ class UserStateComputations extends Loggable {
       clogJ
     )
 
+    clog.debug("")
+
     val lastUpdateTime = TimeHelpers.nowMillis
 
     _workingUserState = _workingUserState.copy(
@@ -603,13 +610,55 @@ case class UserStateWorker(userId: String,
     }
   }
 
-  def allPreviousAndAllImplicitlyStarted: List[ResourceEvent] = {
-    val buffer: FullMutableResourceTypeMap = scala.collection.mutable.Map[FullResourceType, ResourceEvent]()
+//  private[this]
+//  def allPreviousAndAllImplicitlyStarted: List[ResourceEvent] = {
+//    val buffer: FullMutableResourceTypeMap = scala.collection.mutable.Map[FullResourceType, ResourceEvent]()
+//
+//    buffer ++= implicitlyIssuedStartEvents.implicitlyIssuedEventsMap
+//    buffer ++= previousResourceEvents.latestEventsMap
+//
+//    buffer.valuesIterator.toList
+//  }
 
-    buffer ++= implicitlyIssuedStartEvents.implicitlyIssuedEventsMap
-    buffer ++= previousResourceEvents.latestEventsMap
+  /**
+   * Find those events from `implicitlyIssuedStartEvents` and `previousResourceEvents` that will generate implicit
+   * end events along with those implicitly issued events. Before returning, remove the events that generated the
+   * implicit ends from the internal state of this instance.
+   *
+   * @see [[gr.grnet.aquarium.logic.accounting.dsl.DSLCostPolicy]]
+   */
+  def findAndRemoveGeneratorsOfImplicitEndEvents(newOccuredMillis: Long
+                                                ): (List[ResourceEvent], List[ResourceEvent]) = {
+    val buffer = mutable.ListBuffer[(ResourceEvent, ResourceEvent)]()
+    val checkSet = mutable.Set[ResourceEvent]()
     
-    buffer.valuesIterator.toList
+    def doItFor(map: ResourceEvent.FullMutableResourceTypeMap): Unit = {
+      val resourceEvents = map.valuesIterator
+      for {
+        resourceEvent <- resourceEvents
+        dslResource   <- resourcesMap.findResource(resourceEvent.safeResource)
+        costPolicy    =  dslResource.costPolicy
+      } {
+        if(costPolicy.supportsImplicitEvents) {
+          if(costPolicy.mustConstructImplicitEndEventFor(resourceEvent)) {
+            val implicitEnd = costPolicy.constructImplicitEndEventFor(resourceEvent, newOccuredMillis)
+            
+            if(!checkSet.contains(resourceEvent)) {
+              checkSet.add(resourceEvent)
+              buffer append ((resourceEvent, implicitEnd))
+            }
+
+            // remove it anyway
+            map.remove((resourceEvent.safeResource, resourceEvent.safeInstanceId))
+          }
+        }
+      }
+    }
+
+    doItFor(previousResourceEvents.latestEventsMap)                // we give priority for previous
+    doItFor(implicitlyIssuedStartEvents.implicitlyIssuedEventsMap) // ... over implicitly issued...
+
+    (buffer.view.map(_._1).toList, buffer.view.map(_._2).toList)
   }
 }