X-Git-Url: https://code.grnet.gr/git/aquarium/blobdiff_plain/d86318893693d169649d3324d5adc2453eef73b2..refs/heads/selectors:/src/main/scala/gr/grnet/aquarium/computation/TimeslotComputations.scala diff --git a/src/main/scala/gr/grnet/aquarium/computation/TimeslotComputations.scala b/src/main/scala/gr/grnet/aquarium/computation/TimeslotComputations.scala index 33a1055..c0da20d 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/TimeslotComputations.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/TimeslotComputations.scala @@ -36,15 +36,16 @@ package gr.grnet.aquarium.computation import collection.immutable.SortedMap -import com.ckkloverdos.maybe.{NoVal, Maybe, Just} +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.{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.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. @@ -52,9 +53,7 @@ import gr.grnet.aquarium.policy.{EffectivePriceTable, PolicyModel, ResourceType, * @author Georgios Gousios * @author Christos KK Loverdos */ -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. @@ -92,21 +91,23 @@ trait TimeslotComputations extends Loggable { 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() - dslUtils.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] = { @@ -123,6 +124,7 @@ trait TimeslotComputations extends Loggable { } // 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 @@ -132,14 +134,20 @@ trait TimeslotComputations extends Loggable { 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 @@ -161,105 +169,6 @@ trait TimeslotComputations extends Loggable { } /** - * Compute the charge slots generated by a particular resource event. - * - */ - def computeFullChargeslots( - 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 = resourceType.chargingBehavior - - 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. @@ -299,4 +208,69 @@ trait TimeslotComputations extends Loggable { a.head :: alignTimeslots(safeTail(a), safeTail(b)) } } + + type PriceMap = immutable.SortedMap[Timeslot, EffectiveUnitPrice] + private 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[this] def resolveEffectiveUnitPricesForTimeslot( + alignedTimeslot: Timeslot, + policy: PolicyModel, + agreement: UserAgreementModel, + resourceType: ResourceType, + effectivePriceTableSelector: FullPriceTable ⇒ EffectivePriceTable + ): PriceMap = { + + 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 = { + 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 + } }