import collection.immutable.SortedMap
import com.ckkloverdos.maybe.{NoVal, Maybe}
import gr.grnet.aquarium.util.{ContextualLogger, Loggable}
-import gr.grnet.aquarium.store.PolicyStore
-import gr.grnet.aquarium.util.date.MutableDateCalc
-import gr.grnet.aquarium.{Aquarium, AquariumInternalError}
-import gr.grnet.aquarium.event.model.resource.ResourceEventModel
-import gr.grnet.aquarium.logic.accounting.algorithm.SimpleExecutableChargingBehaviorAlgorithm
+import gr.grnet.aquarium.AquariumInternalError
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.policy.ResourceType
import gr.grnet.aquarium.policy.EffectiveUnitPrice
+import gr.grnet.aquarium.charging.{ChargingBehavior, Chargeslot}
/**
* Methods for converting accounting events to wallet entries.
* @author Georgios Gousios <gousiosg@gmail.com>
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
-trait TimeslotComputations extends Loggable {
- // TODO: favour composition over inheritance until we decide what to do with DSLUtils (and TimeslotComputations).
- //protected val dslUtils = new DSLUtils {}
+object TimeslotComputations extends Loggable {
/**
* Breaks a reference timeslot (e.g. billing period) according to policies and agreements.
policy: PolicyModel,
agreement: UserAgreementModel,
resourceType: ResourceType,
+ effectivePriceTableSelector: FullPriceTable ⇒ EffectivePriceTable,
clogOpt: Option[ContextualLogger] = None
): SortedMap[Timeslot, Double] = {
val clog = ContextualLogger.fromOther(clogOpt, logger, "resolveEffectiveUnitPrices()")
// Note that most of the code is taken from calcChangeChunks()
- val ret = resolveEffectiveUnitPricesForTimeslot(alignedTimeslot, policy, agreement, resourceType)
+ val ret = resolveEffectiveUnitPricesForTimeslot(alignedTimeslot, policy, agreement, resourceType, effectivePriceTableSelector)
ret map {case (t,p) => (t,p.unitPrice)}
}
- protected
def computeInitialChargeslots(
referenceTimeslot: Timeslot,
resourceType: ResourceType,
policyByTimeslot: SortedMap[Timeslot, PolicyModel],
agreementByTimeslot: SortedMap[Timeslot, UserAgreementModel],
+ effectivePriceTableSelector: FullPriceTable ⇒ EffectivePriceTable,
clogOpt: Option[ContextualLogger] = None
): List[Chargeslot] = {
}
// 1. Round ONE: split time according to overlapping policies and agreements.
+ //val alignedTimeslots = List(referenceTimeslot) //splitTimeslotByPoliciesAndAgreements(referenceTimeslot, policyTimeslots.toList, agreementTimeslots.toList, Just(clog))
val alignedTimeslots = splitTimeslotByPoliciesAndAgreements(referenceTimeslot, policyTimeslots.toList, agreementTimeslots.toList, Just(clog))
// 2. Round TWO: Use the aligned timeslots of Round ONE to produce even more
val allChargeslots = for {
alignedTimeslot <- alignedTimeslots
} yield {
+ //val policy = policyByTimeslot.valuesIterator.next()//getPolicyWithin(alignedTimeslot)
val policy = getPolicyWithin(alignedTimeslot)
// clog.debug("dslPolicy = %s", dslPolicy)
+ //val userAgreement = agreementByTimeslot.valuesIterator.next()//getAgreementWithin(alignedTimeslot)
val userAgreement = getAgreementWithin(alignedTimeslot)
- // TODO: Factor this out, just like we did with:
- // TODO: val alignedTimeslots = splitTimeslotByPoliciesAndAgreements
- // Note that most of the code is already taken from calcChangeChunks()
- val unitPriceByTimeslot = resolveEffectiveUnitPrices(alignedTimeslot, policy, userAgreement, resourceType, Some(clog))
+ val unitPriceByTimeslot = resolveEffectiveUnitPrices(
+ alignedTimeslot,
+ policy,
+ userAgreement,
+ resourceType,
+ effectivePriceTableSelector,
+ Some(clog)
+ )
// Now, the timeslots must be the same
val finegrainedTimeslots = unitPriceByTimeslot.keySet
}
/**
- * Compute the charge slots generated by a particular resource event.
- *
- */
- def computeFullChargeslots(
- aquarium: Aquarium,
- previousResourceEventOpt: Option[ResourceEventModel],
- currentResourceEvent: ResourceEventModel,
- oldCredits: Double,
- oldTotalAmount: Double,
- newTotalAmount: Double,
- resourceType: ResourceType,
- agreementByTimeslot: SortedMap[Timeslot, UserAgreementModel],
- policyStore: PolicyStore,
- clogOpt: Option[ContextualLogger] = None
- ): (Timeslot, List[Chargeslot]) = {
-
- val clog = ContextualLogger.fromOther(clogOpt, logger, "computeFullChargeslots()")
- // clog.begin()
-
- val occurredDate = currentResourceEvent.occurredDate
- val occurredMillis = currentResourceEvent.occurredMillis
- val chargingBehavior = aquarium.chargingBehaviorOf(resourceType)
-
- val (referenceTimeslot, policyByTimeslot, previousValue) = chargingBehavior.needsPreviousEventForCreditAndAmountCalculation match {
- // We need a previous event
- case true ⇒
- previousResourceEventOpt match {
- // We have a previous event
- case Some(previousResourceEvent) ⇒
- val referenceTimeslot = Timeslot(previousResourceEvent.occurredDate, occurredDate)
- // all policies within the interval from previous to current resource event
- // clog.debug("Calling policyStore.loadAndSortPoliciesWithin(%s)", referenceTimeslot)
- // TODO: store policies in mem?
- val policyByTimeslot = policyStore.loadAndSortPoliciesWithin(referenceTimeslot.from.getTime, referenceTimeslot.to.getTime)
-
- (referenceTimeslot, policyByTimeslot, previousResourceEvent.value)
-
- // We do not have a previous event
- case None ⇒
- throw new AquariumInternalError(
- "Unable to charge. No previous event given for %s".format(currentResourceEvent.toDebugString))
- }
-
- // We do not need a previous event
- case false ⇒
- // ... so we cannot compute timedelta from a previous event, there is just one chargeslot
- // referring to (almost) an instant in time
- val previousValue = chargingBehavior.getResourceInstanceUndefinedAmount
-
- // TODO: Check semantics of this
- val referenceTimeslot = Timeslot(new MutableDateCalc(occurredDate).goPreviousMilli.toDate, occurredDate)
-
- // TODO: store policies in mem?
- val relevantPolicyOpt: Option[PolicyModel] = policyStore.loadValidPolicyAt(occurredMillis)
-
- val policyByTimeslot = relevantPolicyOpt match {
- case Some(relevantPolicy) ⇒
- SortedMap(referenceTimeslot -> relevantPolicy)
-
- case None ⇒
- throw new AquariumInternalError("No relevant policy found for %s".format(referenceTimeslot))
- }
-
- (referenceTimeslot, policyByTimeslot, previousValue)
- }
-
- val initialChargeslots = computeInitialChargeslots(
- referenceTimeslot,
- resourceType,
- policyByTimeslot,
- agreementByTimeslot,
- Some(clog)
- )
-
- val fullChargeslots = initialChargeslots.map {
- case chargeslot@Chargeslot(startMillis, stopMillis, unitPrice, _) ⇒
- val valueMap = chargingBehavior.makeValueMap(
- oldCredits,
- oldTotalAmount,
- newTotalAmount,
- stopMillis - startMillis,
- previousValue,
- currentResourceEvent.value,
- unitPrice
- )
-
- // clog.debug("execAlgorithm = %s", execAlgorithm)
- clog.debugMap("valueMap", valueMap, 1)
-
- // This is it
- val credits = SimpleExecutableChargingBehaviorAlgorithm.apply(valueMap)
- chargeslot.copyWithCredits(credits)
- }
-
- val result = referenceTimeslot -> fullChargeslots
-
- result
- }
-
- /**
* Given two lists of timeslots, produce a list which contains the
* set of timeslot slices, as those are defined by
* timeslot overlaps.
* 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
- }
+ private[this] def resolveEffectiveUnitPricesForTimeslot(
+ alignedTimeslot: Timeslot,
+ policy: PolicyModel,
+ agreement: UserAgreementModel,
+ resourceType: ResourceType,
+ effectivePriceTableSelector: FullPriceTable ⇒ EffectivePriceTable
+ ): PriceMap = {
- val effectivePriceTable = fullPriceTable.perResource.get(resourceType.name) match {
- case None ⇒
- throw new AquariumInternalError("Unknown resource type %s".format(role))
-
- case Some(effectivePriceTable) ⇒
- effectivePriceTable
- }
+ val fullPriceTable = agreement.computeFullPriceTable(policy)
+ val effectivePriceTable = effectivePriceTableSelector(fullPriceTable)
resolveEffective(alignedTimeslot, effectivePriceTable.priceOverrides)
+ //immutable.SortedMap(alignedTimeslot -> effectivePriceTable.priceOverrides.head)
}
private def printPriceList(p: PriceList) : Unit = {
}
private def resolveEffective(alignedTimeslot: Timeslot,p:PriceList): PriceMap = {
- Console.err.println("\n\nInput timeslot: " + alignedTimeslot + "\n\n")
- printPriceList(p)
+ //Console.err.println("\n\nInput timeslot: " + alignedTimeslot + "\n\n")
+ //printPriceList(p)
val ret = resolveEffective3(alignedTimeslot,p) //HERE
- printPriceMap(ret)
+ //printPriceMap(ret)
ret
}