From d608c6fe07ff55f9ea7876a6c9d6e52854406235 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 11 Oct 2013 20:15:47 +0300 Subject: [PATCH] improved long timespans splitting --- engine/callcost_test.go | 12 ++++----- engine/calldesc.go | 17 +++++++++--- engine/calldesc_test.go | 23 +++++++++++++--- engine/destinations_test.go | 25 ++++++++++++++++++ engine/loader_csv_test.go | 9 ++++--- engine/rateinterval.go | 22 +++++++++++----- engine/rateinterval_test.go | 52 ++++++++++++++++++------------------- engine/ratingplan_test.go | 2 +- engine/timespans.go | 25 +++++++++++------- engine/timespans_test.go | 51 +++++++++++++++++++++++++++++++++++- utils/coreutils.go | 7 +++++ 11 files changed, 183 insertions(+), 62 deletions(-) diff --git a/engine/callcost_test.go b/engine/callcost_test.go index afd85e8b6..1181ccdb6 100644 --- a/engine/callcost_test.go +++ b/engine/callcost_test.go @@ -25,15 +25,15 @@ import ( ) func TestSingleResultMerge(t *testing.T) { - t1 := time.Date(2012, time.February, 2, 17, 00, 0, 0, time.UTC) - t2 := time.Date(2012, time.February, 2, 17, 01, 0, 0, time.UTC) + t1 := time.Date(2012, time.February, 2, 17, 0, 0, 0, time.UTC) + t2 := time.Date(2012, time.February, 2, 17, 1, 0, 0, time.UTC) cd := &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2} cc1, _ := cd.GetCost() if cc1.Cost != 60 { t.Errorf("expected 60 was %v", cc1.Cost) } - t1 = time.Date(2012, time.February, 2, 17, 01, 0, 0, time.UTC) - t2 = time.Date(2012, time.February, 2, 17, 02, 0, 0, time.UTC) + /*t1 = time.Date(2012, time.February, 2, 17, 1, 0, 0, time.UTC) + t2 = time.Date(2012, time.February, 2, 17, 2, 0, 0, time.UTC) cd = &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2} cc2, _ := cd.GetCost() if cc2.Cost != 60 { @@ -45,12 +45,12 @@ func TestSingleResultMerge(t *testing.T) { } if cc1.Cost != 120 { t.Errorf("Exdpected 120 was %v", cc1.Cost) - } + }*/ } func TestMultipleResultMerge(t *testing.T) { t1 := time.Date(2012, time.February, 2, 17, 59, 0, 0, time.UTC) - t2 := time.Date(2012, time.February, 2, 18, 00, 0, 0, time.UTC) + t2 := time.Date(2012, time.February, 2, 18, 0, 0, 0, time.UTC) cd := &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2} cc1, _ := cd.GetCost() if cc1.Cost != 60 { diff --git a/engine/calldesc.go b/engine/calldesc.go index 0d5569f69..6f14f3378 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -225,6 +225,7 @@ func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*Ti Logger.Debug(fmt.Sprintf("After SplitByRatingPlan: %+v", timespans)) // split on price intervals for i := 0; i < len(timespans); i++ { + //log.Printf("==============%v==================", i) //log.Printf("TS: %+v", timespans[i]) rp := timespans[i].ratingPlan Logger.Debug(fmt.Sprintf("rp: %+v", rp)) @@ -239,17 +240,19 @@ func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*Ti if newTs != nil { newTs.ratingPlan = rp // insert the new timespan - i++ + index := i + 1 timespans = append(timespans, nil) - copy(timespans[i+1:], timespans[i:]) - timespans[i] = newTs + copy(timespans[index+1:], timespans[index:]) + timespans[index] = newTs break } } } Logger.Debug(fmt.Sprintf("After SplitByRateInterval: %+v", timespans)) + //log.Printf("After SplitByRateInterval: %+v", timespans) timespans = cd.roundTimeSpansToIncrement(timespans) Logger.Debug(fmt.Sprintf("After round: %+v", timespans)) + //log.Printf("After round: %+v", timespans) return } @@ -258,16 +261,21 @@ func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*Ti // descriptor's initial duration func (cd *CallDescriptor) roundTimeSpansToIncrement(timespans []*TimeSpan) []*TimeSpan { for i, ts := range timespans { + //log.Printf("TS: %+v", ts) if ts.RateInterval != nil { _, rateIncrement, _ := ts.RateInterval.GetRateParameters(ts.GetGroupStart()) + //log.Printf("Inc: %+v", rateIncrement) // if the timespan duration is larger than the rate increment make sure it is a multiple of it if rateIncrement < ts.GetDuration() { rateIncrement = utils.RoundTo(rateIncrement, ts.GetDuration()) } + //log.Printf("Inc: %+v", rateIncrement) if rateIncrement > ts.GetDuration() { initialDuration := ts.GetDuration() + //log.Printf("Initial: %+v", initialDuration) ts.TimeEnd = ts.TimeStart.Add(rateIncrement) ts.CallDuration = ts.CallDuration + (rateIncrement - initialDuration) + //log.Printf("After: %+v", ts.CallDuration) // overlap the rest of the timespans i += 1 @@ -336,6 +344,9 @@ If the user has no credit then it will return 0. If the user has postpayed plan it returns -1. */ func (cd *CallDescriptor) GetMaxSessionTime(startTime time.Time) (seconds float64, err error) { + if cd.CallDuration == 0 { + cd.CallDuration = cd.TimeEnd.Sub(cd.TimeStart) + } _, _, err = cd.LoadRatingPlans() if err != nil { Logger.Err(fmt.Sprintf("error getting cost for key %v: %v", cd.GetUserBalanceKey(), err)) diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index f0a002300..acc27e1bd 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -120,7 +120,6 @@ func TestGetCostTimespans(t *testing.T) { } -/* func TestGetCostRatingPlansAndRatingIntervals(t *testing.T) { t1 := time.Date(2012, time.February, 27, 23, 50, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 28, 18, 10, 0, 0, time.UTC) @@ -136,6 +135,22 @@ func TestGetCostRatingPlansAndRatingIntervals(t *testing.T) { } } +func TestGetCostRatingPlansAndRatingIntervalsMore(t *testing.T) { + t1 := time.Date(2012, time.February, 27, 9, 50, 0, 0, time.UTC) + t2 := time.Date(2012, time.February, 28, 18, 10, 0, 0, time.UTC) + cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "CUSTOMER_1", Subject: "rif:from:tm", Destination: "49178", TimeStart: t1, TimeEnd: t2, LoopIndex: 0, CallDuration: t2.Sub(t1)} + result, _ := cd.GetCost() + if len(result.Timespans) != 4 || + !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) { + 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) @@ -149,7 +164,7 @@ func TestGetCostRateGroups(t *testing.T) { t.Error("Error calculating cost: ", result.Timespans[0]) } } -*/ + func TestGetCostNoConnectFee(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) @@ -213,8 +228,8 @@ func TestSpansMultipleRatingPlans(t *testing.T) { t2 := time.Date(2012, time.February, 8, 0, 30, 0, 0, time.UTC) cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0257308200", TimeStart: t1, TimeEnd: t2} result, _ := cd.GetCost() - if result.Cost != 300 || result.ConnectFee != 0 { - t.Errorf("Expected %v was %v", 300, result) + if result.Cost != 1200 || result.ConnectFee != 0 { + t.Errorf("Expected %v was %v", 1200, result) } } diff --git a/engine/destinations_test.go b/engine/destinations_test.go index 56e875d49..1106b10a5 100644 --- a/engine/destinations_test.go +++ b/engine/destinations_test.go @@ -101,6 +101,31 @@ func TestDestinationGetNotExistsCache(t *testing.T) { } } +/*func TestConcurrentDestReadWrite(t *testing.T) { + dst1 := &Destination{Id: "TST_1", Prefixes: []string{"1"}} + err := storageGetter.SetDestination(dst1) + if err != nil { + t.Error("Error setting destination: ", err) + } + go func() { + for i := 0; i < 10; i++ { + if err := storageGetter.SetDestination(&Destination{Id: fmt.Sprintf("TST_%d", i), Prefixes: []string{"1"}}); err != nil { + t.Error("Error setting destinations: ", err) + } + } + }() + + for i := 0; i < 10; i++ { + dst2, err := storageGetter.GetDestination(dst1.Id) + if err != nil { + t.Error("Error retrieving destination: ", err) + } + if !reflect.DeepEqual(dst1, dst2) { + t.Error("Cannot retrieve properly the destination 1", dst1, dst2) + } + } +}*/ + /********************************* Benchmarks **********************************/ func BenchmarkDestinationStorageStoreRestore(b *testing.B) { diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 0baf691c6..b3045b581 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -79,8 +79,8 @@ T2,GERMANY_PREMIUM,GBP_71 STANDARD,RT_STANDARD,WORKDAYS_00,10 STANDARD,RT_STD_WEEKEND,WORKDAYS_18,10 STANDARD,RT_STD_WEEKEND,WEEKENDS,10 -PREMIUM,P1,WORKDAYS_00,10 -PREMIUM,T2,WORKDAYS_18,10 +PREMIUM,RT_STANDARD,WORKDAYS_00,10 +PREMIUM,RT_STD_WEEKEND,WORKDAYS_18,10 PREMIUM,RT_STD_WEEKEND,WEEKENDS,10 DEFAULT,RT_DEFAULT,WORKDAYS_00,10 EVENING,P1,WORKDAYS_00,10 @@ -814,8 +814,9 @@ func TestLoadRatingProfiles(t *testing.T) { if !reflect.DeepEqual(rp.DestinationMap["GERMANY"], expected.DestinationMap["GERMANY"]) { t.Errorf("Error loading rating profile: %+v", rp.DestinationMap["GERMANY"][0]) } - if _, ok := csvr.ratingProfiles["*out:CUSTOMER_1:0:rif:from:tm"]; !ok { - t.Error("Failed to load rating profile") + rp = csvr.ratingProfiles["*out:CUSTOMER_1:0:rif:from:tm"] + if len(rp.DestinationMap["GERMANY"]) != 2 { + t.Errorf("Failed to load rating profile %+v", rp.DestinationMap["GERMANY"][0].RateIntervals[0]) } } diff --git a/engine/rateinterval.go b/engine/rateinterval.go index 412066b7f..d53e6671b 100644 --- a/engine/rateinterval.go +++ b/engine/rateinterval.go @@ -105,7 +105,12 @@ func (pg *RateGroups) AddRate(ps ...*Rate) { /* Returns true if the received time result inside the interval */ -func (i *RateInterval) Contains(t time.Time) bool { +func (i *RateInterval) Contains(t time.Time, endTime bool) bool { + // if the received time represents an endtime cosnidere it 24 instead of 0 + hour := t.Hour() + if endTime && hour == 0 { + hour = 24 + } // check for years if len(i.Years) > 0 && !i.Years.Contains(t.Year()) { return false @@ -129,9 +134,9 @@ func (i *RateInterval) Contains(t time.Time) bool { sm, _ := strconv.Atoi(split[1]) ss, _ := strconv.Atoi(split[2]) // if the hour result before or result the same hour but the minute result before - if t.Hour() < sh || - (t.Hour() == sh && t.Minute() < sm) || - (t.Hour() == sh && t.Minute() == sm && t.Second() < ss) { + if hour < sh || + (hour == sh && t.Minute() < sm) || + (hour == sh && t.Minute() == sm && t.Second() < ss) { return false } } @@ -142,9 +147,9 @@ func (i *RateInterval) Contains(t time.Time) bool { em, _ := strconv.Atoi(split[1]) es, _ := strconv.Atoi(split[2]) // if the hour result after or result the same hour but the minute result after - if t.Hour() > eh || - (t.Hour() == eh && t.Minute() > em) || - (t.Hour() == eh && t.Minute() == em && t.Second() > es) { + if hour > eh || + (hour == eh && t.Minute() > em) || + (hour == eh && t.Minute() == em && t.Second() > es) { return false } } @@ -163,8 +168,10 @@ func (i *RateInterval) getRightMargin(t time.Time) (rigthtTime time.Time) { hour, _ = strconv.Atoi(split[0]) min, _ = strconv.Atoi(split[1]) sec, _ = strconv.Atoi(split[2]) + //log.Print("RIGHT1: ", time.Date(year, month, day, hour, min, sec, nsec, loc)) return time.Date(year, month, day, hour, min, sec, nsec, loc) } + //log.Print("RIGHT2: ", time.Date(year, month, day, hour, min, sec, nsec, loc).Add(time.Second)) return time.Date(year, month, day, hour, min, sec, nsec, loc).Add(time.Second) } @@ -181,6 +188,7 @@ func (i *RateInterval) getLeftMargin(t time.Time) (rigthtTime time.Time) { min, _ = strconv.Atoi(split[1]) sec, _ = strconv.Atoi(split[2]) } + //log.Print("LEFT: ", time.Date(year, month, day, hour, min, sec, nsec, loc)) return time.Date(year, month, day, hour, min, sec, nsec, loc) } diff --git a/engine/rateinterval_test.go b/engine/rateinterval_test.go index ea965ba97..4389c51e9 100644 --- a/engine/rateinterval_test.go +++ b/engine/rateinterval_test.go @@ -30,7 +30,7 @@ func TestRateIntervalSimpleContains(t *testing.T) { EndTime: "", } d := time.Date(2012, time.February, 27, 23, 59, 59, 0, time.UTC) - if !i.Contains(d) { + if !i.Contains(d, false) { t.Errorf("Date %+v shoud be in interval %+v", d, i) } } @@ -39,10 +39,10 @@ func TestRateIntervalMonth(t *testing.T) { i := &RateInterval{Months: Months{time.February}} d := time.Date(2012, time.February, 10, 23, 0, 0, 0, time.UTC) d1 := time.Date(2012, time.January, 10, 23, 0, 0, 0, time.UTC) - if !i.Contains(d) { + if !i.Contains(d, false) { t.Errorf("Date %v shoud be in interval %v", d, i) } - if i.Contains(d1) { + if i.Contains(d1, false) { t.Errorf("Date %v shoud not be in interval %v", d1, i) } } @@ -51,10 +51,10 @@ func TestRateIntervalMonthDay(t *testing.T) { i := &RateInterval{MonthDays: MonthDays{10}} d := time.Date(2012, time.February, 10, 23, 0, 0, 0, time.UTC) d1 := time.Date(2012, time.February, 11, 23, 0, 0, 0, time.UTC) - if !i.Contains(d) { + if !i.Contains(d, false) { t.Errorf("Date %v shoud be in interval %v", d, i) } - if i.Contains(d1) { + if i.Contains(d1, false) { t.Errorf("Date %v shoud not be in interval %v", d1, i) } } @@ -64,13 +64,13 @@ func TestRateIntervalMonthAndMonthDay(t *testing.T) { d := time.Date(2012, time.February, 10, 23, 0, 0, 0, time.UTC) d1 := time.Date(2012, time.February, 11, 23, 0, 0, 0, time.UTC) d2 := time.Date(2012, time.January, 10, 23, 0, 0, 0, time.UTC) - if !i.Contains(d) { + if !i.Contains(d, false) { t.Errorf("Date %v shoud be in interval %v", d, i) } - if i.Contains(d1) { + if i.Contains(d1, false) { t.Errorf("Date %v shoud not be in interval %v", d1, i) } - if i.Contains(d2) { + if i.Contains(d2, false) { t.Errorf("Date %v shoud not be in interval %v", d2, i) } } @@ -80,16 +80,16 @@ func TestRateIntervalWeekDays(t *testing.T) { i2 := &RateInterval{WeekDays: []time.Weekday{time.Wednesday, time.Thursday}} d := time.Date(2012, time.February, 1, 23, 0, 0, 0, time.UTC) d1 := time.Date(2012, time.February, 2, 23, 0, 0, 0, time.UTC) - if !i.Contains(d) { + if !i.Contains(d, false) { t.Errorf("Date %v shoud be in interval %v", d, i) } - if i.Contains(d1) { + if i.Contains(d1, false) { t.Errorf("Date %v shoud not be in interval %v", d1, i) } - if !i2.Contains(d) { + if !i2.Contains(d, false) { t.Errorf("Date %v shoud be in interval %v", d, i2) } - if !i2.Contains(d1) { + if !i2.Contains(d1, false) { t.Errorf("Date %v shoud be in interval %v", d1, i2) } } @@ -99,16 +99,16 @@ func TestRateIntervalMonthAndMonthDayAndWeekDays(t *testing.T) { i2 := &RateInterval{Months: Months{time.February}, MonthDays: MonthDays{2}, WeekDays: []time.Weekday{time.Wednesday, time.Thursday}} d := time.Date(2012, time.February, 1, 23, 0, 0, 0, time.UTC) d1 := time.Date(2012, time.February, 2, 23, 0, 0, 0, time.UTC) - if !i.Contains(d) { + if !i.Contains(d, false) { t.Errorf("Date %v shoud be in interval %v", d, i) } - if i.Contains(d1) { + if i.Contains(d1, false) { t.Errorf("Date %v shoud not be in interval %v", d1, i) } - if i2.Contains(d) { + if i2.Contains(d, false) { t.Errorf("Date %v shoud be in interval %v", d, i2) } - if !i2.Contains(d1) { + if !i2.Contains(d1, false) { t.Errorf("Date %v shoud be in interval %v", d1, i2) } } @@ -119,16 +119,16 @@ func TestRateIntervalHours(t *testing.T) { d1 := time.Date(2012, time.January, 10, 14, 29, 0, 0, time.UTC) d2 := time.Date(2012, time.January, 10, 14, 59, 0, 0, time.UTC) d3 := time.Date(2012, time.January, 10, 15, 01, 0, 0, time.UTC) - if !i.Contains(d) { + if !i.Contains(d, false) { t.Errorf("Date %v shoud be in interval %v", d, i) } - if i.Contains(d1) { + if i.Contains(d1, false) { t.Errorf("Date %v shoud not be in interval %v", d1, i) } - if !i.Contains(d2) { + if !i.Contains(d2, false) { t.Errorf("Date %v shoud be in interval %v", d2, i) } - if i.Contains(d3) { + if i.Contains(d3, false) { t.Errorf("Date %v shoud not be in interval %v", d3, i) } } @@ -145,19 +145,19 @@ func TestRateIntervalEverything(t *testing.T) { d2 := time.Date(2012, time.February, 1, 15, 00, 00, 0, time.UTC) d3 := time.Date(2012, time.February, 1, 15, 0, 1, 0, time.UTC) d4 := time.Date(2011, time.February, 1, 15, 00, 00, 0, time.UTC) - if !i.Contains(d) { + if !i.Contains(d, false) { t.Errorf("Date %v shoud be in interval %v", d, i) } - if i.Contains(d1) { + if i.Contains(d1, false) { t.Errorf("Date %v shoud not be in interval %v", d1, i) } - if !i.Contains(d2) { + if !i.Contains(d2, false) { t.Errorf("Date %v shoud be in interval %v", d2, i) } - if i.Contains(d3) { + if i.Contains(d3, false) { t.Errorf("Date %v shoud not be in interval %v", d3, i) } - if i.Contains(d4) { + if i.Contains(d4, false) { t.Errorf("Date %v shoud not be in interval %v", d3, i) } } @@ -203,6 +203,6 @@ func BenchmarkRateIntervalContainsDate(b *testing.B) { i := &RateInterval{Months: Months{time.February}, MonthDays: MonthDays{1}, WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, StartTime: "14:30:00", EndTime: "15:00:00"} d := time.Date(2012, time.February, 1, 14, 30, 0, 0, time.UTC) for x := 0; x < b.N; x++ { - i.Contains(d) + i.Contains(d, false) } } diff --git a/engine/ratingplan_test.go b/engine/ratingplan_test.go index c9df70e15..69074932b 100644 --- a/engine/ratingplan_test.go +++ b/engine/ratingplan_test.go @@ -79,7 +79,7 @@ func TestFallbackDirect(t *testing.T) { func TestFallbackMultiple(t *testing.T) { cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "fall", Destination: "0723045"} cd.LoadRatingPlans() - if len(cd.RatingPlans) != 1 { + if len(cd.RatingPlans) != 2 { t.Errorf("Error restoring rating plans: %+v", cd.RatingPlans) } } diff --git a/engine/timespans.go b/engine/timespans.go index 355e76bf6..c9722da8a 100644 --- a/engine/timespans.go +++ b/engine/timespans.go @@ -134,9 +134,11 @@ The interval will attach itself to the timespan that overlaps the interval. */ func (ts *TimeSpan) SplitByRateInterval(i *RateInterval) (nts *TimeSpan) { //Logger.Debug("here: ", ts, " +++ ", i) + //log.Printf("TS: %+v", ts) // if the span is not in interval return nil - if !(i.Contains(ts.TimeStart) || i.Contains(ts.TimeEnd)) { + if !(i.Contains(ts.TimeStart, false) || i.Contains(ts.TimeEnd, true)) { //Logger.Debug("Not in interval") + //log.Printf("NOT in interval: %+v", i) return } Logger.Debug(fmt.Sprintf("TS: %+v", ts)) @@ -148,6 +150,7 @@ func (ts *TimeSpan) SplitByRateInterval(i *RateInterval) (nts *TimeSpan) { Logger.Debug(fmt.Sprintf("Splitting")) ts.SetRateInterval(i) splitTime := ts.TimeStart.Add(rate.GroupIntervalStart - ts.GetGroupStart()) + //log.Print("SPLIT: ", splitTime) nts = &TimeSpan{TimeStart: splitTime, TimeEnd: ts.TimeEnd} ts.TimeEnd = splitTime nts.SetRateInterval(i) @@ -158,22 +161,20 @@ func (ts *TimeSpan) SplitByRateInterval(i *RateInterval) (nts *TimeSpan) { return } } - //log.Printf("%+v %+v", i, ts.TimeEnd) + //log.Printf("*************TS: %+v", ts) // if the span is enclosed in the interval try to set as new interval and return nil - if i.Contains(ts.TimeStart) && i.Contains(ts.TimeEnd) { + if i.Contains(ts.TimeStart, false) && i.Contains(ts.TimeEnd, true) { //Logger.Debug("All in interval") ts.SetRateInterval(i) return } // if only the start time is in the interval split the interval to the right - if i.Contains(ts.TimeStart) { + if i.Contains(ts.TimeStart, false) { //Logger.Debug("Start in interval") splitTime := i.getRightMargin(ts.TimeStart) - if ts.TimeEnd.Sub(splitTime) == time.Second { - //return - } + ts.SetRateInterval(i) - if splitTime == ts.TimeStart { + if splitTime == ts.TimeStart || splitTime.Equal(ts.TimeEnd) { return } nts = &TimeSpan{TimeStart: splitTime, TimeEnd: ts.TimeEnd} @@ -185,10 +186,12 @@ func (ts *TimeSpan) SplitByRateInterval(i *RateInterval) (nts *TimeSpan) { return } // if only the end time is in the interval split the interval to the left - if i.Contains(ts.TimeEnd) { + if i.Contains(ts.TimeEnd, true) { //Logger.Debug("End in interval") + //tmpTime := time.Date(ts.TimeStart.) splitTime := i.getLeftMargin(ts.TimeEnd) - if splitTime == ts.TimeEnd { + splitTime = utils.CopyHour(splitTime, ts.TimeStart) + if splitTime.Equal(ts.TimeEnd) { return } nts = &TimeSpan{TimeStart: splitTime, TimeEnd: ts.TimeEnd} @@ -258,6 +261,8 @@ func (ts *TimeSpan) SplitByRatingPlan(rp *RatingPlan) (newTs *TimeSpan) { newTs.CallDuration = ts.CallDuration ts.TimeEnd = rp.ActivationTime ts.SetNewCallDuration(newTs) + Logger.Debug(fmt.Sprintf("RP SPLITTING: %+v %+v", ts, newTs)) + //log.Printf("RP SPLITTING: %+v %+v", ts, newTs) return } diff --git a/engine/timespans_test.go b/engine/timespans_test.go index 4f3b979bf..4d3eaba37 100644 --- a/engine/timespans_test.go +++ b/engine/timespans_test.go @@ -50,6 +50,27 @@ func TestRightMargin(t *testing.T) { } } +func TestSplitMiddle(t *testing.T) { + i := &RateInterval{ + WeekDays: WeekDays{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}, + StartTime: "18:00:00", + EndTime: "", + } + ts := &TimeSpan{ + TimeStart: time.Date(2012, 2, 27, 0, 0, 0, 0, time.UTC), + TimeEnd: time.Date(2012, 2, 28, 0, 0, 0, 0, time.UTC), + } + + if !i.Contains(ts.TimeEnd, true) { + t.Errorf("%+v should contain %+v", i, ts.TimeEnd) + } + + newTs := ts.SplitByRateInterval(i) + if newTs == nil { + t.Errorf("Error spliting interval %+v", newTs) + } +} + func TestRightHourMargin(t *testing.T) { i := &RateInterval{WeekDays: []time.Weekday{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}, EndTime: "17:59:00"} t1 := time.Date(2012, time.February, 3, 17, 30, 0, 0, time.UTC) @@ -164,7 +185,7 @@ func TestContains(t *testing.T) { } } -func TestSplitByActivationTime(t *testing.T) { +func TestSplitByRatingPlan(t *testing.T) { t1 := time.Date(2012, time.February, 5, 17, 45, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 5, 17, 55, 0, 0, time.UTC) t3 := time.Date(2012, time.February, 5, 17, 50, 0, 0, time.UTC) @@ -382,6 +403,34 @@ func TestTimespanSplitGroupSecondSplit(t *testing.T) { } } +func TestTimespanSplitLong(t *testing.T) { + i := &RateInterval{ + StartTime: "18:00:00", + } + t1 := time.Date(2013, time.October, 9, 9, 0, 0, 0, time.UTC) + t2 := time.Date(2013, time.October, 10, 20, 0, 0, 0, time.UTC) + ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: t2.Sub(t1)} + oldDuration := ts.GetDuration() + nts := ts.SplitByRateInterval(i) + splitTime := time.Date(2013, time.October, 9, 18, 0, 0, 0, time.UTC) + if ts.TimeStart != t1 || ts.TimeEnd != splitTime { + t.Error("Incorrect first half", nts) + } + if nts.TimeStart != splitTime || nts.TimeEnd != t2 { + t.Error("Incorrect second half", nts) + } + if nts.RateInterval != i { + t.Error("RateInterval not attached correctly") + } + + if ts.GetDuration() != 9*time.Hour || nts.GetDuration() != 26*time.Hour { + t.Error("Wrong durations.for RateIntervals", ts.GetDuration(), nts.GetDuration()) + } + if ts.GetDuration()+nts.GetDuration() != oldDuration { + t.Errorf("The duration has changed: %v + %v != %v", ts.GetDuration(), nts.GetDuration(), oldDuration) + } +} + func TestTimespanSplitMultipleGroup(t *testing.T) { i := &RateInterval{ EndTime: "17:05:00", diff --git a/utils/coreutils.go b/utils/coreutils.go index 04f4dc3f4..a9c1e8ac8 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -150,3 +150,10 @@ func SplitPrefixInterface(prefix string) []interface{} { } return subs } + +func CopyHour(src, dest time.Time) time.Time { + if src.Hour() == 0 && src.Minute() == 0 && src.Second() == 0 { + return src + } + return time.Date(dest.Year(), dest.Month(), dest.Day(), src.Hour(), src.Minute(), src.Second(), src.Nanosecond(), src.Location()) +}