From bafa9d9802e0198d28b78bb924d3b7997abfc4df Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 12 Apr 2016 22:33:54 +0300 Subject: [PATCH] improve rating selection with splitting on days --- engine/calldesc.go | 21 ++++++++++++++++++-- engine/calldesc_test.go | 37 ++++++++++++++++++++++++++++++++++++ engine/ratingplan.go | 7 +------ engine/ratingprofile_test.go | 35 ++++++++++++++++++++++++++++++++++ engine/timespans.go | 22 +++++++++++++++++++++ 5 files changed, 114 insertions(+), 8 deletions(-) diff --git a/engine/calldesc.go b/engine/calldesc.go index fca3a671d..d6b98f57e 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -362,6 +362,7 @@ func (cd *CallDescriptor) splitInTimeSpans() (timespans []*TimeSpan) { // split on rating plans afterStart, afterEnd := false, false //optimization for multiple activation periods for _, rp := range cd.RatingInfos { + //log.Print("RP: ", utils.ToJSON(rp)) if !afterStart && !afterEnd && rp.ActivationTime.Before(cd.TimeStart) { firstSpan.setRatingInfo(rp) } else { @@ -369,7 +370,6 @@ func (cd *CallDescriptor) splitInTimeSpans() (timespans []*TimeSpan) { for i := 0; i < len(timespans); i++ { newTs := timespans[i].SplitByRatingPlan(rp) if newTs != nil { - //log.Print("NEW TS", newTs.TimeStart) timespans = append(timespans, newTs) } else { afterEnd = true @@ -379,7 +379,22 @@ func (cd *CallDescriptor) splitInTimeSpans() (timespans []*TimeSpan) { } } } - // utils.Logger.Debug(fmt.Sprintf("After SplitByRatingPlan: %+v", timespans)) + //log.Printf("After SplitByRatingPlan: %+v", utils.ToJSON(timespans)) + // split on days + for i := 0; i < len(timespans); i++ { + rp := timespans[i].ratingInfo + newTs := timespans[i].SplitByDay() + if newTs != nil { + //log.Print("NEW TS: ", newTs.TimeStart, newTs.TimeEnd) + newTs.setRatingInfo(rp) + // insert the new timespan + index := i + 1 + timespans = append(timespans, nil) + copy(timespans[index+1:], timespans[index:]) + timespans[index] = newTs + } + } + //log.Printf("After SplitByDay: %+v", utils.ToJSON(timespans)) // split on rate intervals for i := 0; i < len(timespans); i++ { //log.Printf("==============%v==================", i) @@ -388,6 +403,7 @@ func (cd *CallDescriptor) splitInTimeSpans() (timespans []*TimeSpan) { // utils.Logger.Debug(fmt.Sprintf("rp: %+v", rp)) //timespans[i].RatingPlan = nil rateIntervals := rp.SelectRatingIntevalsForTimespan(timespans[i]) + //log.Print("RIs: ", utils.ToJSON(rateIntervals)) /*for _, interval := range rp.RateIntervals { if !timespans[i].hasBetterRateIntervalThan(interval) { timespans[i].SetRateInterval(interval) @@ -520,6 +536,7 @@ func (cd *CallDescriptor) getCost() (*CallCost, error) { cd.TOR = utils.VOICE } err := cd.LoadRatingPlans() + //log.Print("RI: ", utils.ToJSON(cd.RatingInfos)) if err != nil { //utils.Logger.Err(fmt.Sprintf("error getting cost for key <%s>: %s", cd.GetKey(cd.Subject), err.Error())) return &CallCost{Cost: -1}, err diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index ddde0471f..607a97a23 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -326,6 +326,43 @@ func TestGetCostRatingPlansAndRatingIntervalsMore(t *testing.T) { } } +func TestGetCostRatingPlansAndRatingIntervalsMoreDays(t *testing.T) { + t1 := time.Date(2012, time.February, 20, 9, 50, 0, 0, time.UTC) + t2 := time.Date(2012, time.February, 23, 18, 10, 0, 0, time.UTC) + cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "CUSTOMER_1", Subject: "rif:from:tm", Destination: "49178", TimeStart: t1, TimeEnd: t2, LoopIndex: 0, DurationIndex: t2.Sub(t1)} + result, _ := cd.GetCost() + if len(result.Timespans) != 8 || + !result.Timespans[0].TimeEnd.Equal(result.Timespans[1].TimeStart) || + !result.Timespans[1].TimeEnd.Equal(result.Timespans[2].TimeStart) || + !result.Timespans[2].TimeEnd.Equal(result.Timespans[3].TimeStart) || + !result.Timespans[3].TimeEnd.Equal(result.Timespans[4].TimeStart) || + !result.Timespans[4].TimeEnd.Equal(result.Timespans[5].TimeStart) || + !result.Timespans[5].TimeEnd.Equal(result.Timespans[6].TimeStart) || + !result.Timespans[6].TimeEnd.Equal(result.Timespans[7].TimeStart) { + for _, ts := range result.Timespans { + t.Logf("TS %+v", ts) + } + t.Errorf("Expected %+v was %+v", 4, len(result.Timespans)) + } +} + +func TestGetCostRatingPlansAndRatingIntervalsMoreDaysWeekend(t *testing.T) { + t1 := time.Date(2012, time.February, 24, 9, 50, 0, 0, time.UTC) + t2 := time.Date(2012, time.February, 27, 18, 10, 0, 0, time.UTC) + cd := &CallDescriptor{Direction: "*out", Category: "0", Tenant: "CUSTOMER_1", Subject: "rif:from:tm", Destination: "49178", TimeStart: t1, TimeEnd: t2, LoopIndex: 0, DurationIndex: t2.Sub(t1)} + result, _ := cd.GetCost() + if len(result.Timespans) != 5 || + !result.Timespans[0].TimeEnd.Equal(result.Timespans[1].TimeStart) || + !result.Timespans[1].TimeEnd.Equal(result.Timespans[2].TimeStart) || + !result.Timespans[2].TimeEnd.Equal(result.Timespans[3].TimeStart) || + !result.Timespans[3].TimeEnd.Equal(result.Timespans[4].TimeStart) { + for _, ts := range result.Timespans { + t.Logf("TS %+v", ts) + } + t.Errorf("Expected %+v was %+v", 4, len(result.Timespans)) + } +} + func TestGetCostRateGroups(t *testing.T) { t1 := time.Date(2013, time.October, 7, 14, 50, 0, 0, time.UTC) t2 := time.Date(2013, time.October, 7, 14, 52, 12, 0, time.UTC) diff --git a/engine/ratingplan.go b/engine/ratingplan.go index 3f1214477..fd902ed8e 100644 --- a/engine/ratingplan.go +++ b/engine/ratingplan.go @@ -59,12 +59,7 @@ func (rp *RatingPlan) RateIntervalList(dId string) RateIntervalList { return ril } -/* -type xCachedRatingPlan struct { - rp *RatingPlan - *cache2go.XEntry -} -*/ +// no sorter because it's sorted with RateIntervalTimeSorter /* Adds one ore more intervals to the internal interval list only if it is not allready in the list. diff --git a/engine/ratingprofile_test.go b/engine/ratingprofile_test.go index 4f929029a..100aa0630 100644 --- a/engine/ratingprofile_test.go +++ b/engine/ratingprofile_test.go @@ -250,6 +250,41 @@ func TestRatingProfileRIforTSMidnight(t *testing.T) { } } +func TestRatingProfileYearMonthDay(t *testing.T) { + ri := &RatingInfo{ + RateIntervals: RateIntervalList{ + &RateInterval{ + Timing: &RITiming{ + StartTime: "09:00:00", + }, + }, + &RateInterval{ + Timing: &RITiming{ + StartTime: "00:00:00", + }, + }, + &RateInterval{ + Timing: &RITiming{ + Years: utils.Years{2016}, + Months: utils.Months{1}, + MonthDays: utils.MonthDays{6, 7}, + WeekDays: utils.WeekDays{}, + StartTime: "19:00:00", + }, + }, + }, + } + ts := &TimeSpan{ + TimeStart: time.Date(2016, 1, 6, 23, 40, 0, 0, time.UTC), + TimeEnd: time.Date(2016, 1, 7, 1, 1, 30, 0, time.UTC), + } + rIntervals := ri.SelectRatingIntevalsForTimespan(ts) + if len(rIntervals) != 1 || + rIntervals[0].Timing.StartTime != "19:00:00" { + t.Error("Wrong interval list: ", utils.ToIJSON(rIntervals)) + } +} + func TestRatingProfileSubjectPrefixMatching(t *testing.T) { rpSubjectPrefixMatching = true rp, err := RatingProfileSubjectPrefixMatching("*out:cgrates.org:data:rif") diff --git a/engine/timespans.go b/engine/timespans.go index 8a716f307..366de1d3f 100644 --- a/engine/timespans.go +++ b/engine/timespans.go @@ -612,6 +612,28 @@ func (ts *TimeSpan) SplitByRatingPlan(rp *RatingInfo) (newTs *TimeSpan) { return } +// Splits the given timespan on activation period's activation time. +func (ts *TimeSpan) SplitByDay() (newTs *TimeSpan) { + if ts.TimeStart.Day() == ts.TimeEnd.Day() { + return + } + splitDate := ts.TimeStart.AddDate(0, 0, 1) + splitDate = time.Date(splitDate.Year(), splitDate.Month(), splitDate.Day(), 0, 0, 0, 0, splitDate.Location()) + if splitDate == ts.TimeEnd { // the end date time was actually 00:00:00 + return + } + newTs = &TimeSpan{ + TimeStart: splitDate, + TimeEnd: ts.TimeEnd, + } + newTs.copyRatingInfo(ts) + newTs.DurationIndex = ts.DurationIndex + ts.TimeEnd = splitDate + ts.SetNewDurationIndex(newTs) + // Logger.Debug(fmt.Sprintf("RP SPLITTING: %+v %+v", ts, newTs)) + return +} + // Returns the starting time of this timespan func (ts *TimeSpan) GetGroupStart() time.Duration { s := ts.DurationIndex - ts.GetDuration()