From dcea9a3a8ff0c23444d96370553076590f353525 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 22 Feb 2012 19:33:36 +0200 Subject: [PATCH] started created timespans for minute buckets --- cmd/rater/rater.go | 3 +- timespans/calldesc.go | 67 +++++++++++++++++++------------ timespans/calldesc_test.go | 78 ++++++++++++++++++------------------ timespans/minute_buckets.go | 12 +++++- timespans/tariff_plans.go | 2 +- timespans/timespans.go | 20 +++++++++ timespans/userbudget.go | 24 +++++------ timespans/userbudget_test.go | 8 ++-- 8 files changed, 130 insertions(+), 84 deletions(-) diff --git a/cmd/rater/rater.go b/cmd/rater/rater.go index ebcc86554..39bdb480f 100644 --- a/cmd/rater/rater.go +++ b/cmd/rater/rater.go @@ -28,7 +28,8 @@ RPC method providing the rating information from the storage. */ func (s *Storage) GetCost(cd timespans.CallDescriptor, reply *timespans.CallCost) (err error) { descriptor := &cd - r, e := descriptor.GetCost(s.sg) + descriptor.storageGetter = s.sg + r, e := descriptor.GetCost() *reply, err = *r, e return nil } diff --git a/timespans/calldesc.go b/timespans/calldesc.go index 8ed49c6c7..16fe519c6 100644 --- a/timespans/calldesc.go +++ b/timespans/calldesc.go @@ -37,6 +37,7 @@ type CallDescriptor struct { CstmId, Subject, DestinationPrefix string TimeStart, TimeEnd time.Time ActivationPeriods []*ActivationPeriod + storageGetter StorageGetter } /* @@ -51,15 +52,15 @@ func (cd *CallDescriptor) AddActivationPeriod(aps ...*ActivationPeriod) { /* Restores the activation periods from storage. */ -func (cd *CallDescriptor) RestoreFromStorage(sg StorageGetter) (destPrefix string, err error) { +func (cd *CallDescriptor) RestoreFromStorage() (destPrefix string, err error) { cd.ActivationPeriods = make([]*ActivationPeriod, 0) base := fmt.Sprintf("%s:%s:", cd.CstmId, cd.Subject) destPrefix = cd.DestinationPrefix key := base + destPrefix - values, err := sg.GetActivationPeriods(key) + values, err := cd.storageGetter.GetActivationPeriods(key) //get for a smaller prefix if the orignal one was not found - for i := len(cd.DestinationPrefix); err != nil && i >= MinPrefixLength; values, err = sg.GetActivationPeriods(key) { + for i := len(cd.DestinationPrefix); err != nil && i >= MinPrefixLength; values, err = cd.storageGetter.GetActivationPeriods(key) { i-- destPrefix = cd.DestinationPrefix[:i] key = base + destPrefix @@ -90,6 +91,24 @@ func (cd *CallDescriptor) splitInTimeSpans() (timespans []*TimeSpan) { Splits the received timespan into sub time spans according to the activation periods intervals. */ func (cd *CallDescriptor) splitTimeSpan(firstSpan *TimeSpan) (timespans []*TimeSpan) { + timespans = append(timespans, firstSpan) + // split on (free) minute buckets + if userBudget, err := cd.storageGetter.GetUserBudget(cd.Subject); err == nil { + _, bucketList := userBudget.getSecondsForPrefix(cd.storageGetter, cd.DestinationPrefix) + for _, mb := range bucketList { + for i := 0; i < len(timespans); i++ { + newTs := timespans[i].SplitByMinuteBucket(mb) + if newTs != nil { + timespans = append(timespans, newTs) + firstSpan = newTs // we move the firstspan to the newly created one for further spliting + break + } + } + } + } + if firstSpan.MinuteBucket != nil { + return // all the timespans are on minutes + } if len(cd.ActivationPeriods) == 0 { log.Print("Nothing to split, move along... ", cd) return @@ -97,7 +116,6 @@ func (cd *CallDescriptor) splitTimeSpan(firstSpan *TimeSpan) (timespans []*TimeS firstSpan.ActivationPeriod = cd.ActivationPeriods[0] // split on activation periods - timespans = append(timespans, firstSpan) afterStart, afterEnd := false, false //optimization for multiple activation periods for _, ap := range cd.ActivationPeriods { if !afterStart && !afterEnd && ap.ActivationTime.Before(cd.TimeStart) { @@ -105,6 +123,9 @@ func (cd *CallDescriptor) splitTimeSpan(firstSpan *TimeSpan) (timespans []*TimeS } else { afterStart = true for i := 0; i < len(timespans); i++ { + if timespans[i].MinuteBucket != nil { + continue + } newTs := timespans[i].SplitByActivationPeriod(ap) if newTs != nil { timespans = append(timespans, newTs) @@ -118,6 +139,9 @@ func (cd *CallDescriptor) splitTimeSpan(firstSpan *TimeSpan) (timespans []*TimeS // split on price intervals for i := 0; i < len(timespans); i++ { ap := timespans[i].ActivationPeriod + if timespans[i].MinuteBucket != nil { + continue + } //timespans[i].ActivationPeriod = nil for _, interval := range ap.Intervals { newTs := timespans[i].SplitByInterval(interval) @@ -133,33 +157,26 @@ func (cd *CallDescriptor) splitTimeSpan(firstSpan *TimeSpan) (timespans []*TimeS /* Creates a CallCost structure with the cost nformation calculated for the received CallDescriptor. */ -func (cd *CallDescriptor) GetCost(sg StorageGetter) (*CallCost, error) { - destPrefix, err := cd.RestoreFromStorage(sg) - cc := &CallCost{TOR: cd.TOR, CstmId: cd.CstmId, Subject: cd.Subject, DestinationPrefix: destPrefix} - - if userBudget, err := sg.GetUserBudget(cd.Subject); err == nil { - nbSeconds := cd.TimeEnd.Sub(cd.TimeStart).Seconds() - log.Print("seconds: ", nbSeconds) - avaliableNbSeconds := userBudget.getSecondsForPrefix(sg, cd.DestinationPrefix) - log.Print("available: ", avaliableNbSeconds) - if nbSeconds < avaliableNbSeconds { - cc.Cost, cc.ConnectFee = 0, 0 - return cc, nil - } - } +func (cd *CallDescriptor) GetCost() (*CallCost, error) { + destPrefix, err := cd.RestoreFromStorage() timespans := cd.splitInTimeSpans() cost := 0.0 connectionFee := 0.0 for i, ts := range timespans { - if i == 0 { + if ts.MinuteBucket == nil && i == 0 { connectionFee = ts.Interval.ConnectFee } cost += ts.GetCost() } - - cc.Cost, cc.ConnectFee, cc.Timespans = cost, connectionFee, timespans + cc := &CallCost{TOR: cd.TOR, + CstmId: cd.CstmId, + Subject: cd.Subject, + DestinationPrefix: destPrefix, + Cost: cost, + ConnectFee: connectionFee, + Timespans: timespans} return cc, err } @@ -167,8 +184,8 @@ func (cd *CallDescriptor) GetCost(sg StorageGetter) (*CallCost, error) { /* Returns the cost of a second in the present time conditions. */ -func (cd *CallDescriptor) getPresentSecondCost(sg StorageGetter) (cost float64, err error) { - _, err = cd.RestoreFromStorage(sg) +func (cd *CallDescriptor) getPresentSecondCost() (cost float64, err error) { + _, err = cd.RestoreFromStorage() now := time.Now() oneSecond, _ := time.ParseDuration("1s") ts := &TimeSpan{TimeStart: now, TimeEnd: now.Add(oneSecond)} @@ -183,8 +200,8 @@ func (cd *CallDescriptor) getPresentSecondCost(sg StorageGetter) (cost float64, /* Returns the cost of a second in the present time conditions. */ -func (cd *CallDescriptor) GetMaxSessionTime(sg StorageGetter, maxSessionSeconds int) (seconds int, err error) { - _, err = cd.RestoreFromStorage(sg) +func (cd *CallDescriptor) GetMaxSessionTime(maxSessionSeconds int) (seconds int, err error) { + _, err = cd.RestoreFromStorage() now := time.Now() maxDuration, _ := time.ParseDuration(fmt.Sprintf("%ds", maxSessionSeconds)) ts := &TimeSpan{TimeStart: now, TimeEnd: now.Add(maxDuration)} diff --git a/timespans/calldesc_test.go b/timespans/calldesc_test.go index be1f82642..4e9c3049c 100644 --- a/timespans/calldesc_test.go +++ b/timespans/calldesc_test.go @@ -12,9 +12,9 @@ func TestKyotoSplitSpans(t *testing.T) { t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2} + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter} - cd.RestoreFromStorage(getter) + cd.RestoreFromStorage() timespans := cd.splitInTimeSpans() if len(timespans) != 2 { t.Error("Wrong number of timespans: ", len(timespans)) @@ -27,8 +27,8 @@ func TestRedisSplitSpans(t *testing.T) { t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", TimeStart: t1, TimeEnd: t2} - cd.RestoreFromStorage(getter) + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", TimeStart: t1, TimeEnd: t2, storageGetter: getter} + cd.RestoreFromStorage() timespans := cd.splitInTimeSpans() if len(timespans) != 2 { @@ -42,14 +42,14 @@ func TestKyotoGetCost(t *testing.T) { t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2} - result, _ := cd.GetCost(getter) + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter} + result, _ := cd.GetCost() expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", Cost: 540, ConnectFee: 0} if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee { t.Errorf("Expected %v was %v", expected, result) } - cd = &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", TimeStart: t1, TimeEnd: t2} - result, _ = cd.GetCost(getter) + cd = &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", TimeStart: t1, TimeEnd: t2, storageGetter: getter} + result, _ = cd.GetCost() expected = &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", Cost: 540, ConnectFee: 0} } @@ -59,8 +59,8 @@ func TestRedisGetCost(t *testing.T) { t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2} - result, _ := cd.GetCost(getter) + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter} + result, _ := cd.GetCost() expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", Cost: 540, ConnectFee: 0} if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee { t.Errorf("Expected %v was %v", expected, result) @@ -73,8 +73,8 @@ func TestMongoGetCost(t *testing.T) { t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2} - result, _ := cd.GetCost(getter) + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter} + result, _ := cd.GetCost() expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", Cost: 540, ConnectFee: 0} if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee { t.Errorf("Expected %v was %v", expected, result) @@ -87,8 +87,8 @@ func TestFullDestNotFound(t *testing.T) { t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256308200", TimeStart: t1, TimeEnd: t2} - result, _ := cd.GetCost(getter) + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256308200", TimeStart: t1, TimeEnd: t2, storageGetter: getter} + result, _ := cd.GetCost() expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", Cost: 540, ConnectFee: 0} if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee { t.Errorf("Expected %v was %v", expected, result) @@ -101,8 +101,8 @@ func TestMultipleActivationPeriods(t *testing.T) { t1 := time.Date(2012, time.February, 8, 17, 30, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 8, 18, 30, 0, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257308200", TimeStart: t1, TimeEnd: t2} - result, _ := cd.GetCost(getter) + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257308200", TimeStart: t1, TimeEnd: t2, storageGetter: getter} + result, _ := cd.GetCost() expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", Cost: 330, ConnectFee: 0} if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee { t.Log(result.Timespans[0].ActivationPeriod) @@ -117,8 +117,8 @@ func TestSpansMultipleActivationPeriods(t *testing.T) { t1 := time.Date(2012, time.February, 7, 23, 50, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 8, 0, 30, 0, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257308200", TimeStart: t1, TimeEnd: t2} - result, _ := cd.GetCost(getter) + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257308200", TimeStart: t1, TimeEnd: t2, storageGetter: getter} + result, _ := cd.GetCost() expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", Cost: 360, ConnectFee: 0} if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee { t.Errorf("Expected %v was %v", expected, result) @@ -131,8 +131,8 @@ func TestLessThanAMinute(t *testing.T) { t1 := time.Date(2012, time.February, 8, 23, 50, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 8, 23, 50, 30, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257308200", TimeStart: t1, TimeEnd: t2} - result, _ := cd.GetCost(getter) + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257308200", TimeStart: t1, TimeEnd: t2, storageGetter: getter} + result, _ := cd.GetCost() expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", Cost: 0.5, ConnectFee: 0} if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee { t.Errorf("Expected %v was %v", expected, result) @@ -145,8 +145,8 @@ func TestUniquePrice(t *testing.T) { t1 := time.Date(2012, time.February, 8, 22, 50, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 8, 23, 50, 21, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0723045326", TimeStart: t1, TimeEnd: t2} - result, _ := cd.GetCost(getter) + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0723045326", TimeStart: t1, TimeEnd: t2, storageGetter: getter} + result, _ := cd.GetCost() expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0723", Cost: 60.35, ConnectFee: 0} if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee { t.Errorf("Expected %v was %v", expected, result) @@ -159,8 +159,8 @@ func TestPresentSecodCost(t *testing.T) { t1 := time.Date(2012, time.February, 8, 22, 50, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 8, 23, 50, 21, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0723", TimeStart: t1, TimeEnd: t2} - result, _ := cd.getPresentSecondCost(getter) + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0723", TimeStart: t1, TimeEnd: t2, storageGetter: getter} + result, _ := cd.getPresentSecondCost() expected := 0.016 if result != expected { t.Errorf("Expected %v was %v", expected, result) @@ -173,8 +173,8 @@ func TestMinutesCost(t *testing.T) { t1 := time.Date(2012, time.February, 8, 22, 50, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 8, 23, 50, 21, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0723", TimeStart: t1, TimeEnd: t2} - result, _ := cd.GetCost(getter) + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0723", TimeStart: t1, TimeEnd: t2, storageGetter: getter} + result, _ := cd.GetCost() expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0723", Cost: 0, ConnectFee: 0} if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee { t.Errorf("Expected %v was %v", expected, result) @@ -203,10 +203,10 @@ func BenchmarkRedisRestoring(b *testing.B) { t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2} + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter} b.StartTimer() for i := 0; i < b.N; i++ { - cd.RestoreFromStorage(getter) + cd.RestoreFromStorage() } } @@ -217,10 +217,10 @@ func BenchmarkRedisGetCost(b *testing.B) { t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2} + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter} b.StartTimer() for i := 0; i < b.N; i++ { - cd.GetCost(getter) + cd.GetCost() } } @@ -231,7 +231,7 @@ func BenchmarkKyotoGetting(b *testing.B) { t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2} + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter} b.StartTimer() for i := 0; i < b.N; i++ { key := cd.GetKey() @@ -246,10 +246,10 @@ func BenchmarkKyotoRestoring(b *testing.B) { t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2} + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter} b.StartTimer() for i := 0; i < b.N; i++ { - cd.RestoreFromStorage(getter) + cd.RestoreFromStorage() } } @@ -260,8 +260,8 @@ func BenchmarkSplitting(b *testing.B) { t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2} - cd.RestoreFromStorage(getter) + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter} + cd.RestoreFromStorage() b.StartTimer() for i := 0; i < b.N; i++ { cd.splitInTimeSpans() @@ -275,10 +275,10 @@ func BenchmarkKyotoGetCost(b *testing.B) { t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2} + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter} b.StartTimer() for i := 0; i < b.N; i++ { - cd.GetCost(getter) + cd.GetCost() } } @@ -303,9 +303,9 @@ func BenchmarkMongoGetCost(b *testing.B) { t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) - cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2} + cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter} b.StartTimer() for i := 0; i < b.N; i++ { - cd.GetCost(getter) + cd.GetCost() } } diff --git a/timespans/minute_buckets.go b/timespans/minute_buckets.go index c9997630a..0812a0a00 100644 --- a/timespans/minute_buckets.go +++ b/timespans/minute_buckets.go @@ -1,7 +1,9 @@ package timespans +import "math" + type MinuteBucket struct { - Seconds int + Seconds float64 Priority int Price float64 DestinationId string @@ -18,3 +20,11 @@ func (mb *MinuteBucket) getDestination(storage StorageGetter) (dest *Destination } return mb.destination } + +func (mb *MinuteBucket) GetSecondsForCredit(credit float64) (seconds float64) { + seconds = mb.Seconds + if mb.Price > 0 { + seconds = math.Min(credit/mb.Price, mb.Seconds) + } + return +} diff --git a/timespans/tariff_plans.go b/timespans/tariff_plans.go index 1eb9f84fe..ffa6d4544 100644 --- a/timespans/tariff_plans.go +++ b/timespans/tariff_plans.go @@ -40,7 +40,7 @@ func (tp *TariffPlan) restore(input string) { for _, mbs := range elements[1 : len(elements)-1] { mb := &MinuteBucket{} mbse := strings.Split(mbs, "|") - mb.Seconds, _ = strconv.Atoi(mbse[0]) + mb.Seconds, _ = strconv.ParseFloat(mbse[0], 64) mb.Priority, _ = strconv.Atoi(mbse[1]) mb.Price, _ = strconv.ParseFloat(mbse[2], 64) mb.DestinationId = mbse[3] diff --git a/timespans/timespans.go b/timespans/timespans.go index 537fa8e5f..e429f028f 100644 --- a/timespans/timespans.go +++ b/timespans/timespans.go @@ -2,6 +2,7 @@ package timespans import ( "time" + "fmt" //"log" ) @@ -12,6 +13,7 @@ type TimeSpan struct { TimeStart, TimeEnd time.Time ActivationPeriod *ActivationPeriod Interval *Interval + MinuteBucket *MinuteBucket } /* @@ -110,3 +112,21 @@ func (ts *TimeSpan) SplitByActivationPeriod(ap *ActivationPeriod) (newTs *TimeSp ts.TimeEnd = ap.ActivationTime return } + +/* +Splits the given timespan on activation period's activation time. +*/ +func (ts *TimeSpan) SplitByMinuteBucket(mb *MinuteBucket) (newTs *TimeSpan) { + ts.MinuteBucket = mb + s := ts.GetDuration().Seconds() + if s <= mb.Seconds { + mb.Seconds -= s + return nil + } + secDuration,_ := time.ParseDuration(fmt.Sprintf("%ds", mb.Seconds)) + newTimeEnd := ts.TimeStart.Add(secDuration) + newTs = &TimeSpan{TimeStart: newTimeEnd, TimeEnd: ts.TimeEnd} + ts.TimeEnd = newTimeEnd + mb.Seconds = 0 + return +} diff --git a/timespans/userbudget.go b/timespans/userbudget.go index 00f0b4452..f1449c760 100644 --- a/timespans/userbudget.go +++ b/timespans/userbudget.go @@ -2,9 +2,8 @@ package timespans import ( "log" - "math" - "strconv" "sort" + "strconv" "strings" ) @@ -37,7 +36,7 @@ func (bs BucketSorter) Swap(i, j int) { func (bs BucketSorter) Less(j, i int) bool { return bs[i].Priority < bs[j].Priority || bs[i].precision < bs[j].precision || - bs[i].Price < bs[j].Price + bs[i].Price > bs[j].Price } /* @@ -71,7 +70,7 @@ func (ub *UserBudget) restore(input string) { for _, mbs := range elements[4 : len(elements)-1] { mb := &MinuteBucket{} mbse := strings.Split(mbs, "|") - mb.Seconds, _ = strconv.Atoi(mbse[0]) + mb.Seconds, _ = strconv.ParseFloat(mbse[0], 64) mb.Priority, _ = strconv.Atoi(mbse[1]) mb.Price, _ = strconv.ParseFloat(mbse[2], 64) mb.DestinationId = mbse[3] @@ -93,12 +92,12 @@ func (ub *UserBudget) getTariffPlan(storage StorageGetter) (tp *TariffPlan) { /* Returns user's avaliable minutes for the specified destination */ -func (ub *UserBudget) getSecondsForPrefix(storage StorageGetter, prefix string) (seconds float64) { +func (ub *UserBudget) getSecondsForPrefix(storage StorageGetter, prefix string) (seconds float64, bucketList BucketSorter) { if len(ub.MinuteBuckets) == 0 { log.Print("There are no minute buckets to check for user", ub.Id) return } - var bucketList BucketSorter + for _, mb := range ub.MinuteBuckets { d := mb.getDestination(storage) if d == nil { @@ -107,17 +106,16 @@ func (ub *UserBudget) getSecondsForPrefix(storage StorageGetter, prefix string) contains, precision := d.containsPrefix(prefix) if contains { mb.precision = precision - bucketList = append(bucketList, mb) + if mb.Seconds > 0 { + bucketList = append(bucketList, mb) + } } } - sort.Sort(bucketList) + sort.Sort(bucketList) // sorts the buckets according to priority, precision or price credit := ub.Credit for _, mb := range bucketList { - s := float64(mb.Seconds) - if mb.Price > 0 { - s = math.Min(credit/mb.Price, s) - credit -= s - } + s := mb.GetSecondsForCredit(credit) + credit -= s seconds += s } return diff --git a/timespans/userbudget_test.go b/timespans/userbudget_test.go index 9816cc68a..586aefa31 100644 --- a/timespans/userbudget_test.go +++ b/timespans/userbudget_test.go @@ -16,9 +16,9 @@ func TestGetSeconds(t *testing.T) { tf1 := &TariffPlan{MinuteBuckets: []*MinuteBucket{b1, b2}} ub1 := &UserBudget{Id: "rif", MinuteBuckets: []*MinuteBucket{b1, b2}, Credit: 200, tariffPlan: tf1, ResetDayOfTheMonth: 10} - seconds := ub1.getSecondsForPrefix(nil, "0723") + seconds, bucketList := ub1.getSecondsForPrefix(nil, "0723") expected := 110.0 - if seconds != expected { + if seconds != expected || bucketList[0].Priority < bucketList[1].Priority { t.Errorf("Expected %v was %v", expected, seconds) } } @@ -29,9 +29,9 @@ func TestGetPricedSeconds(t *testing.T) { tf1 := &TariffPlan{MinuteBuckets: []*MinuteBucket{b1, b2}} ub1 := &UserBudget{Id: "rif", MinuteBuckets: []*MinuteBucket{b1, b2}, Credit: 21, tariffPlan: tf1, ResetDayOfTheMonth: 10} - seconds := ub1.getSecondsForPrefix(nil, "0723") + seconds, bucketList := ub1.getSecondsForPrefix(nil, "0723") expected := 21.0 - if seconds != expected { + if seconds != expected || bucketList[0].Priority < bucketList[1].Priority { t.Errorf("Expected %v was %v", expected, seconds) } }