From 00ff2a8da5840652b0c3974753522163a0878b9a Mon Sep 17 00:00:00 2001 From: Christos KK Loverdos Date: Tue, 20 Mar 2012 15:28:34 +0200 Subject: [PATCH] Calculations for synthetic stuff --- .../aquarium/logic/events/NewWalletEntry.scala | 6 +- .../aquarium/logic/events/ResourceEvent.scala | 6 +- .../gr/grnet/aquarium/user/UserDataSnapshot.scala | 15 +++ .../aquarium/user/UserStateComputations.scala | 99 +++++++++++++++----- 4 files changed, 96 insertions(+), 30 deletions(-) diff --git a/src/main/scala/gr/grnet/aquarium/logic/events/NewWalletEntry.scala b/src/main/scala/gr/grnet/aquarium/logic/events/NewWalletEntry.scala index 1cce544..19980c0 100644 --- a/src/main/scala/gr/grnet/aquarium/logic/events/NewWalletEntry.scala +++ b/src/main/scala/gr/grnet/aquarium/logic/events/NewWalletEntry.scala @@ -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 ) } diff --git a/src/main/scala/gr/grnet/aquarium/logic/events/ResourceEvent.scala b/src/main/scala/gr/grnet/aquarium/logic/events/ResourceEvent.scala index 1207dab..96babd6 100644 --- a/src/main/scala/gr/grnet/aquarium/logic/events/ResourceEvent.scala +++ b/src/main/scala/gr/grnet/aquarium/logic/events/ResourceEvent.scala @@ -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), diff --git a/src/main/scala/gr/grnet/aquarium/user/UserDataSnapshot.scala b/src/main/scala/gr/grnet/aquarium/user/UserDataSnapshot.scala index 8e74097..9fbd070 100644 --- a/src/main/scala/gr/grnet/aquarium/user/UserDataSnapshot.scala +++ b/src/main/scala/gr/grnet/aquarium/user/UserDataSnapshot.scala @@ -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 diff --git a/src/main/scala/gr/grnet/aquarium/user/UserStateComputations.scala b/src/main/scala/gr/grnet/aquarium/user/UserStateComputations.scala index a481a8f..a87c476 100644 --- a/src/main/scala/gr/grnet/aquarium/user/UserStateComputations.scala +++ b/src/main/scala/gr/grnet/aquarium/user/UserStateComputations.scala @@ -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) } } -- 1.7.10.4