working on user budget

This commit is contained in:
Radu Ioan Fericean
2012-02-20 21:33:47 +02:00
parent afbc9e451e
commit 9b35bb8ce3
7 changed files with 113 additions and 39 deletions

View File

@@ -28,7 +28,7 @@ func (ap *ActivationPeriod) AddInterval(is ...*Interval) {
Serializes the activation periods for the storage. Used for key-value storages.
*/
func (ap *ActivationPeriod) store() (result string) {
result += strconv.FormatInt(ap.ActivationTime.UnixNano(), 10) + ";"
result += strconv.FormatInt(ap.ActivationTime.UnixNano(), 10) + ";"
for _, i := range ap.Intervals {
var is string
is = strconv.Itoa(int(i.Month)) + "|"

View File

@@ -4,15 +4,17 @@ import (
"strings"
)
/*
Structure that gathers multiple destination prefixes under a common id.
*/
type Destination struct {
Id string
Prefixes []string
}
func (d *Destination) GetKey() (result string) {
return d.Id
}
/*
Serializes the destination for the storage. Used for key-value storages.
*/
func (d *Destination) store() (result string) {
for _, p := range d.Prefixes {
result += p + ","
@@ -26,7 +28,7 @@ func (d *Destination) restore(input string) {
}
/*
Returns true if the bucket contains specified prefix.
De-serializes the destination for the storage. Used for key-value storages.
*/
func (d *Destination) containsPrefix(prefix string) bool {
for _, p := range d.Prefixes {

View File

@@ -1,16 +1,19 @@
package timespans
type MinuteBucket struct {
Seconds int
Priority int
Price float64
Seconds int
Priority int
Price float64
DestinationId string
destination *Destination
destination *Destination
}
/*
Returns the destination loading it from the storage if necessary.
*/
func (mb *MinuteBucket) getDestination(storage StorageGetter) (dest *Destination) {
if mb.destination == nil {
mb.destination,_ = storage.GetDestination(mb.DestinationId)
mb.destination, _ = storage.GetDestination(mb.DestinationId)
}
return mb.destination
}

View File

@@ -11,12 +11,12 @@ these numbers to the user budget every month.
*/
type TariffPlan struct {
Id string
SmsCredit int
SmsCredit int
MinuteBuckets []*MinuteBucket
}
/*
Serializes the activation periods for the storage. Used for key-value storages.
Serializes the tariff plan for the storage. Used for key-value storages.
*/
func (tp *TariffPlan) store() (result string) {
result += strconv.Itoa(tp.SmsCredit) + ";"
@@ -32,17 +32,17 @@ func (tp *TariffPlan) store() (result string) {
}
/*
De-serializes the activation periods for the storage. Used for key-value storages.
De-serializes the tariff plan for the storage. Used for key-value storages.
*/
func (tp *TariffPlan) restore(input string) {
elements := strings.Split(input, ";")
tp.SmsCredit,_ = strconv.Atoi(elements[0])
tp.SmsCredit, _ = strconv.Atoi(elements[0])
for _, mbs := range elements[1 : len(elements)-1] {
mb := &MinuteBucket{}
mbse := strings.Split(mbs, "|")
mb.Seconds,_ = strconv.Atoi(mbse[0])
mb.Priority,_ = strconv.Atoi(mbse[1])
mb.Price,_ = strconv.ParseFloat(mbse[2], 64)
mb.Seconds, _ = strconv.Atoi(mbse[0])
mb.Priority, _ = strconv.Atoi(mbse[1])
mb.Price, _ = strconv.ParseFloat(mbse[2], 64)
mb.DestinationId = mbse[3]
tp.MinuteBuckets = append(tp.MinuteBuckets, mb)

View File

@@ -7,7 +7,7 @@ import (
func TestTariffPlanStoreRestore(t *testing.T) {
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.01, DestinationId: "nationale"}
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
seara := &TariffPlan{Id: "seara", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
seara := &TariffPlan{Id: "seara", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
s := seara.store()
tp1 := &TariffPlan{Id: "seara"}
tp1.restore(s)
@@ -21,7 +21,7 @@ func TestTariffPlanKyotoStore(t *testing.T) {
defer getter.Close()
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.01, DestinationId: "nationale"}
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
seara := &TariffPlan{Id: "seara", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
seara := &TariffPlan{Id: "seara", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
getter.SetTariffPlan(seara)
result, _ := getter.GetTariffPlan(seara.Id)
if result.SmsCredit != seara.SmsCredit || len(result.MinuteBuckets) != len(seara.MinuteBuckets) {
@@ -34,7 +34,7 @@ func TestTariffPlanRedisStore(t *testing.T) {
defer getter.Close()
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.01, DestinationId: "nationale"}
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
seara := &TariffPlan{Id: "seara", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
seara := &TariffPlan{Id: "seara", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
getter.SetTariffPlan(seara)
result, _ := getter.GetTariffPlan(seara.Id)
if result.SmsCredit != seara.SmsCredit || len(result.MinuteBuckets) != len(seara.MinuteBuckets) {
@@ -47,7 +47,7 @@ func TestTariffPlanMongoStore(t *testing.T) {
defer getter.Close()
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.01, DestinationId: "nationale"}
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
seara := &TariffPlan{Id: "seara", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
seara := &TariffPlan{Id: "seara", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
getter.SetTariffPlan(seara)
result, _ := getter.GetTariffPlan(seara.Id)
if result.SmsCredit != seara.SmsCredit || len(result.MinuteBuckets) != len(seara.MinuteBuckets) {
@@ -62,7 +62,7 @@ func BenchmarkTariffPlanKyotoStoreRestore(b *testing.B) {
defer getter.Close()
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.01, DestinationId: "nationale"}
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
seara := &TariffPlan{Id: "seara", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
seara := &TariffPlan{Id: "seara", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
for i := 0; i < b.N; i++ {
getter.SetTariffPlan(seara)
getter.GetTariffPlan(seara.Id)
@@ -74,7 +74,7 @@ func BenchmarkTariffPlanRedisStoreRestore(b *testing.B) {
defer getter.Close()
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.01, DestinationId: "nationale"}
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
seara := &TariffPlan{Id: "seara", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
seara := &TariffPlan{Id: "seara", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
for i := 0; i < b.N; i++ {
getter.SetTariffPlan(seara)
getter.GetTariffPlan(seara.Id)
@@ -86,7 +86,7 @@ func BenchmarkTariffPlanMongoStoreRestore(b *testing.B) {
defer getter.Close()
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.01, DestinationId: "nationale"}
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
seara := &TariffPlan{Id: "seara", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
seara := &TariffPlan{Id: "seara", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
for i := 0; i < b.N; i++ {
getter.SetTariffPlan(seara)
getter.GetTariffPlan(seara.Id)

View File

@@ -3,31 +3,85 @@ package timespans
import (
"log"
"math"
"strconv"
"strings"
)
/*
Structure conatining information about user's credit (minutes, cents, sms...).'
*/
type UserBudget struct {
id string
minuteBuckets []*MinuteBucket
credit float64
smsCredit int
Id string
Credit float64
SmsCredit int
ResetDayOfTheMonth int
TariffPlanId string
tariffPlan *TariffPlan
resetDayOfTheMonth int
MinuteBuckets []*MinuteBucket
}
/*
Serializes the user budget for the storage. Used for key-value storages.
*/
func (ub *UserBudget) store() (result string) {
result += strconv.FormatFloat(ub.Credit, 'f', -1, 64) + ";"
result += strconv.Itoa(ub.SmsCredit) + ";"
result += strconv.Itoa(ub.ResetDayOfTheMonth) + ";"
result += ub.TariffPlanId + ";"
for _, mb := range ub.MinuteBuckets {
var mbs string
mbs += strconv.Itoa(int(mb.Seconds)) + "|"
mbs += strconv.Itoa(int(mb.Priority)) + "|"
mbs += strconv.FormatFloat(mb.Price, 'f', -1, 64) + "|"
mbs += mb.DestinationId
result += mbs + ";"
}
return
}
/*
De-serializes the user budget for the storage. Used for key-value storages.
*/
func (ub *UserBudget) restore(input string) {
elements := strings.Split(input, ";")
ub.Credit, _ = strconv.ParseFloat(elements[0], 64)
ub.SmsCredit, _ = strconv.Atoi(elements[1])
ub.ResetDayOfTheMonth, _ = strconv.Atoi(elements[2])
ub.TariffPlanId = elements[3]
for _, mbs := range elements[4 : len(elements)-1] {
mb := &MinuteBucket{}
mbse := strings.Split(mbs, "|")
mb.Seconds, _ = strconv.Atoi(mbse[0])
mb.Priority, _ = strconv.Atoi(mbse[1])
mb.Price, _ = strconv.ParseFloat(mbse[2], 64)
mb.DestinationId = mbse[3]
ub.MinuteBuckets = append(ub.MinuteBuckets, mb)
}
}
/*
Returns the tariff plan loading it from the storage if necessary.
*/
func (ub *UserBudget) getTariffPlan(storage StorageGetter) (tp *TariffPlan) {
if ub.tariffPlan == nil {
ub.tariffPlan, _ = storage.GetTariffPlan(ub.TariffPlanId)
}
return ub.tariffPlan
}
/*
Returns user's avaliable minutes for the specified destination
*/
func (ub *UserBudget) GetSecondsForPrefix(storage StorageGetter, prefix string) (seconds int) {
if len(ub.minuteBuckets) == 0 {
log.Print("There are no minute buckets to check for user", ub.id)
if len(ub.MinuteBuckets) == 0 {
log.Print("There are no minute buckets to check for user", ub.Id)
return
}
bestBucket := ub.minuteBuckets[0]
bestBucket := ub.MinuteBuckets[0]
for _, mb := range ub.minuteBuckets {
for _, mb := range ub.MinuteBuckets {
d := mb.getDestination(storage)
if d.containsPrefix(prefix) && mb.Priority > bestBucket.Priority {
bestBucket = mb
@@ -35,7 +89,7 @@ func (ub *UserBudget) GetSecondsForPrefix(storage StorageGetter, prefix string)
}
seconds = bestBucket.Seconds
if bestBucket.Price > 0 {
seconds = int(math.Min(ub.credit/bestBucket.Price, float64(seconds)))
seconds = int(math.Min(ub.Credit/bestBucket.Price, float64(seconds)))
}
return
}

View File

@@ -2,6 +2,7 @@ package timespans
import (
"testing"
//"log"
)
var (
@@ -14,7 +15,7 @@ func TestGetSeconds(t *testing.T) {
b2 := &MinuteBucket{Seconds: 100, Priority: 20, destination: retea}
tf1 := &TariffPlan{MinuteBuckets: []*MinuteBucket{b1, b2}}
ub1 := &UserBudget{id: "rif", minuteBuckets: []*MinuteBucket{b1, b2}, credit: 200, tariffPlan: tf1, resetDayOfTheMonth: 10}
ub1 := &UserBudget{Id: "rif", MinuteBuckets: []*MinuteBucket{b1, b2}, Credit: 200, tariffPlan: tf1, ResetDayOfTheMonth: 10}
seconds := ub1.GetSecondsForPrefix(nil, "0723")
expected := 100
if seconds != expected {
@@ -27,7 +28,7 @@ func TestGetPricedSeconds(t *testing.T) {
b2 := &MinuteBucket{Seconds: 100, Price: 1, Priority: 20, destination: retea}
tf1 := &TariffPlan{MinuteBuckets: []*MinuteBucket{b1, b2}}
ub1 := &UserBudget{id: "rif", minuteBuckets: []*MinuteBucket{b1, b2}, credit: 21, tariffPlan: tf1, resetDayOfTheMonth: 10}
ub1 := &UserBudget{Id: "rif", MinuteBuckets: []*MinuteBucket{b1, b2}, Credit: 21, tariffPlan: tf1, ResetDayOfTheMonth: 10}
seconds := ub1.GetSecondsForPrefix(nil, "0723")
expected := 21
if seconds != expected {
@@ -35,6 +36,20 @@ func TestGetPricedSeconds(t *testing.T) {
}
}
func TestUserBudgetStoreRestore(t *testing.T) {
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.01, DestinationId: "nationale"}
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
seara := &TariffPlan{Id: "seara", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
rifsBudget := &UserBudget{Id: "rif", MinuteBuckets: []*MinuteBucket{b1, b2}, Credit: 21, tariffPlan: seara, ResetDayOfTheMonth: 10}
s := rifsBudget.store()
ub1 := &UserBudget{Id: "rif"}
ub1.restore(s)
if ub1.store() != s {
t.Errorf("Expected %q was %q", s, ub1.store())
}
}
/*********************************** Benchmarks *******************************/
func BenchmarkGetSecondForPrefix(b *testing.B) {
@@ -43,9 +58,9 @@ func BenchmarkGetSecondForPrefix(b *testing.B) {
b2 := &MinuteBucket{Seconds: 100, Price: 1, Priority: 20, destination: retea}
tf1 := &TariffPlan{MinuteBuckets: []*MinuteBucket{b1, b2}}
ub1 := &UserBudget{id: "rif", minuteBuckets: []*MinuteBucket{b1, b2}, credit: 21, tariffPlan: tf1, resetDayOfTheMonth: 10}
ub1 := &UserBudget{Id: "rif", MinuteBuckets: []*MinuteBucket{b1, b2}, Credit: 21, tariffPlan: tf1, ResetDayOfTheMonth: 10}
b.StartTimer()
for i := 0; i < b.N; i++ {
ub1.GetSecondsForPrefix(nil,"0723")
ub1.GetSecondsForPrefix(nil, "0723")
}
}