WIP Resource event handling
[aquarium] / src / main / scala / gr / grnet / aquarium / computation / reason / UserStateChangeReason.scala
1 /*
2  * Copyright 2011-2012 GRNET S.A. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or
5  * without modification, are permitted provided that the following
6  * conditions are met:
7  *
8  *   1. Redistributions of source code must retain the above
9  *      copyright notice, this list of conditions and the following
10  *      disclaimer.
11  *
12  *   2. Redistributions in binary form must reproduce the above
13  *      copyright notice, this list of conditions and the following
14  *      disclaimer in the documentation and/or other materials
15  *      provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  * The views and conclusions contained in the software and
31  * documentation are those of the authors and should not be
32  * interpreted as representing official policies, either expressed
33  * or implied, of GRNET S.A.
34  */
35
36 package gr.grnet.aquarium.computation.reason
37
38 import gr.grnet.aquarium.computation.BillingMonthInfo
39 import gr.grnet.aquarium.event.model.im.IMEventModel
40 import gr.grnet.aquarium.util.shortClassNameOf
41
42 /**
43  * Provides information explaining the reason Aquarium calculated a new
44  * [[gr.grnet.aquarium.computation.state.UserState]].
45  */
46 case class UserStateChangeReason(
47     details: Map[String, String],
48     billingMonthInfo: Option[BillingMonthInfo],
49     parentReason: Option[UserStateChangeReason]
50 ) {
51
52   require(
53     details.contains(UserStateChangeReason.Names.name),
54     "No name present in the details of %s".format(shortClassNameOf(this))
55   )
56
57   private[this] def booleanFromDetails(name: String, default: Boolean) = {
58     details.get(name) match {
59       case Some(value) ⇒
60         value.toBoolean
61
62       case _ ⇒
63         false
64     }
65   }
66
67    /**
68     * Return `true` if the result of the calculation should be stored back to the
69     * [[gr.grnet.aquarium.store.UserStateStore]].
70     *
71     */
72 //  def shouldStoreUserState: Boolean =
73 //     booleanFromDetails(UserStateChangeReason.Names.shouldStoreUserState, false)
74
75   def shouldStoreCalculatedWalletEntries: Boolean =
76     booleanFromDetails(UserStateChangeReason.Names.shouldStoreCalculatedWalletEntries, false)
77
78   def calculateCreditsForImplicitlyTerminated: Boolean =
79     booleanFromDetails(UserStateChangeReason.Names.calculateCreditsForImplicitlyTerminated, false)
80
81   def forBillingMonthInfo(bmi: BillingMonthInfo) = {
82     copy(
83       parentReason = Some(this),
84       billingMonthInfo = Some(bmi)
85     )
86   }
87
88   def name: String = {
89     // This must be always present
90     details.get(UserStateChangeReason.Names.name).getOrElse("<unknown>")
91   }
92 }
93
94 object UserStateChangeReason {
95   object Names {
96     final val name = "name"
97
98     final val imEvent = "imEvent"
99     final val forWhenMillis = "forWhenMillis"
100
101     final val shouldStoreUserState = "shouldStoreUserState"
102     final val shouldStoreCalculatedWalletEntries = "shouldStoreCalculatedWalletEntries"
103     final val calculateCreditsForImplicitlyTerminated = "calculateCreditsForImplicitlyTerminated"
104   }
105 }
106
107 sealed trait UserStateChangeReason_ {
108   def originalReason: UserStateChangeReason_
109   /**
110     * Return `true` if the result of the calculation should be stored back to the
111     * [[gr.grnet.aquarium.store.UserStateStore]].
112     *
113     */
114   def shouldStoreUserState: Boolean
115
116   def shouldStoreCalculatedWalletEntries: Boolean
117
118   def forPreviousBillingMonth: UserStateChangeReason_
119
120   def calculateCreditsForImplicitlyTerminated: Boolean
121
122   def code: UserStateChangeReasonCodes.ChangeReasonCode
123 }
124
125 /**
126  * Used when the very first user state is saved.
127  */
128 object InitialUserStateSetup {
129   def name = "InitialUserStateSetup"
130
131   /**
132    * When the user state is initially set up.
133    */
134   def apply(parentReason: Option[UserStateChangeReason]) = {
135     UserStateChangeReason(
136       Map(
137         UserStateChangeReason.Names.name -> name//,
138 //        UserStateChangeReason.Names.shouldStoreUserState -> true
139       ),
140       None,
141       parentReason
142     )
143   }
144 }
145
146 object InitialUserActorSetup {
147   def name = "InitialUserActorSetup"
148
149   /**
150    * When the user processing unit (actor) is initially set up.
151    */
152   def apply() = {
153     UserStateChangeReason(
154       Map(
155         UserStateChangeReason.Names.name -> name//,
156 //        UserStateChangeReason.Names.shouldStoreUserState -> true
157       ),
158       None,
159       None
160     )
161   }
162 }
163
164 object NoSpecificChangeReason {
165   def name = "NoSpecificChangeReason"
166
167   /**
168    * A calculation made for no specific reason. Can be for testing, for example.
169    */
170   def apply() = {
171     UserStateChangeReason(
172       Map(
173         UserStateChangeReason.Names.name -> name
174       ),
175       None,
176       None
177     )
178   }
179 }
180
181 object MonthlyBillingCalculation {
182   def name = "MonthlyBillingCalculation"
183
184   /**
185    * An authoritative calculation for the billing period.
186    */
187   def apply(parentReason: UserStateChangeReason, billingMongthInfo: BillingMonthInfo) = {
188     UserStateChangeReason(
189       Map(
190         UserStateChangeReason.Names.name -> name,
191 //        UserStateChangeReason.Names.shouldStoreUserState -> true,
192         UserStateChangeReason.Names.shouldStoreCalculatedWalletEntries -> true.toString,
193         UserStateChangeReason.Names.calculateCreditsForImplicitlyTerminated -> true.toString
194       ),
195       Some(billingMongthInfo),
196       Some(parentReason)
197     )
198   }
199 }
200
201 object RealtimeBillingCalculation {
202   def name = "RealtimeBillingCalculation"
203
204   /**
205    * Used for the real-time billing calculation.
206    */
207   def apply(parentReason: Option[UserStateChangeReason], forWhenMillis: Long) = {
208     UserStateChangeReason(
209       Map(
210         UserStateChangeReason.Names.name -> name,
211         UserStateChangeReason.Names.forWhenMillis -> forWhenMillis.toString
212       ),
213       None,
214       parentReason
215     )
216   }
217 }
218
219 object IMEventArrival {
220   def name = "IMEventArrival"
221
222   def apply(imEvent: IMEventModel) = {
223     UserStateChangeReason(
224       Map(
225         UserStateChangeReason.Names.name -> name,
226         UserStateChangeReason.Names.imEvent -> imEvent.toJsonString//,
227 //        UserStateChangeReason.Names.shouldStoreUserState -> true
228       ),
229       None,
230       None
231     )
232   }
233 }