interval and activation profile refactoring

This commit is contained in:
Radu Ioan Fericean
2013-09-12 16:24:57 +03:00
parent a1612f5507
commit a35f5ed480
29 changed files with 487 additions and 490 deletions

View File

@@ -50,13 +50,15 @@ func (self *ApierV1) SetTPActions(attrs utils.TPActions, reply *string) error {
ActionType: act.Identifier,
BalanceId: act.BalanceType,
Direction: act.Direction,
Units: act.Units,
ExpirationString: act.ExpiryTime,
DestinationTag: act.DestinationId,
RateType: act.RateType,
RateValue: act.Rate,
MinutesWeight: act.MinutesWeight,
Weight: act.Weight,
Balance: &engine.Balance{
Value: act.Units,
DestinationId: act.DestinationId,
SpecialPriceType: act.RateType,
SpecialPrice: act.Rate,
Weight: act.MinutesWeight,
},
Weight: act.Weight,
}
}
if err := self.StorDb.SetTPActions(attrs.TPid, map[string][]*engine.Action{attrs.ActionsId: acts}); err != nil {

View File

@@ -23,9 +23,9 @@ package apier
import (
"errors"
"fmt"
"time"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
"time"
)
// Creates a new rate within a tariff plan
@@ -38,7 +38,7 @@ func (self *ApierV1) SetTPRate(attrs utils.TPRate, reply *string) error {
} else if exists {
return errors.New(utils.ERR_DUPLICATE)
}
rts := make([]*engine.Rate, len(attrs.RateSlots))
rts := make([]*engine.LoadRate, len(attrs.RateSlots))
for idx, rtSlot := range attrs.RateSlots {
var errParse error
itrvlStrs := []string{rtSlot.RatedUnits, rtSlot.RateIncrements, rtSlot.GroupIntervalStart}
@@ -48,10 +48,10 @@ func (self *ApierV1) SetTPRate(attrs utils.TPRate, reply *string) error {
return fmt.Errorf("%s:Parsing interval failed:%s", utils.ERR_SERVER_ERROR, errParse.Error())
}
}
rts[idx] = &engine.Rate{attrs.RateId, rtSlot.ConnectFee, rtSlot.Rate, itrvls[0], itrvls[1], itrvls[2],
rts[idx] = &engine.LoadRate{attrs.RateId, rtSlot.ConnectFee, rtSlot.Rate, itrvls[0], itrvls[1], itrvls[2],
rtSlot.RoundingMethod, rtSlot.RoundingDecimals, rtSlot.Weight}
}
if err := self.StorDb.SetTPRates(attrs.TPid, map[string][]*engine.Rate{attrs.RateId: rts}); err != nil {
if err := self.StorDb.SetTPRates(attrs.TPid, map[string][]*engine.LoadRate{attrs.RateId: rts}); err != nil {
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
}
*reply = "OK"

View File

@@ -15,7 +15,7 @@ The call information comes to CGRateS having the following vital information lik
LoopIndex // indicates the position of this segment in a cost request loop
CallDuration // the call duration so far (partial or final)
FallbackSubject // the subject to check for destination if not found on primary subject
ActivationPeriods
RatingPlans
}
When the session manager receives a call start event it will first check if the call is prepaid or postpaid. If the call is postpaid than the cost will be determined only once at the end of the call but if the call is prepaid there will be a debit operation every X seconds (X is configurable).
@@ -31,7 +31,7 @@ What are the activation periods?
::
type Interval struct {
type RateInterval struct {
Years
Months
MonthDays
@@ -51,9 +51,9 @@ What are the activation periods?
}
An **Interval** specifies the Month, the MonthDay, the WeekDays, the StartTime and the EndTime when the Interval's price profile is in effect.
An **RateInterval** specifies the Month, the MonthDay, the WeekDays, the StartTime and the EndTime when the RateInterval's price profile is in effect.
:Example: The Interval {"Month": [1], "WeekDays":[1,2,3,4,5], "StartTime":"18:00:00"} specifies the *Price* for the first month of each year from Monday to Friday starting 18:00. Most structure elements are optional and they can be combined in any way it makes sense. If an element is omitted it means it is zero or any.
:Example: The RateInterval {"Month": [1], "WeekDays":[1,2,3,4,5], "StartTime":"18:00:00"} specifies the *Price* for the first month of each year from Monday to Friday starting 18:00. Most structure elements are optional and they can be combined in any way it makes sense. If an element is omitted it means it is zero or any.
The *ConnectFee* specifies the connection price for the call if this interval is the first one of the call.
@@ -65,7 +65,7 @@ The *RoundingMethod* and the *RoundingDecimals* will adjust the price using the
The **Price** structure defines the start (*GroupIntervalStart*) of a section of a call with a specified rate *Value* per *RateUnit* diving and rounding the section in *RateIncrement* subsections.
So when there is a need to define new sets of prices just define new ActivationPeriods with the activation time set to the moment when it becomes active.
So when there is a need to define new sets of prices just define new RatingPlans with the activation time set to the moment when it becomes active.
Let's get back to the engine. When a GetCost or Debit call comes to the engine it will try to match the best rating profile for the given *Direction*, *Tenant*, *TOR* and *Subject* using the longest *Subject* prefix method or using the *FallbackSubject* if not found. The rating profile contains the activation periods that might apply to the call in question.
@@ -75,21 +75,21 @@ At this point in rating process the engine will start splitting the call into va
2. Activation periods: if there were not enough special price minutes available than the engine will check if the call spans over multiple activation periods (the call starts in initial rates period and continues in another).
3. Intervals: for each activation period that apply to the call the engine will select the best rate intervals that apply.
3. RateIntervals: for each activation period that apply to the call the engine will select the best rate intervals that apply.
::
type TimeSpan struct {
TimeStart, TimeEnd
Cost
ActivationPeriod
Interval
RatingPlan
RateInterval
MinuteInfo
CallDuration // the call duration so far till TimeEnd
}
The result of this splitting will be a list of *TimeSpan* structures each having attached the MinuteInfo or the Interval that gave the price for it. The *CallDuration* attribute will select the right *Price* from the *Interval* *Prices* list. The final cost for the call will be the sum of the prices of these times spans plus the *ConnectionFee* from the first time span of the call.
The result of this splitting will be a list of *TimeSpan* structures each having attached the MinuteInfo or the RateInterval that gave the price for it. The *CallDuration* attribute will select the right *Price* from the *RateInterval* *Prices* list. The final cost for the call will be the sum of the prices of these times spans plus the *ConnectionFee* from the first time span of the call.
6.2.1 User balances
-------------------

View File

@@ -21,23 +21,19 @@ package engine
import (
"fmt"
"sort"
"time"
)
/*
Structure to be filled for each tariff plan with the bonus value for received calls minutes.
*/
type Action struct {
Id string
ActionType string
BalanceId string
Direction string
ExpirationString string
Weight float64
Balance *Balance
DestinationTag, RateType string // From here for import/load purposes only
ExpirationDate time.Time
Units, RateValue, MinutesWeight float64
Id string
ActionType string
BalanceId string
Direction string
ExpirationString string
Weight float64
Balance *Balance
}
const (

View File

@@ -37,7 +37,7 @@ type ActionTiming struct {
Id string // uniquely identify the timing
Tag string // informative purpose only
UserBalanceIds []string
Timing *Interval
Timing *RateInterval
Weight float64
ActionsId string
actions Actions

View File

@@ -34,7 +34,7 @@ func TestActionTimingNothing(t *testing.T) {
}
func TestActionTimingOnlyHour(t *testing.T) {
at := &ActionTiming{Timing: &Interval{StartTime: "10:01:00"}}
at := &ActionTiming{Timing: &RateInterval{StartTime: "10:01:00"}}
st := at.GetNextStartTime()
now := time.Now()
y, m, d := now.Date()
@@ -45,7 +45,7 @@ func TestActionTimingOnlyHour(t *testing.T) {
}
func TestActionTimingOnlyWeekdays(t *testing.T) {
at := &ActionTiming{Timing: &Interval{WeekDays: []time.Weekday{time.Monday}}}
at := &ActionTiming{Timing: &RateInterval{WeekDays: []time.Weekday{time.Monday}}}
st := at.GetNextStartTime()
now := time.Now()
y, m, d := now.Date()
@@ -64,7 +64,7 @@ func TestActionTimingOnlyWeekdays(t *testing.T) {
}
func TestActionTimingHourWeekdays(t *testing.T) {
at := &ActionTiming{Timing: &Interval{WeekDays: []time.Weekday{time.Monday}, StartTime: "10:01:00"}}
at := &ActionTiming{Timing: &RateInterval{WeekDays: []time.Weekday{time.Monday}, StartTime: "10:01:00"}}
st := at.GetNextStartTime()
now := time.Now()
y, m, d := now.Date()
@@ -85,7 +85,7 @@ func TestActionTimingOnlyMonthdays(t *testing.T) {
now := time.Now()
y, m, d := now.Date()
tomorrow := time.Date(y, m, d, 0, 0, 0, 0, time.Local).AddDate(0, 0, 1)
at := &ActionTiming{Timing: &Interval{MonthDays: MonthDays{1, 25, 2, tomorrow.Day()}}}
at := &ActionTiming{Timing: &RateInterval{MonthDays: MonthDays{1, 25, 2, tomorrow.Day()}}}
st := at.GetNextStartTime()
expected := tomorrow
if !st.Equal(expected) {
@@ -102,7 +102,7 @@ func TestActionTimingHourMonthdays(t *testing.T) {
if now.After(testTime) {
day = tomorrow.Day()
}
at := &ActionTiming{Timing: &Interval{MonthDays: MonthDays{now.Day(), tomorrow.Day()}, StartTime: "10:01:00"}}
at := &ActionTiming{Timing: &RateInterval{MonthDays: MonthDays{now.Day(), tomorrow.Day()}, StartTime: "10:01:00"}}
st := at.GetNextStartTime()
expected := time.Date(y, m, day, 10, 1, 0, 0, time.Local)
if !st.Equal(expected) {
@@ -114,7 +114,7 @@ func TestActionTimingOnlyMonths(t *testing.T) {
now := time.Now()
y, m, d := now.Date()
nextMonth := time.Date(y, m, d, 0, 0, 0, 0, time.Local).AddDate(0, 1, 0)
at := &ActionTiming{Timing: &Interval{Months: Months{time.February, time.May, nextMonth.Month()}}}
at := &ActionTiming{Timing: &RateInterval{Months: Months{time.February, time.May, nextMonth.Month()}}}
st := at.GetNextStartTime()
expected := time.Date(y, nextMonth.Month(), 1, 0, 0, 0, 0, time.Local)
if !st.Equal(expected) {
@@ -131,7 +131,7 @@ func TestActionTimingHourMonths(t *testing.T) {
if now.After(testTime) {
month = nextMonth.Month()
}
at := &ActionTiming{Timing: &Interval{Months: Months{now.Month(), nextMonth.Month()}, StartTime: "10:01:00"}}
at := &ActionTiming{Timing: &RateInterval{Months: Months{now.Month(), nextMonth.Month()}, StartTime: "10:01:00"}}
st := at.GetNextStartTime()
expected := time.Date(y, month, d, 10, 1, 0, 0, time.Local)
if !st.Equal(expected) {
@@ -156,7 +156,7 @@ func TestActionTimingHourMonthdaysMonths(t *testing.T) {
month = nextMonth.Month()
}
}
at := &ActionTiming{Timing: &Interval{
at := &ActionTiming{Timing: &RateInterval{
Months: Months{now.Month(), nextMonth.Month()},
MonthDays: MonthDays{now.Day(), tomorrow.Day()},
StartTime: "10:01:00",
@@ -172,7 +172,7 @@ func TestActionTimingFirstOfTheMonth(t *testing.T) {
now := time.Now()
y, m, _ := now.Date()
nextMonth := time.Date(y, m, 1, 0, 0, 0, 0, time.Local).AddDate(0, 1, 0)
at := &ActionTiming{Timing: &Interval{
at := &ActionTiming{Timing: &RateInterval{
Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December},
MonthDays: MonthDays{1},
}}
@@ -187,7 +187,7 @@ func TestActionTimingOnlyYears(t *testing.T) {
now := time.Now()
y, m, d := now.Date()
nextYear := time.Date(y, m, d, 0, 0, 0, 0, time.Local).AddDate(1, 0, 0)
at := &ActionTiming{Timing: &Interval{Years: Years{now.Year(), nextYear.Year()}}}
at := &ActionTiming{Timing: &RateInterval{Years: Years{now.Year(), nextYear.Year()}}}
st := at.GetNextStartTime()
expected := time.Date(nextYear.Year(), 1, 1, 0, 0, 0, 0, time.Local)
if !st.Equal(expected) {
@@ -204,7 +204,7 @@ func TestActionTimingHourYears(t *testing.T) {
if now.After(testTime) {
year = nextYear.Year()
}
at := &ActionTiming{Timing: &Interval{Years: Years{now.Year(), nextYear.Year()}, StartTime: "10:01:00"}}
at := &ActionTiming{Timing: &RateInterval{Years: Years{now.Year(), nextYear.Year()}, StartTime: "10:01:00"}}
st := at.GetNextStartTime()
expected := time.Date(year, m, d, 10, 1, 0, 0, time.Local)
if !st.Equal(expected) {
@@ -229,7 +229,7 @@ func TestActionTimingHourMonthdaysYear(t *testing.T) {
year = nextYear.Year()
}
}
at := &ActionTiming{Timing: &Interval{
at := &ActionTiming{Timing: &RateInterval{
Years: Years{now.Year(), nextYear.Year()},
MonthDays: MonthDays{now.Day(), tomorrow.Day()},
StartTime: "10:01:00",
@@ -266,7 +266,7 @@ func TestActionTimingHourMonthdaysMonthYear(t *testing.T) {
year = nextYear.Year()
}
}
at := &ActionTiming{Timing: &Interval{
at := &ActionTiming{Timing: &RateInterval{
Years: Years{now.Year(), nextYear.Year()},
Months: Months{now.Month(), nextMonth.Month()},
MonthDays: MonthDays{now.Day(), tomorrow.Day()},
@@ -283,7 +283,7 @@ func TestActionTimingFirstOfTheYear(t *testing.T) {
now := time.Now()
y, _, _ := now.Date()
nextYear := time.Date(y, 1, 1, 0, 0, 0, 0, time.Local).AddDate(1, 0, 0)
at := &ActionTiming{Timing: &Interval{
at := &ActionTiming{Timing: &RateInterval{
Years: Years{nextYear.Year()},
Months: Months{time.January},
MonthDays: MonthDays{1},
@@ -300,7 +300,7 @@ func TestActionTimingFirstMonthOfTheYear(t *testing.T) {
now := time.Now()
y, _, _ := now.Date()
nextYear := time.Date(y, 1, 1, 0, 0, 0, 0, time.Local).AddDate(1, 0, 0)
at := &ActionTiming{Timing: &Interval{
at := &ActionTiming{Timing: &RateInterval{
Months: Months{time.January},
}}
st := at.GetNextStartTime()
@@ -311,14 +311,14 @@ func TestActionTimingFirstMonthOfTheYear(t *testing.T) {
}
func TestActionTimingCheckForASAP(t *testing.T) {
at := &ActionTiming{Timing: &Interval{StartTime: ASAP}}
at := &ActionTiming{Timing: &RateInterval{StartTime: ASAP}}
if !at.CheckForASAP() {
t.Errorf("%v should be asap!", at)
}
}
func TestActionTimingIsOneTimeRun(t *testing.T) {
at := &ActionTiming{Timing: &Interval{StartTime: ASAP}}
at := &ActionTiming{Timing: &RateInterval{StartTime: ASAP}}
if !at.CheckForASAP() {
t.Errorf("%v should be asap!", at)
}
@@ -328,7 +328,7 @@ func TestActionTimingIsOneTimeRun(t *testing.T) {
}
func TestActionTimingOneTimeRun(t *testing.T) {
at := &ActionTiming{Timing: &Interval{StartTime: ASAP}}
at := &ActionTiming{Timing: &RateInterval{StartTime: ASAP}}
at.CheckForASAP()
nextRun := at.GetNextStartTime()
if nextRun.IsZero() {
@@ -352,14 +352,14 @@ func TestActionTimingLogFunction(t *testing.T) {
}
func TestActionTimingPriotityListSortByWeight(t *testing.T) {
at1 := &ActionTiming{Timing: &Interval{
at1 := &ActionTiming{Timing: &RateInterval{
Years: Years{2100},
Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December},
MonthDays: MonthDays{1},
StartTime: "00:00:00",
Weight: 20,
}}
at2 := &ActionTiming{Timing: &Interval{
at2 := &ActionTiming{Timing: &RateInterval{
Years: Years{2100},
Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December},
MonthDays: MonthDays{2},
@@ -376,7 +376,7 @@ func TestActionTimingPriotityListSortByWeight(t *testing.T) {
func TestActionTimingPriotityListWeight(t *testing.T) {
at1 := &ActionTiming{
Timing: &Interval{
Timing: &RateInterval{
Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December},
MonthDays: MonthDays{1},
StartTime: "00:00:00",
@@ -384,7 +384,7 @@ func TestActionTimingPriotityListWeight(t *testing.T) {
Weight: 10.0,
}
at2 := &ActionTiming{
Timing: &Interval{
Timing: &RateInterval{
Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December},
MonthDays: MonthDays{1},
StartTime: "00:00:00",
@@ -848,7 +848,7 @@ func TestActionTriggerLogging(t *testing.T) {
}
func TestActionTimingLogging(t *testing.T) {
i := &Interval{
i := &RateInterval{
Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December},
MonthDays: MonthDays{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
WeekDays: WeekDays{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday},
@@ -856,7 +856,7 @@ func TestActionTimingLogging(t *testing.T) {
EndTime: "00:00:00",
Weight: 10.0,
ConnectFee: 0.0,
Prices: PriceGroups{&Price{0, 1.0, 1 * time.Second, 60 * time.Second}},
Rates: RateGroups{&Rate{0, 1.0, 1 * time.Second, 60 * time.Second}},
}
at := &ActionTiming{
Id: "some uuid",

View File

@@ -47,8 +47,8 @@ func (cc *CallCost) Merge(other *CallCost) {
}
ts := cc.Timespans[len(cc.Timespans)-1]
otherTs := other.Timespans[0]
if reflect.DeepEqual(ts.ActivationPeriod, otherTs.ActivationPeriod) &&
reflect.DeepEqual(ts.MinuteInfo, otherTs.MinuteInfo) && reflect.DeepEqual(ts.Interval, otherTs.Interval) {
if reflect.DeepEqual(ts.RatingPlan, otherTs.RatingPlan) &&
reflect.DeepEqual(ts.MinuteInfo, otherTs.MinuteInfo) && reflect.DeepEqual(ts.RateInterval, otherTs.RateInterval) {
// extend the last timespan with
ts.TimeEnd = ts.TimeEnd.Add(otherTs.GetDuration())
// add the rest of the timspans

View File

@@ -56,7 +56,7 @@ func TestMultipleResultMerge(t *testing.T) {
if cc1.Cost != 60 {
t.Errorf("expected 60 was %v", cc1.Cost)
for _, ts := range cc1.Timespans {
t.Log(ts.Interval)
t.Log(ts.RateInterval)
}
}
t1 = time.Date(2012, time.February, 2, 18, 00, 0, 0, time.UTC)
@@ -66,7 +66,7 @@ func TestMultipleResultMerge(t *testing.T) {
if cc2.Cost != 30 {
t.Errorf("expected 30 was %v", cc2.Cost)
for _, ts := range cc1.Timespans {
t.Log(ts.Interval)
t.Log(ts.RateInterval)
}
}
cc1.Merge(cc2)

View File

@@ -107,13 +107,13 @@ type CallDescriptor struct {
CallDuration time.Duration // the call duration so far (partial or final)
Amount float64
FallbackSubject string // the subject to check for destination if not found on primary subject
ActivationPeriods []*ActivationPeriod
RatingPlans []*RatingPlan
userBalance *UserBalance
}
// Adds an activation period that applyes to current call descriptor.
func (cd *CallDescriptor) AddActivationPeriod(aps ...*ActivationPeriod) {
cd.ActivationPeriods = append(cd.ActivationPeriods, aps...)
func (cd *CallDescriptor) AddRatingPlan(aps ...*RatingPlan) {
cd.RatingPlans = append(cd.RatingPlans, aps...)
}
// Returns the key used to retrive the user balance involved in this call
@@ -136,28 +136,28 @@ func (cd *CallDescriptor) getUserBalance() (ub *UserBalance, err error) {
/*
Restores the activation periods for the specified prefix from storage.
*/
func (cd *CallDescriptor) LoadActivationPeriods() (destPrefix string, err error) {
func (cd *CallDescriptor) LoadRatingPlans() (destPrefix string, err error) {
if val, err := cache2go.GetXCached(cd.GetKey() + cd.Destination); err == nil {
xaps := val.(xCachedActivationPeriods)
cd.ActivationPeriods = xaps.aps
xaps := val.(xCachedRatingPlans)
cd.RatingPlans = xaps.aps
return xaps.destPrefix, nil
}
destPrefix, values, err := cd.getActivationPeriodsForPrefix(cd.GetKey(), 1)
destPrefix, values, err := cd.getRatingPlansForPrefix(cd.GetKey(), 1)
if err != nil {
fallbackKey := fmt.Sprintf("%s:%s:%s:%s", cd.Direction, cd.Tenant, cd.TOR, FALLBACK_SUBJECT)
// use the default subject
destPrefix, values, err = cd.getActivationPeriodsForPrefix(fallbackKey, 1)
destPrefix, values, err = cd.getRatingPlansForPrefix(fallbackKey, 1)
}
//load the activation preriods
if err == nil && len(values) > 0 {
xaps := xCachedActivationPeriods{destPrefix, values, new(cache2go.XEntry)}
xaps := xCachedRatingPlans{destPrefix, values, new(cache2go.XEntry)}
xaps.XCache(cd.GetKey()+cd.Destination, debitPeriod+5*time.Second, xaps)
cd.ActivationPeriods = values
cd.RatingPlans = values
}
return
}
func (cd *CallDescriptor) getActivationPeriodsForPrefix(key string, recursionDepth int) (foundPrefix string, aps []*ActivationPeriod, err error) {
func (cd *CallDescriptor) getRatingPlansForPrefix(key string, recursionDepth int) (foundPrefix string, aps []*RatingPlan, err error) {
if recursionDepth > RECURSION_MAX_DEPTH {
err = errors.New("Max fallback recursion depth reached!" + key)
return
@@ -166,12 +166,12 @@ func (cd *CallDescriptor) getActivationPeriodsForPrefix(key string, recursionDep
if err != nil {
return "", nil, err
}
foundPrefix, aps, err = rp.GetActivationPeriodsForPrefix(cd.Destination)
foundPrefix, aps, err = rp.GetRatingPlansForPrefix(cd.Destination)
if err != nil {
if rp.FallbackKey != "" {
recursionDepth++
for _, fbk := range strings.Split(rp.FallbackKey, FALLBACK_SEP) {
if destPrefix, values, err := cd.getActivationPeriodsForPrefix(fbk, recursionDepth); err == nil {
if destPrefix, values, err := cd.getRatingPlansForPrefix(fbk, recursionDepth); err == nil {
return destPrefix, values, err
}
}
@@ -217,23 +217,23 @@ func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*Ti
if firstSpan.MinuteInfo != nil {
return // all the timespans are on minutes
}
if len(cd.ActivationPeriods) == 0 {
if len(cd.RatingPlans) == 0 {
return
}
firstSpan.ActivationPeriod = cd.ActivationPeriods[0]
firstSpan.RatingPlan = cd.RatingPlans[0]
// split on activation periods
afterStart, afterEnd := false, false //optimization for multiple activation periods
for _, ap := range cd.ActivationPeriods {
for _, ap := range cd.RatingPlans {
if !afterStart && !afterEnd && ap.ActivationTime.Before(cd.TimeStart) {
firstSpan.ActivationPeriod = ap
firstSpan.RatingPlan = ap
} else {
afterStart = true
for i := 0; i < len(timespans); i++ {
if timespans[i].MinuteInfo != nil {
continue
}
newTs := timespans[i].SplitByActivationPeriod(ap)
newTs := timespans[i].SplitByRatingPlan(ap)
if newTs != nil {
timespans = append(timespans, newTs)
} else {
@@ -248,16 +248,16 @@ func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*Ti
if timespans[i].MinuteInfo != nil {
continue // cont try to split timespans payed with minutes
}
ap := timespans[i].ActivationPeriod
//timespans[i].ActivationPeriod = nil
ap.Intervals.Sort()
for _, interval := range ap.Intervals {
if timespans[i].Interval != nil && timespans[i].Interval.Weight < interval.Weight {
ap := timespans[i].RatingPlan
//timespans[i].RatingPlan = nil
ap.RateIntervals.Sort()
for _, interval := range ap.RateIntervals {
if timespans[i].RateInterval != nil && timespans[i].RateInterval.Weight < interval.Weight {
continue // if the timespan has an interval than it already has a heigher weight
}
newTs := timespans[i].SplitByInterval(interval)
newTs := timespans[i].SplitByRateInterval(interval)
if newTs != nil {
newTs.ActivationPeriod = ap
newTs.RatingPlan = ap
timespans = append(timespans, newTs)
}
}
@@ -266,15 +266,20 @@ func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*Ti
return
}
// if the rate interval for any timespan has a RatingInterval larger than the timespan duration
// if the rate interval for any timespan has a RatingIncrement larger than the timespan duration
// the timespan must expand potentially overlaping folowing timespans and may exceed call
// descriptor's initial duration
func (cd *CallDescriptor) expandTimeSpans(timespans []*TimeSpan) []*TimeSpan {
for i, ts := range timespans {
if ts.Interval != nil {
_, rateIncrement, _ := ts.Interval.GetPriceParameters(ts.GetGroupStart())
if ts.RateInterval != nil {
_, rateIncrement, _ := ts.RateInterval.GetRateParameters(ts.GetGroupStart())
// 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())
}
if rateIncrement > ts.GetDuration() {
ts.TimeEnd = ts.TimeStart.Add(rateIncrement)
ts.SetNewCallDuration(ts) // set new call duration for this timespan
// overlap the rest of the timespans
for ; i < len(timespans); i++ {
if timespans[i].TimeEnd.Before(ts.TimeEnd) {
@@ -301,7 +306,7 @@ func (cd *CallDescriptor) expandTimeSpans(timespans []*TimeSpan) []*TimeSpan {
Creates a CallCost structure with the cost information calculated for the received CallDescriptor.
*/
func (cd *CallDescriptor) GetCost() (*CallCost, error) {
destPrefix, err := cd.LoadActivationPeriods()
destPrefix, err := cd.LoadRatingPlans()
if err != nil {
Logger.Err(fmt.Sprintf("error getting cost for key %v: %v", cd.GetUserBalanceKey(), err))
return &CallCost{Cost: -1}, err
@@ -312,8 +317,8 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) {
for i, ts := range timespans {
// only add connect fee if this is the first/only call cost request
if cd.LoopIndex == 0 && i == 0 && ts.MinuteInfo == nil && ts.Interval != nil {
connectionFee = ts.Interval.ConnectFee
if cd.LoopIndex == 0 && i == 0 && ts.MinuteInfo == nil && ts.RateInterval != nil {
connectionFee = ts.RateInterval.ConnectFee
}
cost += ts.getCost(cd)
}
@@ -339,7 +344,7 @@ 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) {
_, err = cd.LoadActivationPeriods()
_, err = cd.LoadRatingPlans()
if err != nil {
Logger.Err(fmt.Sprintf("error getting cost for key %v: %v", cd.GetUserBalanceKey(), err))
return 0, err
@@ -371,8 +376,8 @@ func (cd *CallDescriptor) GetMaxSessionTime(startTime time.Time) (seconds float6
cost := 0.0
for i, ts := range timespans {
if i == 0 && ts.MinuteInfo == nil && ts.Interval != nil {
cost += ts.Interval.ConnectFee
if i == 0 && ts.MinuteInfo == nil && ts.RateInterval != nil {
cost += ts.RateInterval.ConnectFee
}
cost += ts.getCost(cd)
}

View File

@@ -68,10 +68,10 @@ func TestSplitSpans(t *testing.T) {
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd.LoadActivationPeriods()
cd.LoadRatingPlans()
timespans := cd.splitInTimeSpans(nil)
if len(timespans) != 2 {
t.Log(cd.ActivationPeriods)
t.Log(cd.RatingPlans)
t.Error("Wrong number of timespans: ", len(timespans))
}
}
@@ -116,7 +116,7 @@ func TestFullDestNotFound(t *testing.T) {
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0256", Cost: 2700, ConnectFee: 1}
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
t.Log(cd.ActivationPeriods)
t.Log(cd.RatingPlans)
t.Errorf("Expected %v was %v", expected, result)
}
}
@@ -128,12 +128,12 @@ func TestSubjectNotFound(t *testing.T) {
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0257", Cost: 2700, ConnectFee: 1}
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
t.Log(cd.ActivationPeriods)
t.Log(cd.RatingPlans)
t.Errorf("Expected %v was %v", expected, result)
}
}
func TestMultipleActivationPeriods(t *testing.T) {
func TestMultipleRatingPlans(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{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0257308200", TimeStart: t1, TimeEnd: t2}
@@ -145,7 +145,7 @@ func TestMultipleActivationPeriods(t *testing.T) {
}
}
func TestSpansMultipleActivationPeriods(t *testing.T) {
func TestSpansMultipleRatingPlans(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{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0257308200", TimeStart: t1, TimeEnd: t2}
@@ -242,7 +242,7 @@ func BenchmarkStorageRestoring(b *testing.B) {
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.LoadActivationPeriods()
cd.LoadRatingPlans()
}
}
@@ -262,7 +262,7 @@ 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{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd.LoadActivationPeriods()
cd.LoadRatingPlans()
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.splitInTimeSpans(nil)

File diff suppressed because one or more lines are too long

View File

@@ -39,9 +39,9 @@ type CSVReader struct {
accountActions []*UserBalance
destinations []*Destination
timings map[string]*Timing
rates map[string]*Rate
rates map[string]*LoadRate
destinationRates map[string][]*DestinationRate
activationPeriods map[string]*ActivationPeriod
activationPeriods map[string]*RatingPlan
ratingProfiles map[string]*RatingProfile
// file names
destinationsFn, ratesFn, destinationratesFn, timingsFn, destinationratetimingsFn, ratingprofilesFn,
@@ -55,10 +55,10 @@ func NewFileCSVReader(storage DataStorage, sep rune, destinationsFn, timingsFn,
c.actions = make(map[string][]*Action)
c.actionsTimings = make(map[string][]*ActionTiming)
c.actionsTriggers = make(map[string][]*ActionTrigger)
c.rates = make(map[string]*Rate)
c.rates = make(map[string]*LoadRate)
c.destinationRates = make(map[string][]*DestinationRate)
c.timings = make(map[string]*Timing)
c.activationPeriods = make(map[string]*ActivationPeriod)
c.activationPeriods = make(map[string]*RatingPlan)
c.ratingProfiles = make(map[string]*RatingProfile)
c.readerFunc = openFileCSVReader
c.destinationsFn, c.timingsFn, c.ratesFn, c.destinationratesFn, c.destinationratetimingsFn, c.ratingprofilesFn,
@@ -223,8 +223,8 @@ func (csvr *CSVReader) LoadRates() (err error) {
}
for record, err := csvReader.Read(); err == nil; record, err = csvReader.Read() {
tag := record[0]
var r *Rate
r, err = NewRate(record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7], record[8])
var r *LoadRate
r, err = NewLoadRate(record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7], record[8])
if err != nil {
return err
}
@@ -285,9 +285,9 @@ func (csvr *CSVReader) LoadDestinationRateTimings() (err error) {
}
for _, dr := range drs {
if _, exists := csvr.activationPeriods[tag]; !exists {
csvr.activationPeriods[tag] = &ActivationPeriod{}
csvr.activationPeriods[tag] = &RatingPlan{}
}
csvr.activationPeriods[tag].AddInterval(rt.GetInterval(dr))
csvr.activationPeriods[tag].AddRateInterval(rt.GetRateInterval(dr))
}
}
return
@@ -320,10 +320,10 @@ func (csvr *CSVReader) LoadRatingProfiles() (err error) {
if !exists {
return errors.New(fmt.Sprintf("Could not load ratinTiming for tag: %v", record[5]))
}
newAP := &ActivationPeriod{ActivationTime: at}
newAP := &RatingPlan{ActivationTime: at}
//copy(newAP.Intervals, ap.Intervals)
newAP.Intervals = append(newAP.Intervals, ap.Intervals...)
rp.AddActivationPeriodIfNotPresent(d.Id, newAP)
newAP.RateIntervals = append(newAP.RateIntervals, ap.RateIntervals...)
rp.AddRatingPlanIfNotPresent(d.Id, newAP)
if fallbacksubject != "" {
rp.FallbackKey = fmt.Sprintf("%s:%s:%s:%s", direction, tenant, tor, fallbacksubject)
}
@@ -411,7 +411,7 @@ func (csvr *CSVReader) LoadActionTimings() (err error) {
Id: utils.GenUUID(),
Tag: record[2],
Weight: weight,
Timing: &Interval{
Timing: &RateInterval{
Months: t.Months,
MonthDays: t.MonthDays,
WeekDays: t.WeekDays,

View File

@@ -35,9 +35,9 @@ type DbReader struct {
accountActions []*UserBalance
destinations []*Destination
timings map[string]*Timing
rates map[string]*Rate
rates map[string]*LoadRate
destinationRates map[string][]*DestinationRate
activationPeriods map[string]*ActivationPeriod
activationPeriods map[string]*RatingPlan
ratingProfiles map[string]*RatingProfile
}
@@ -46,7 +46,7 @@ func NewDbReader(storDB LoadStorage, storage DataStorage, tpid string) *DbReader
c.storDb = storDB
c.dataDb = storage
c.tpid = tpid
c.activationPeriods = make(map[string]*ActivationPeriod)
c.activationPeriods = make(map[string]*RatingPlan)
c.actionsTimings = make(map[string][]*ActionTiming)
return c
}
@@ -169,9 +169,9 @@ func (dbr *DbReader) LoadDestinationRateTimings() error {
for _, dr := range drs {
_, exists := dbr.activationPeriods[rt.Tag]
if !exists {
dbr.activationPeriods[rt.Tag] = &ActivationPeriod{}
dbr.activationPeriods[rt.Tag] = &RatingPlan{}
}
dbr.activationPeriods[rt.Tag].AddInterval(rt.GetInterval(dr))
dbr.activationPeriods[rt.Tag].AddRateInterval(rt.GetRateInterval(dr))
}
}
return nil
@@ -192,10 +192,10 @@ func (dbr *DbReader) LoadRatingProfiles() error {
if !exists {
return errors.New(fmt.Sprintf("Could not load rating timing for tag: %v", rp.DestRatesTimingTag))
}
newAP := &ActivationPeriod{ActivationTime: at}
newAP := &RatingPlan{ActivationTime: at}
//copy(newAP.Intervals, ap.Intervals)
newAP.Intervals = append(newAP.Intervals, ap.Intervals...)
rp.AddActivationPeriodIfNotPresent(d.Id, newAP)
newAP.RateIntervals = append(newAP.RateIntervals, ap.RateIntervals...)
rp.AddRatingPlanIfNotPresent(d.Id, newAP)
}
}
@@ -203,7 +203,7 @@ func (dbr *DbReader) LoadRatingProfiles() error {
}
func (dbr *DbReader) LoadRatingProfileByTag(tag string) error {
activationPeriods := make(map[string]*ActivationPeriod)
activationPeriods := make(map[string]*RatingPlan)
resultRatingProfile := &RatingProfile{}
rpm, err := dbr.storDb.GetTpRatingProfiles(dbr.tpid, tag)
if err != nil || len(rpm) == 0 {
@@ -242,9 +242,9 @@ func (dbr *DbReader) LoadRatingProfileByTag(tag string) error {
Logger.Debug(fmt.Sprintf("Rate: %v", rpm))
drate.Rate = rt[drate.RateTag]
if _, exists := activationPeriods[destrateTiming.Tag]; !exists {
activationPeriods[destrateTiming.Tag] = &ActivationPeriod{}
activationPeriods[destrateTiming.Tag] = &RatingPlan{}
}
activationPeriods[destrateTiming.Tag].AddInterval(destrateTiming.GetInterval(drate))
activationPeriods[destrateTiming.Tag].AddRateInterval(destrateTiming.GetRateInterval(drate))
dm, err := dbr.storDb.GetTpDestinations(dbr.tpid, drate.DestinationsTag)
if err != nil || len(dm) == 0 {
return fmt.Errorf("Could not get destination id %s: %v", drate.DestinationsTag, err)
@@ -253,9 +253,9 @@ func (dbr *DbReader) LoadRatingProfileByTag(tag string) error {
for _, destination := range dm {
Logger.Debug(fmt.Sprintf("Destination: %v", rpm))
ap := activationPeriods[ratingProfile.DestRatesTimingTag]
newAP := &ActivationPeriod{ActivationTime: at}
newAP.Intervals = append(newAP.Intervals, ap.Intervals...)
resultRatingProfile.AddActivationPeriodIfNotPresent(destination.Id, newAP)
newAP := &RatingPlan{ActivationTime: at}
newAP.RateIntervals = append(newAP.RateIntervals, ap.RateIntervals...)
resultRatingProfile.AddRatingPlanIfNotPresent(destination.Id, newAP)
dbr.dataDb.SetDestination(destination)
}
}
@@ -288,7 +288,7 @@ func (dbr *DbReader) LoadActionTimings() (err error) {
Id: utils.GenUUID(),
Tag: at.Tag,
Weight: at.Weight,
Timing: &Interval{
Timing: &RateInterval{
Months: t.Months,
MonthDays: t.MonthDays,
WeekDays: t.WeekDays,
@@ -387,7 +387,7 @@ func (dbr *DbReader) LoadAccountActionsByTag(tag string) error {
Id: utils.GenUUID(),
Tag: at.Tag,
Weight: at.Weight,
Timing: &Interval{
Timing: &RateInterval{
Months: t.Months,
MonthDays: t.MonthDays,
WeekDays: t.WeekDays,

View File

@@ -46,7 +46,7 @@ type TPLoader interface {
WriteToDatabase(bool, bool) error
}
type Rate struct {
type LoadRate struct {
Tag string
ConnectFee, Price float64
RateUnit, RateIncrement, GroupIntervalStart time.Duration
@@ -55,7 +55,7 @@ type Rate struct {
Weight float64
}
func NewRate(tag, connectFee, price, ratedUnits, rateIncrements, groupInterval, roundingMethod, roundingDecimals, weight string) (r *Rate, err error) {
func NewLoadRate(tag, connectFee, price, ratedUnits, rateIncrements, groupInterval, roundingMethod, roundingDecimals, weight string) (r *LoadRate, err error) {
cf, err := strconv.ParseFloat(connectFee, 64)
if err != nil {
log.Printf("Error parsing connect fee from: %v", connectFee)
@@ -92,7 +92,7 @@ func NewRate(tag, connectFee, price, ratedUnits, rateIncrements, groupInterval,
return
}
r = &Rate{
r = &LoadRate{
Tag: tag,
ConnectFee: cf,
Price: p,
@@ -110,7 +110,7 @@ type DestinationRate struct {
Tag string
DestinationsTag string
RateTag string
Rate *Rate
Rate *LoadRate
}
type Timing struct {
@@ -155,8 +155,8 @@ func NewDestinationRateTiming(destinationRatesTag string, timing *Timing, weight
return
}
func (rt *DestinationRateTiming) GetInterval(dr *DestinationRate) (i *Interval) {
i = &Interval{
func (rt *DestinationRateTiming) GetRateInterval(dr *DestinationRate) (i *RateInterval) {
i = &RateInterval{
Years: rt.timing.Years,
Months: rt.timing.Months,
MonthDays: rt.timing.MonthDays,
@@ -164,7 +164,7 @@ func (rt *DestinationRateTiming) GetInterval(dr *DestinationRate) (i *Interval)
StartTime: rt.timing.StartTime,
Weight: rt.Weight,
ConnectFee: dr.Rate.ConnectFee,
Prices: PriceGroups{&Price{
Rates: RateGroups{&Rate{
GroupIntervalStart: dr.Rate.GroupIntervalStart,
Value: dr.Rate.Price,
RateIncrement: dr.Rate.RateIncrement,

View File

@@ -32,48 +32,48 @@ import (
/*
Defines a time interval for which a certain set of prices will apply
*/
type Interval struct {
type RateInterval struct {
Years Years
Months Months
MonthDays MonthDays
WeekDays WeekDays
StartTime, EndTime string // ##:##:## format
Weight, ConnectFee float64
Prices PriceGroups // GroupInterval (start time): Price
Rates RateGroups // GroupRateInterval (start time): Rate
RoundingMethod string
RoundingDecimals int
}
type Price struct {
type Rate struct {
GroupIntervalStart time.Duration
Value float64
RateIncrement time.Duration
RateUnit time.Duration
}
func (p *Price) Equal(o *Price) bool {
func (p *Rate) Equal(o *Rate) bool {
return p.GroupIntervalStart == o.GroupIntervalStart && p.Value == o.Value && p.RateIncrement == o.RateIncrement && p.RateUnit == o.RateUnit
}
type PriceGroups []*Price
type RateGroups []*Rate
func (pg PriceGroups) Len() int {
func (pg RateGroups) Len() int {
return len(pg)
}
func (pg PriceGroups) Swap(i, j int) {
func (pg RateGroups) Swap(i, j int) {
pg[i], pg[j] = pg[j], pg[i]
}
func (pg PriceGroups) Less(i, j int) bool {
func (pg RateGroups) Less(i, j int) bool {
return pg[i].GroupIntervalStart < pg[j].GroupIntervalStart
}
func (pg PriceGroups) Sort() {
func (pg RateGroups) Sort() {
sort.Sort(pg)
}
func (pg PriceGroups) Equal(og PriceGroups) bool {
func (pg RateGroups) Equal(og RateGroups) bool {
if len(pg) != len(og) {
return false
}
@@ -85,7 +85,7 @@ func (pg PriceGroups) Equal(og PriceGroups) bool {
return true
}
func (pg *PriceGroups) AddPrice(ps ...*Price) {
func (pg *RateGroups) AddRate(ps ...*Rate) {
for _, p := range ps {
found := false
for _, op := range *pg {
@@ -103,7 +103,7 @@ func (pg *PriceGroups) AddPrice(ps ...*Price) {
/*
Returns true if the received time result inside the interval
*/
func (i *Interval) Contains(t time.Time) bool {
func (i *RateInterval) Contains(t time.Time) bool {
// check for years
if len(i.Years) > 0 && !i.Years.Contains(t.Year()) {
return false
@@ -152,7 +152,7 @@ func (i *Interval) Contains(t time.Time) bool {
/*
Returns a time object that represents the end of the interval realtive to the received time
*/
func (i *Interval) getRightMargin(t time.Time) (rigthtTime time.Time) {
func (i *RateInterval) getRightMargin(t time.Time) (rigthtTime time.Time) {
year, month, day := t.Year(), t.Month(), t.Day()
hour, min, sec, nsec := 23, 59, 59, 0
loc := t.Location()
@@ -168,7 +168,7 @@ func (i *Interval) getRightMargin(t time.Time) (rigthtTime time.Time) {
/*
Returns a time object that represents the start of the interval realtive to the received time
*/
func (i *Interval) getLeftMargin(t time.Time) (rigthtTime time.Time) {
func (i *RateInterval) getLeftMargin(t time.Time) (rigthtTime time.Time) {
year, month, day := t.Year(), t.Month(), t.Day()
hour, min, sec, nsec := 0, 0, 0, 0
loc := t.Location()
@@ -181,11 +181,11 @@ func (i *Interval) getLeftMargin(t time.Time) (rigthtTime time.Time) {
return time.Date(year, month, day, hour, min, sec, nsec, loc)
}
func (i *Interval) String() string {
func (i *RateInterval) String() string {
return fmt.Sprintf("%v %v %v %v %v %v", i.Years, i.Months, i.MonthDays, i.WeekDays, i.StartTime, i.EndTime)
}
func (i *Interval) Equal(o *Interval) bool {
func (i *RateInterval) Equal(o *RateInterval) bool {
return reflect.DeepEqual(i.Years, o.Years) &&
reflect.DeepEqual(i.Months, o.Months) &&
reflect.DeepEqual(i.MonthDays, o.MonthDays) &&
@@ -194,8 +194,8 @@ func (i *Interval) Equal(o *Interval) bool {
i.EndTime == o.EndTime
}
func (i *Interval) GetCost(duration, startSecond time.Duration) (cost float64) {
price, rateIncrement, rateUnit := i.GetPriceParameters(startSecond)
func (i *RateInterval) GetCost(duration, startSecond time.Duration) (cost float64) {
price, rateIncrement, rateUnit := i.GetRateParameters(startSecond)
d := float64(duration.Seconds())
price /= rateUnit.Seconds()
ri := rateIncrement.Seconds()
@@ -204,11 +204,11 @@ func (i *Interval) GetCost(duration, startSecond time.Duration) (cost float64) {
}
// Gets the price for a the provided start second
func (i *Interval) GetPriceParameters(startSecond time.Duration) (price float64, rateIncrement, rateUnit time.Duration) {
i.Prices.Sort()
for index, price := range i.Prices {
if price.GroupIntervalStart <= startSecond && (index == len(i.Prices)-1 ||
i.Prices[index+1].GroupIntervalStart > startSecond) {
func (i *RateInterval) GetRateParameters(startSecond time.Duration) (price float64, rateIncrement, rateUnit time.Duration) {
i.Rates.Sort()
for index, price := range i.Rates {
if price.GroupIntervalStart <= startSecond && (index == len(i.Rates)-1 ||
i.Rates[index+1].GroupIntervalStart > startSecond) {
if price.RateIncrement == 0 {
price.RateIncrement = 1 * time.Second
}
@@ -222,20 +222,20 @@ func (i *Interval) GetPriceParameters(startSecond time.Duration) (price float64,
}
// Structure to store intervals according to weight
type IntervalList []*Interval
type RateIntervalList []*RateInterval
func (il IntervalList) Len() int {
func (il RateIntervalList) Len() int {
return len(il)
}
func (il IntervalList) Swap(i, j int) {
func (il RateIntervalList) Swap(i, j int) {
il[i], il[j] = il[j], il[i]
}
func (il IntervalList) Less(i, j int) bool {
func (il RateIntervalList) Less(i, j int) bool {
return il[i].Weight < il[j].Weight
}
func (il IntervalList) Sort() {
func (il RateIntervalList) Sort() {
sort.Sort(il)
}

View File

@@ -23,8 +23,8 @@ import (
"time"
)
func TestIntervalMonth(t *testing.T) {
i := &Interval{Months: Months{time.February}}
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) {
@@ -35,8 +35,8 @@ func TestIntervalMonth(t *testing.T) {
}
}
func TestIntervalMonthDay(t *testing.T) {
i := &Interval{MonthDays: MonthDays{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) {
@@ -47,8 +47,8 @@ func TestIntervalMonthDay(t *testing.T) {
}
}
func TestIntervalMonthAndMonthDay(t *testing.T) {
i := &Interval{Months: Months{time.February}, MonthDays: MonthDays{10}}
func TestRateIntervalMonthAndMonthDay(t *testing.T) {
i := &RateInterval{Months: Months{time.February}, 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)
d2 := time.Date(2012, time.January, 10, 23, 0, 0, 0, time.UTC)
@@ -63,9 +63,9 @@ func TestIntervalMonthAndMonthDay(t *testing.T) {
}
}
func TestIntervalWeekDays(t *testing.T) {
i := &Interval{WeekDays: []time.Weekday{time.Wednesday}}
i2 := &Interval{WeekDays: []time.Weekday{time.Wednesday, time.Thursday}}
func TestRateIntervalWeekDays(t *testing.T) {
i := &RateInterval{WeekDays: []time.Weekday{time.Wednesday}}
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) {
@@ -82,9 +82,9 @@ func TestIntervalWeekDays(t *testing.T) {
}
}
func TestIntervalMonthAndMonthDayAndWeekDays(t *testing.T) {
i := &Interval{Months: Months{time.February}, MonthDays: MonthDays{1}, WeekDays: []time.Weekday{time.Wednesday}}
i2 := &Interval{Months: Months{time.February}, MonthDays: MonthDays{2}, WeekDays: []time.Weekday{time.Wednesday, time.Thursday}}
func TestRateIntervalMonthAndMonthDayAndWeekDays(t *testing.T) {
i := &RateInterval{Months: Months{time.February}, MonthDays: MonthDays{1}, WeekDays: []time.Weekday{time.Wednesday}}
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) {
@@ -101,8 +101,8 @@ func TestIntervalMonthAndMonthDayAndWeekDays(t *testing.T) {
}
}
func TestIntervalHours(t *testing.T) {
i := &Interval{StartTime: "14:30:00", EndTime: "15:00:00"}
func TestRateIntervalHours(t *testing.T) {
i := &RateInterval{StartTime: "14:30:00", EndTime: "15:00:00"}
d := time.Date(2012, time.February, 10, 14, 30, 1, 0, time.UTC)
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)
@@ -121,8 +121,8 @@ func TestIntervalHours(t *testing.T) {
}
}
func TestIntervalEverything(t *testing.T) {
i := &Interval{Months: Months{time.February},
func TestRateIntervalEverything(t *testing.T) {
i := &RateInterval{Months: Months{time.February},
Years: Years{2012},
MonthDays: MonthDays{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
@@ -150,13 +150,13 @@ func TestIntervalEverything(t *testing.T) {
}
}
func TestIntervalEqual(t *testing.T) {
i1 := &Interval{Months: Months{time.February},
func TestRateIntervalEqual(t *testing.T) {
i1 := &RateInterval{Months: Months{time.February},
MonthDays: MonthDays{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
StartTime: "14:30:00",
EndTime: "15:00:00"}
i2 := &Interval{Months: Months{time.February},
i2 := &RateInterval{Months: Months{time.February},
MonthDays: MonthDays{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
StartTime: "14:30:00",
@@ -166,13 +166,13 @@ func TestIntervalEqual(t *testing.T) {
}
}
func TestIntervalNotEqual(t *testing.T) {
i1 := &Interval{Months: Months{time.February},
func TestRateIntervalNotEqual(t *testing.T) {
i1 := &RateInterval{Months: Months{time.February},
MonthDays: MonthDays{1},
WeekDays: []time.Weekday{time.Wednesday},
StartTime: "14:30:00",
EndTime: "15:00:00"}
i2 := &Interval{Months: Months{time.February},
i2 := &RateInterval{Months: Months{time.February},
MonthDays: MonthDays{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
StartTime: "14:30:00",
@@ -182,13 +182,13 @@ func TestIntervalNotEqual(t *testing.T) {
}
}
func TestIntervalGetCost(t *testing.T) {
func TestRateIntervalGetCost(t *testing.T) {
}
/*********************************Benchmarks**************************************/
func BenchmarkIntervalContainsDate(b *testing.B) {
i := &Interval{Months: Months{time.February}, MonthDays: MonthDays{1}, WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, StartTime: "14:30:00", EndTime: "15:00:00"}
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)

View File

@@ -26,36 +26,36 @@ import (
/*
The struture that is saved to storage.
*/
type ActivationPeriod struct {
type RatingPlan struct {
ActivationTime time.Time
Intervals IntervalList
RateIntervals RateIntervalList
}
type xCachedActivationPeriods struct {
type xCachedRatingPlans struct {
destPrefix string
aps []*ActivationPeriod
aps []*RatingPlan
*cache2go.XEntry
}
/*
Adds one ore more intervals to the internal interval list only if it is not allready in the list.
*/
func (ap *ActivationPeriod) AddInterval(is ...*Interval) {
func (ap *RatingPlan) AddRateInterval(is ...*RateInterval) {
for _, i := range is {
found := false
for _, ei := range ap.Intervals {
for _, ei := range ap.RateIntervals {
if i.Equal(ei) {
(&ei.Prices).AddPrice(i.Prices...)
(&ei.Rates).AddRate(i.Rates...)
found = true
break
}
}
if !found {
ap.Intervals = append(ap.Intervals, i)
ap.RateIntervals = append(ap.RateIntervals, i)
}
}
}
func (ap *ActivationPeriod) Equal(o *ActivationPeriod) bool {
func (ap *RatingPlan) Equal(o *RatingPlan) bool {
return ap.ActivationTime == o.ActivationTime
}

View File

@@ -32,23 +32,23 @@ func TestApRestoreFromStorage(t *testing.T) {
Tenant: "CUSTOMER_1",
Subject: "rif:from:tm",
Destination: "49"}
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 2 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
cd.LoadRatingPlans()
if len(cd.RatingPlans) != 2 {
t.Error("Error restoring activation periods: ", len(cd.RatingPlans))
}
}
func TestApStoreRestoreJson(t *testing.T) {
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{Months: []time.Month{time.February},
i := &RateInterval{Months: []time.Month{time.February},
MonthDays: []int{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
StartTime: "14:30:00",
EndTime: "15:00:00"}
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
ap := &RatingPlan{ActivationTime: d}
ap.AddRateInterval(i)
result, _ := json.Marshal(ap)
ap1 := &ActivationPeriod{}
ap1 := &RatingPlan{}
json.Unmarshal(result, ap1)
if !reflect.DeepEqual(ap, ap1) {
t.Errorf("Expected %v was %v", ap, ap1)
@@ -57,11 +57,11 @@ func TestApStoreRestoreJson(t *testing.T) {
func TestApStoreRestoreBlank(t *testing.T) {
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{}
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
i := &RateInterval{}
ap := &RatingPlan{ActivationTime: d}
ap.AddRateInterval(i)
result, _ := json.Marshal(ap)
ap1 := ActivationPeriod{}
ap1 := RatingPlan{}
json.Unmarshal(result, &ap1)
if reflect.DeepEqual(ap, ap1) {
t.Errorf("Expected %v was %v", ap, ap1)
@@ -70,116 +70,116 @@ func TestApStoreRestoreBlank(t *testing.T) {
func TestFallbackDirect(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "CUSTOMER_2", Subject: "danb:87.139.12.167", Destination: "41"}
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 1 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
cd.LoadRatingPlans()
if len(cd.RatingPlans) != 1 {
t.Error("Error restoring activation periods: ", len(cd.RatingPlans))
}
}
func TestFallbackMultiple(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "fall", Destination: "0723045"}
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 1 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
cd.LoadRatingPlans()
if len(cd.RatingPlans) != 1 {
t.Error("Error restoring activation periods: ", len(cd.RatingPlans))
}
}
func TestFallbackWithBackTrace(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "CUSTOMER_2", Subject: "danb:87.139.12.167", Destination: "4123"}
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 1 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
cd.LoadRatingPlans()
if len(cd.RatingPlans) != 1 {
t.Error("Error restoring activation periods: ", len(cd.RatingPlans))
}
}
func TestFallbackDefault(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "one", Destination: "0723"}
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 1 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
cd.LoadRatingPlans()
if len(cd.RatingPlans) != 1 {
t.Error("Error restoring activation periods: ", len(cd.RatingPlans))
}
}
func TestFallbackNoInfiniteLoop(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "rif", Destination: "0721"}
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 0 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
cd.LoadRatingPlans()
if len(cd.RatingPlans) != 0 {
t.Error("Error restoring activation periods: ", len(cd.RatingPlans))
}
}
func TestFallbackNoInfiniteLoopSelf(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "inf", Destination: "0721"}
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 0 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
cd.LoadRatingPlans()
if len(cd.RatingPlans) != 0 {
t.Error("Error restoring activation periods: ", len(cd.RatingPlans))
}
}
func TestApAddIntervalIfNotPresent(t *testing.T) {
i1 := &Interval{Months: Months{time.February},
i1 := &RateInterval{Months: Months{time.February},
MonthDays: MonthDays{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
StartTime: "14:30:00",
EndTime: "15:00:00"}
i2 := &Interval{Months: Months{time.February},
i2 := &RateInterval{Months: Months{time.February},
MonthDays: MonthDays{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
StartTime: "14:30:00",
EndTime: "15:00:00"}
i3 := &Interval{Months: Months{time.February},
i3 := &RateInterval{Months: Months{time.February},
MonthDays: MonthDays{1},
WeekDays: []time.Weekday{time.Wednesday},
StartTime: "14:30:00",
EndTime: "15:00:00"}
ap := &ActivationPeriod{}
ap.AddInterval(i1)
ap.AddInterval(i2)
if len(ap.Intervals) != 1 {
ap := &RatingPlan{}
ap.AddRateInterval(i1)
ap.AddRateInterval(i2)
if len(ap.RateIntervals) != 1 {
t.Error("Wronfully appended interval ;)")
}
ap.AddInterval(i3)
if len(ap.Intervals) != 2 {
ap.AddRateInterval(i3)
if len(ap.RateIntervals) != 2 {
t.Error("Wronfully not appended interval ;)")
}
}
func TestApAddIntervalGroups(t *testing.T) {
i1 := &Interval{
Prices: PriceGroups{&Price{0, 1, 1 * time.Second, 1 * time.Second}},
func TestApAddRateIntervalGroups(t *testing.T) {
i1 := &RateInterval{
Rates: RateGroups{&Rate{0, 1, 1 * time.Second, 1 * time.Second}},
}
i2 := &Interval{
Prices: PriceGroups{&Price{30 * time.Second, 2, 1 * time.Second, 1 * time.Second}},
i2 := &RateInterval{
Rates: RateGroups{&Rate{30 * time.Second, 2, 1 * time.Second, 1 * time.Second}},
}
i3 := &Interval{
Prices: PriceGroups{&Price{30 * time.Second, 2, 1 * time.Second, 1 * time.Second}},
i3 := &RateInterval{
Rates: RateGroups{&Rate{30 * time.Second, 2, 1 * time.Second, 1 * time.Second}},
}
ap := &ActivationPeriod{}
ap.AddInterval(i1)
ap.AddInterval(i2)
ap.AddInterval(i3)
if len(ap.Intervals) != 1 {
ap := &RatingPlan{}
ap.AddRateInterval(i1)
ap.AddRateInterval(i2)
ap.AddRateInterval(i3)
if len(ap.RateIntervals) != 1 {
t.Error("Wronfully appended interval ;)")
}
if len(ap.Intervals[0].Prices) != 2 {
t.Error("Group prices not formed: ", ap.Intervals[0].Prices)
if len(ap.RateIntervals[0].Rates) != 2 {
t.Error("Group prices not formed: ", ap.RateIntervals[0].Rates)
}
}
/**************************** Benchmarks *************************************/
func BenchmarkActivationPeriodStoreRestoreJson(b *testing.B) {
func BenchmarkRatingPlanStoreRestoreJson(b *testing.B) {
b.StopTimer()
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{Months: []time.Month{time.February},
i := &RateInterval{Months: []time.Month{time.February},
MonthDays: []int{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
StartTime: "14:30:00",
EndTime: "15:00:00"}
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
ap := &RatingPlan{ActivationTime: d}
ap.AddRateInterval(i)
ap1 := ActivationPeriod{}
ap1 := RatingPlan{}
b.StartTimer()
for i := 0; i < b.N; i++ {
result, _ := json.Marshal(ap)
@@ -187,18 +187,18 @@ func BenchmarkActivationPeriodStoreRestoreJson(b *testing.B) {
}
}
func BenchmarkActivationPeriodStoreRestore(b *testing.B) {
func BenchmarkRatingPlanStoreRestore(b *testing.B) {
b.StopTimer()
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{Months: []time.Month{time.February},
i := &RateInterval{Months: []time.Month{time.February},
MonthDays: []int{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
StartTime: "14:30:00",
EndTime: "15:00:00"}
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
ap := &RatingPlan{ActivationTime: d}
ap.AddRateInterval(i)
ap1 := &ActivationPeriod{}
ap1 := &RatingPlan{}
b.StartTimer()
for i := 0; i < b.N; i++ {
result, _ := marsh.Marshal(ap)

View File

@@ -26,14 +26,14 @@ import (
type RatingProfile struct {
Id string
FallbackKey string // FallbackKey is used as complete combination of Tenant:TOR:Direction:Subject
DestinationMap map[string][]*ActivationPeriod
DestinationMap map[string][]*RatingPlan
Tag, Tenant, TOR, Direction, Subject, DestRatesTimingTag, RatesFallbackSubject, ActivationTime string // used only for loading
}
// Adds an activation period that applyes to current rating profile if not already present.
func (rp *RatingProfile) AddActivationPeriodIfNotPresent(destInfo string, aps ...*ActivationPeriod) {
func (rp *RatingProfile) AddRatingPlanIfNotPresent(destInfo string, aps ...*RatingPlan) {
if rp.DestinationMap == nil {
rp.DestinationMap = make(map[string][]*ActivationPeriod, 1)
rp.DestinationMap = make(map[string][]*RatingPlan, 1)
}
for _, ap := range aps {
found := false
@@ -49,7 +49,7 @@ func (rp *RatingProfile) AddActivationPeriodIfNotPresent(destInfo string, aps ..
}
}
func (rp *RatingProfile) GetActivationPeriodsForPrefix(destPrefix string) (foundPrefix string, aps []*ActivationPeriod, err error) {
func (rp *RatingProfile) GetRatingPlansForPrefix(destPrefix string) (foundPrefix string, aps []*RatingPlan, err error) {
bestPrecision := 0
for k, v := range rp.DestinationMap {
d, err := GetDestination(k)

View File

@@ -24,16 +24,16 @@ import (
)
func TestRpAddAPIfNotPresent(t *testing.T) {
ap1 := &ActivationPeriod{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 0, time.UTC)}
ap2 := &ActivationPeriod{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 0, time.UTC)}
ap3 := &ActivationPeriod{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 1, time.UTC)}
ap1 := &RatingPlan{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 0, time.UTC)}
ap2 := &RatingPlan{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 0, time.UTC)}
ap3 := &RatingPlan{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 1, time.UTC)}
rp := &RatingProfile{}
rp.AddActivationPeriodIfNotPresent("test", ap1)
rp.AddActivationPeriodIfNotPresent("test", ap2)
rp.AddRatingPlanIfNotPresent("test", ap1)
rp.AddRatingPlanIfNotPresent("test", ap2)
if len(rp.DestinationMap["test"]) != 1 {
t.Error("Wronfully appended activation period ;)", len(rp.DestinationMap["test"]))
}
rp.AddActivationPeriodIfNotPresent("test", ap3)
rp.AddRatingPlanIfNotPresent("test", ap3)
if len(rp.DestinationMap["test"]) != 2 {
t.Error("Wronfully not appended activation period ;)", len(rp.DestinationMap["test"]))
}

View File

@@ -110,7 +110,7 @@ type LoadStorage interface {
GetTPDestination(string, string) (*Destination, error)
GetTPDestinationIds(string) ([]string, error)
ExistsTPRate(string, string) (bool, error)
SetTPRates(string, map[string][]*Rate) error
SetTPRates(string, map[string][]*LoadRate) error
GetTPRate(string, string) (*utils.TPRate, error)
GetTPRateIds(string) ([]string, error)
ExistsTPDestinationRate(string, string) (bool, error)
@@ -142,7 +142,7 @@ type LoadStorage interface {
// loader functions
GetTpDestinations(string, string) ([]*Destination, error)
GetTpTimings(string, string) (map[string]*Timing, error)
GetTpRates(string, string) (map[string]*Rate, error)
GetTpRates(string, string) (map[string]*LoadRate, error)
GetTpDestinationRates(string, string) (map[string][]*DestinationRate, error)
GetTpDestinationRateTimings(string, string) ([]*DestinationRateTiming, error)
GetTpRatingProfiles(string, string) (map[string]*RatingProfile, error)

View File

@@ -192,7 +192,7 @@ func (self *SQLStorage) ExistsTPRate(tpid, rtId string) (bool, error) {
return exists, nil
}
func (self *SQLStorage) SetTPRates(tpid string, rts map[string][]*Rate) error {
func (self *SQLStorage) SetTPRates(tpid string, rts map[string][]*LoadRate) error {
if len(rts) == 0 {
return nil //Nothing to set
}
@@ -541,7 +541,7 @@ func (self *SQLStorage) SetTPActions(tpid string, acts map[string][]*Action) err
}
qry += fmt.Sprintf("('%s','%s','%s','%s','%s',%f,'%s','%s','%s',%f,%f,%f)",
tpid, actId, act.ActionType, act.BalanceId, act.Direction, act.Balance.Value, act.ExpirationString,
act.DestinationTag, act.RateType, act.RateValue, act.MinutesWeight, act.Weight)
act.Balance.DestinationId, act.Balance.SpecialPriceType, act.Balance.SpecialPrice, act.Balance.Weight, act.Weight)
i++
}
}
@@ -921,8 +921,8 @@ func (self *SQLStorage) GetTpDestinations(tpid, tag string) ([]*Destination, err
return dests, nil
}
func (self *SQLStorage) GetTpRates(tpid, tag string) (map[string]*Rate, error) {
rts := make(map[string]*Rate)
func (self *SQLStorage) GetTpRates(tpid, tag string) (map[string]*LoadRate, error) {
rts := make(map[string]*LoadRate)
q := fmt.Sprintf("SELECT tag, connect_fee, rate, rate_unit, rate_increment, group_interval_start, rounding_method, rounding_decimals, weight FROM %s WHERE tpid='%s' ", utils.TBL_TP_RATES, tpid)
if tag != "" {
q += fmt.Sprintf(" AND tag='%s'", tag)
@@ -940,7 +940,7 @@ func (self *SQLStorage) GetTpRates(tpid, tag string) (map[string]*Rate, error) {
if err := rows.Scan(&tag, &connect_fee, &rate, &rate_unit, &rate_increment, &group_interval_start, &roundingMethod, &roundingDecimals, &weight); err != nil {
return nil, err
}
r := &Rate{
r := &LoadRate{
Tag: tag,
ConnectFee: connect_fee,
Price: rate,

View File

@@ -104,16 +104,16 @@ func GetUB() *UserBalance {
func BenchmarkMarshallerJSONStoreRestore(b *testing.B) {
b.StopTimer()
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{Months: []time.Month{time.February},
i := &RateInterval{Months: []time.Month{time.February},
MonthDays: []int{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
StartTime: "14:30:00",
EndTime: "15:00:00"}
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
ap := &RatingPlan{ActivationTime: d}
ap.AddRateInterval(i)
ub := GetUB()
ap1 := ActivationPeriod{}
ap1 := RatingPlan{}
ub1 := &UserBalance{}
b.StartTimer()
ms := new(JSONMarshaler)
@@ -128,16 +128,16 @@ func BenchmarkMarshallerJSONStoreRestore(b *testing.B) {
func BenchmarkMarshallerBSONStoreRestore(b *testing.B) {
b.StopTimer()
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{Months: []time.Month{time.February},
i := &RateInterval{Months: []time.Month{time.February},
MonthDays: []int{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
StartTime: "14:30:00",
EndTime: "15:00:00"}
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
ap := &RatingPlan{ActivationTime: d}
ap.AddRateInterval(i)
ub := GetUB()
ap1 := ActivationPeriod{}
ap1 := RatingPlan{}
ub1 := &UserBalance{}
b.StartTimer()
ms := new(BSONMarshaler)
@@ -152,16 +152,16 @@ func BenchmarkMarshallerBSONStoreRestore(b *testing.B) {
func BenchmarkMarshallerJSONBufStoreRestore(b *testing.B) {
b.StopTimer()
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{Months: []time.Month{time.February},
i := &RateInterval{Months: []time.Month{time.February},
MonthDays: []int{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
StartTime: "14:30:00",
EndTime: "15:00:00"}
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
ap := &RatingPlan{ActivationTime: d}
ap.AddRateInterval(i)
ub := GetUB()
ap1 := ActivationPeriod{}
ap1 := RatingPlan{}
ub1 := &UserBalance{}
b.StartTimer()
ms := new(JSONBufMarshaler)
@@ -176,16 +176,16 @@ func BenchmarkMarshallerJSONBufStoreRestore(b *testing.B) {
func BenchmarkMarshallerGOBStoreRestore(b *testing.B) {
b.StopTimer()
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{Months: []time.Month{time.February},
i := &RateInterval{Months: []time.Month{time.February},
MonthDays: []int{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
StartTime: "14:30:00",
EndTime: "15:00:00"}
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
ap := &RatingPlan{ActivationTime: d}
ap.AddRateInterval(i)
ub := GetUB()
ap1 := ActivationPeriod{}
ap1 := RatingPlan{}
ub1 := &UserBalance{}
b.StartTimer()
ms := new(GOBMarshaler)
@@ -200,16 +200,16 @@ func BenchmarkMarshallerGOBStoreRestore(b *testing.B) {
func BenchmarkMarshallerMsgpackStoreRestore(b *testing.B) {
b.StopTimer()
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{Months: []time.Month{time.February},
i := &RateInterval{Months: []time.Month{time.February},
MonthDays: []int{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
StartTime: "14:30:00",
EndTime: "15:00:00"}
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
ap := &RatingPlan{ActivationTime: d}
ap.AddRateInterval(i)
ub := GetUB()
ap1 := ActivationPeriod{}
ap1 := RatingPlan{}
ub1 := &UserBalance{}
b.StartTimer()
ms := new(MsgpackMarshaler)
@@ -225,16 +225,16 @@ func BenchmarkMarshallerMsgpackStoreRestore(b *testing.B) {
func BenchmarkMarshallerCodecMsgpackStoreRestore(b *testing.B) {
b.StopTimer()
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{Months: []time.Month{time.February},
i := &RateInterval{Months: []time.Month{time.February},
MonthDays: []int{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
StartTime: "14:30:00",
EndTime: "15:00:00"}
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
ap := &RatingPlan{ActivationTime: d}
ap.AddRateInterval(i)
ub := GetUB()
ap1 := ActivationPeriod{}
ap1 := RatingPlan{}
ub1 := &UserBalance{}
b.StartTimer()
ms := NewCodecMsgpackMarshaler()
@@ -249,16 +249,16 @@ func BenchmarkMarshallerCodecMsgpackStoreRestore(b *testing.B) {
func BenchmarkMarshallerBincStoreRestore(b *testing.B) {
b.StopTimer()
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{Months: []time.Month{time.February},
i := &RateInterval{Months: []time.Month{time.February},
MonthDays: []int{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
StartTime: "14:30:00",
EndTime: "15:00:00"}
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
ap := &RatingPlan{ActivationTime: d}
ap.AddRateInterval(i)
ub := GetUB()
ap1 := ActivationPeriod{}
ap1 := RatingPlan{}
ub1 := &UserBalance{}
b.StartTimer()
ms := NewBincMarshaler()

View File

@@ -29,8 +29,8 @@ A unit in which a call will be split that has a specific price related interval
type TimeSpan struct {
TimeStart, TimeEnd time.Time
Cost float64
ActivationPeriod *ActivationPeriod
Interval *Interval
RatingPlan *RatingPlan
RateInterval *RateInterval
MinuteInfo *MinuteInfo
CallDuration time.Duration // the call duration so far till TimeEnd
overlapped bool // mark a timespan as overlapped by an expanded one
@@ -43,9 +43,7 @@ type MinuteInfo struct {
Price float64
}
/*
Returns the duration of the timespan
*/
// Returns the duration of the timespan
func (ts *TimeSpan) GetDuration() time.Duration {
return ts.TimeEnd.Sub(ts.TimeStart)
}
@@ -57,18 +55,11 @@ func (ts *TimeSpan) getCost(cd *CallDescriptor) (cost float64) {
if ts.MinuteInfo != nil {
return ts.GetDuration().Seconds() * ts.MinuteInfo.Price
}
if ts.Interval == nil {
if ts.RateInterval == nil {
return 0
}
i := ts.Interval
i := ts.RateInterval
cost = i.GetCost(ts.GetDuration(), ts.GetGroupStart())
// if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
// userBalance.mux.RLock()
// if percentageDiscount, err := userBalance.getVolumeDiscount(cd.Destination, INBOUND); err == nil && percentageDiscount > 0 {
// cost *= (100 - percentageDiscount) / 100
// }
// userBalance.mux.RUnlock()
// }
ts.Cost = cost
return
}
@@ -84,15 +75,15 @@ func (ts *TimeSpan) Contains(t time.Time) bool {
Will set the interval as spans's interval if new Weight is lower then span's interval Weight
or if the Weights are equal and new price is lower then spans's interval price
*/
func (ts *TimeSpan) SetInterval(i *Interval) {
if ts.Interval == nil || ts.Interval.Weight < i.Weight {
ts.Interval = i
func (ts *TimeSpan) SetRateInterval(i *RateInterval) {
if ts.RateInterval == nil || ts.RateInterval.Weight < i.Weight {
ts.RateInterval = i
return
}
iPrice, _, _ := i.GetPriceParameters(ts.GetGroupStart())
tsPrice, _, _ := ts.Interval.GetPriceParameters(ts.GetGroupStart())
if ts.Interval.Weight == i.Weight && iPrice < tsPrice {
ts.Interval = i
iPrice, _, _ := i.GetRateParameters(ts.GetGroupStart())
tsPrice, _, _ := ts.RateInterval.GetRateParameters(ts.GetGroupStart())
if ts.RateInterval.Weight == i.Weight && iPrice < tsPrice {
ts.RateInterval = i
}
}
@@ -102,7 +93,7 @@ It will modify the endtime of the received timespan and it will return
a new timespan starting from the end of the received one.
The interval will attach itself to the timespan that overlaps the interval.
*/
func (ts *TimeSpan) SplitByInterval(i *Interval) (nts *TimeSpan) {
func (ts *TimeSpan) SplitByRateInterval(i *RateInterval) (nts *TimeSpan) {
//Logger.Debug("here: ", ts, " +++ ", i)
// if the span is not in interval return nil
@@ -111,14 +102,14 @@ func (ts *TimeSpan) SplitByInterval(i *Interval) (nts *TimeSpan) {
return
}
// split by GroupStart
i.Prices.Sort()
for _, price := range i.Prices {
i.Rates.Sort()
for _, price := range i.Rates {
if ts.GetGroupStart() < price.GroupIntervalStart && ts.GetGroupEnd() >= price.GroupIntervalStart {
ts.SetInterval(i)
ts.SetRateInterval(i)
splitTime := ts.TimeStart.Add(price.GroupIntervalStart - ts.GetGroupStart())
nts = &TimeSpan{TimeStart: splitTime, TimeEnd: ts.TimeEnd}
ts.TimeEnd = splitTime
nts.SetInterval(i)
nts.SetRateInterval(i)
nts.CallDuration = ts.CallDuration
ts.SetNewCallDuration(nts)
@@ -129,14 +120,14 @@ func (ts *TimeSpan) SplitByInterval(i *Interval) (nts *TimeSpan) {
// 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) {
//Logger.Debug("All in interval")
ts.SetInterval(i)
ts.SetRateInterval(i)
return
}
// if only the start time is in the interval split the interval to the right
if i.Contains(ts.TimeStart) {
//Logger.Debug("Start in interval")
splitTime := i.getRightMargin(ts.TimeStart)
ts.SetInterval(i)
ts.SetRateInterval(i)
if splitTime == ts.TimeStart {
return
}
@@ -157,7 +148,7 @@ func (ts *TimeSpan) SplitByInterval(i *Interval) (nts *TimeSpan) {
nts = &TimeSpan{TimeStart: splitTime, TimeEnd: ts.TimeEnd}
ts.TimeEnd = splitTime
nts.SetInterval(i)
nts.SetRateInterval(i)
nts.CallDuration = ts.CallDuration
ts.SetNewCallDuration(nts)
@@ -169,11 +160,11 @@ func (ts *TimeSpan) SplitByInterval(i *Interval) (nts *TimeSpan) {
/*
Splits the given timespan on activation period's activation time.
*/
func (ts *TimeSpan) SplitByActivationPeriod(ap *ActivationPeriod) (newTs *TimeSpan) {
func (ts *TimeSpan) SplitByRatingPlan(ap *RatingPlan) (newTs *TimeSpan) {
if !ts.Contains(ap.ActivationTime) {
return nil
}
newTs = &TimeSpan{TimeStart: ap.ActivationTime, TimeEnd: ts.TimeEnd, ActivationPeriod: ap}
newTs = &TimeSpan{TimeStart: ap.ActivationTime, TimeEnd: ts.TimeEnd, RatingPlan: ap}
newTs.CallDuration = ts.CallDuration
ts.TimeEnd = ap.ActivationTime
ts.SetNewCallDuration(newTs)
@@ -217,6 +208,7 @@ func (ts *TimeSpan) SplitByMinuteBalance(mb *Balance) (newTs *TimeSpan) {
return
}
// Returns the starting time of this timespan
func (ts *TimeSpan) GetGroupStart() time.Duration {
s := ts.CallDuration - ts.GetDuration()
if s < 0 {
@@ -229,6 +221,7 @@ func (ts *TimeSpan) GetGroupEnd() time.Duration {
return ts.CallDuration
}
// sets the CallDuration attribute to reflect new timespan
func (ts *TimeSpan) SetNewCallDuration(nts *TimeSpan) {
d := ts.CallDuration - nts.GetDuration()
if d < 0 {

View File

@@ -24,24 +24,24 @@ import (
)
func TestRightMargin(t *testing.T) {
i := &Interval{WeekDays: []time.Weekday{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}}
i := &RateInterval{WeekDays: []time.Weekday{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}}
t1 := time.Date(2012, time.February, 3, 23, 45, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 4, 0, 10, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(i)
nts := ts.SplitByRateInterval(i)
if ts.TimeStart != t1 || ts.TimeEnd != time.Date(2012, time.February, 3, 23, 59, 59, 0, time.UTC) {
t.Error("Incorrect first half", ts)
}
if nts.TimeStart != time.Date(2012, time.February, 3, 23, 59, 59, 0, time.UTC) || nts.TimeEnd != t2 {
t.Error("Incorrect second half", nts)
}
if ts.Interval != i {
t.Error("Interval not attached correctly")
if ts.RateInterval != i {
t.Error("RateInterval not attached correctly")
}
if ts.GetDuration().Seconds() != 15*60-1 || nts.GetDuration().Seconds() != 10*60+1 {
t.Error("Wrong durations.for Intervals", ts.GetDuration().Seconds(), ts.GetDuration().Seconds())
t.Error("Wrong durations.for RateIntervals", ts.GetDuration().Seconds(), ts.GetDuration().Seconds())
}
if ts.GetDuration().Seconds()+nts.GetDuration().Seconds() != oldDuration.Seconds() {
@@ -50,24 +50,24 @@ func TestRightMargin(t *testing.T) {
}
func TestRightHourMargin(t *testing.T) {
i := &Interval{WeekDays: []time.Weekday{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}, EndTime: "17:59:00"}
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)
t2 := time.Date(2012, time.February, 3, 18, 00, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(i)
nts := ts.SplitByRateInterval(i)
if ts.TimeStart != t1 || ts.TimeEnd != time.Date(2012, time.February, 3, 17, 59, 00, 0, time.UTC) {
t.Error("Incorrect first half", ts)
}
if nts.TimeStart != time.Date(2012, time.February, 3, 17, 59, 00, 0, time.UTC) || nts.TimeEnd != t2 {
t.Error("Incorrect second half", nts)
}
if ts.Interval != i {
t.Error("Interval not attached correctly")
if ts.RateInterval != i {
t.Error("RateInterval not attached correctly")
}
if ts.GetDuration().Seconds() != 29*60 || nts.GetDuration().Seconds() != 1*60 {
t.Error("Wrong durations.for Intervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
t.Error("Wrong durations.for RateIntervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
}
if ts.GetDuration().Seconds()+nts.GetDuration().Seconds() != oldDuration.Seconds() {
t.Errorf("The duration has changed: %v + %v != %v", ts.GetDuration().Seconds(), nts.GetDuration().Seconds(), oldDuration.Seconds())
@@ -75,23 +75,23 @@ func TestRightHourMargin(t *testing.T) {
}
func TestLeftMargin(t *testing.T) {
i := &Interval{WeekDays: []time.Weekday{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}}
i := &RateInterval{WeekDays: []time.Weekday{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}}
t1 := time.Date(2012, time.February, 5, 23, 45, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 6, 0, 10, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(i)
nts := ts.SplitByRateInterval(i)
if ts.TimeStart != t1 || ts.TimeEnd != time.Date(2012, time.February, 6, 0, 0, 0, 0, time.UTC) {
t.Error("Incorrect first half", ts)
}
if nts.TimeStart != time.Date(2012, time.February, 6, 0, 0, 0, 0, time.UTC) || nts.TimeEnd != t2 {
t.Error("Incorrect second half", nts)
}
if nts.Interval != i {
t.Error("Interval not attached correctly")
if nts.RateInterval != i {
t.Error("RateInterval not attached correctly")
}
if ts.GetDuration().Seconds() != 15*60 || nts.GetDuration().Seconds() != 10*60 {
t.Error("Wrong durations.for Intervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
t.Error("Wrong durations.for RateIntervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
}
if ts.GetDuration().Seconds()+nts.GetDuration().Seconds() != oldDuration.Seconds() {
t.Errorf("The duration has changed: %v + %v != %v", ts.GetDuration().Seconds(), nts.GetDuration().Seconds(), oldDuration.Seconds())
@@ -99,23 +99,23 @@ func TestLeftMargin(t *testing.T) {
}
func TestLeftHourMargin(t *testing.T) {
i := &Interval{Months: Months{time.December}, MonthDays: MonthDays{1}, StartTime: "09:00:00"}
i := &RateInterval{Months: Months{time.December}, MonthDays: MonthDays{1}, StartTime: "09:00:00"}
t1 := time.Date(2012, time.December, 1, 8, 45, 0, 0, time.UTC)
t2 := time.Date(2012, time.December, 1, 9, 20, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(i)
nts := ts.SplitByRateInterval(i)
if ts.TimeStart != t1 || ts.TimeEnd != time.Date(2012, time.December, 1, 9, 0, 0, 0, time.UTC) {
t.Error("Incorrect first half", ts)
}
if nts.TimeStart != time.Date(2012, time.December, 1, 9, 0, 0, 0, time.UTC) || nts.TimeEnd != t2 {
t.Error("Incorrect second half", nts)
}
if nts.Interval != i {
t.Error("Interval not attached correctly")
if nts.RateInterval != i {
t.Error("RateInterval not attached correctly")
}
if ts.GetDuration().Seconds() != 15*60 || nts.GetDuration().Seconds() != 20*60 {
t.Error("Wrong durations.for Intervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
t.Error("Wrong durations.for RateIntervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
}
if ts.GetDuration().Seconds()+nts.GetDuration().Seconds() != oldDuration.Seconds() {
t.Errorf("The duration has changed: %v + %v != %v", ts.GetDuration().Seconds(), nts.GetDuration().Seconds(), oldDuration.Seconds())
@@ -123,27 +123,27 @@ func TestLeftHourMargin(t *testing.T) {
}
func TestEnclosingMargin(t *testing.T) {
i := &Interval{WeekDays: []time.Weekday{time.Sunday}}
i := &RateInterval{WeekDays: []time.Weekday{time.Sunday}}
t1 := time.Date(2012, time.February, 5, 17, 45, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 5, 18, 10, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2}
nts := ts.SplitByInterval(i)
nts := ts.SplitByRateInterval(i)
if ts.TimeStart != t1 || ts.TimeEnd != t2 || nts != nil {
t.Error("Incorrect enclosing", ts)
}
if ts.Interval != i {
t.Error("Interval not attached correctly")
if ts.RateInterval != i {
t.Error("RateInterval not attached correctly")
}
}
func TestOutsideMargin(t *testing.T) {
i := &Interval{WeekDays: []time.Weekday{time.Monday}}
i := &RateInterval{WeekDays: []time.Weekday{time.Monday}}
t1 := time.Date(2012, time.February, 5, 17, 45, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 5, 18, 10, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2}
result := ts.SplitByInterval(i)
result := ts.SplitByRateInterval(i)
if result != nil {
t.Error("Interval not split correctly")
t.Error("RateInterval not split correctly")
}
}
@@ -168,17 +168,17 @@ func TestSplitByActivationTime(t *testing.T) {
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)
ts := TimeSpan{TimeStart: t1, TimeEnd: t2}
ap1 := &ActivationPeriod{ActivationTime: t1}
ap2 := &ActivationPeriod{ActivationTime: t2}
ap3 := &ActivationPeriod{ActivationTime: t3}
ap1 := &RatingPlan{ActivationTime: t1}
ap2 := &RatingPlan{ActivationTime: t2}
ap3 := &RatingPlan{ActivationTime: t3}
if ts.SplitByActivationPeriod(ap1) != nil {
if ts.SplitByRatingPlan(ap1) != nil {
t.Error("Error spliting on left margin")
}
if ts.SplitByActivationPeriod(ap2) != nil {
if ts.SplitByRatingPlan(ap2) != nil {
t.Error("Error spliting on right margin")
}
result := ts.SplitByActivationPeriod(ap3)
result := ts.SplitByRatingPlan(ap3)
if result.TimeStart != t3 || result.TimeEnd != t2 {
t.Error("Error spliting on interior")
}
@@ -192,27 +192,27 @@ func TestTimespanGetCost(t *testing.T) {
if ts1.getCost(cd) != 0 {
t.Error("No interval and still kicking")
}
ts1.Interval = &Interval{Prices: PriceGroups{&Price{0, 1.0, 1 * time.Second, 1 * time.Second}}}
ts1.RateInterval = &RateInterval{Rates: RateGroups{&Rate{0, 1.0, 1 * time.Second, 1 * time.Second}}}
if ts1.getCost(cd) != 600 {
t.Error("Expected 10 got ", ts1.getCost(cd))
}
ts1.Interval.Prices[0].RateUnit = 60 * time.Second
ts1.RateInterval.Rates[0].RateUnit = 60 * time.Second
if ts1.getCost(cd) != 10 {
t.Error("Expected 6000 got ", ts1.getCost(cd))
}
}
func TestSetInterval(t *testing.T) {
i1 := &Interval{Prices: PriceGroups{&Price{0, 1.0, 1 * time.Second, 1 * time.Second}}}
ts1 := TimeSpan{Interval: i1}
i2 := &Interval{Prices: PriceGroups{&Price{0, 2.0, 1 * time.Second, 1 * time.Second}}}
ts1.SetInterval(i2)
if ts1.Interval != i1 {
func TestSetRateInterval(t *testing.T) {
i1 := &RateInterval{Rates: RateGroups{&Rate{0, 1.0, 1 * time.Second, 1 * time.Second}}}
ts1 := TimeSpan{RateInterval: i1}
i2 := &RateInterval{Rates: RateGroups{&Rate{0, 2.0, 1 * time.Second, 1 * time.Second}}}
ts1.SetRateInterval(i2)
if ts1.RateInterval != i1 {
t.Error("Smaller price interval should win")
}
i2.Weight = 1
ts1.SetInterval(i2)
if ts1.Interval != i2 {
ts1.SetRateInterval(i2)
if ts1.RateInterval != i2 {
t.Error("Bigger ponder interval should win")
}
}
@@ -330,15 +330,15 @@ func TestTimespanSplitByMinuteBalanceScarceExpiringDifferentScarceFirst(t *testi
}
func TestTimespanSplitGroupedRates(t *testing.T) {
i := &Interval{
i := &RateInterval{
EndTime: "17:59:00",
Prices: PriceGroups{&Price{0, 2, 1 * time.Second, 1 * time.Second}, &Price{900 * time.Second, 1, 1 * time.Second, 1 * time.Second}},
Rates: RateGroups{&Rate{0, 2, 1 * time.Second, 1 * time.Second}, &Rate{900 * time.Second, 1, 1 * time.Second, 1 * time.Second}},
}
t1 := time.Date(2012, time.February, 3, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 3, 18, 00, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 1800 * time.Second}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(i)
nts := ts.SplitByRateInterval(i)
splitTime := time.Date(2012, time.February, 3, 17, 45, 00, 0, time.UTC)
if ts.TimeStart != t1 || ts.TimeEnd != splitTime {
t.Error("Incorrect first half", ts.TimeStart, ts.TimeEnd)
@@ -346,17 +346,17 @@ func TestTimespanSplitGroupedRates(t *testing.T) {
if nts.TimeStart != splitTime || nts.TimeEnd != t2 {
t.Error("Incorrect second half", nts)
}
if ts.Interval != i {
t.Error("Interval not attached correctly")
if ts.RateInterval != i {
t.Error("RateInterval not attached correctly")
}
c1 := ts.Interval.GetCost(ts.GetDuration(), ts.GetGroupStart())
c2 := nts.Interval.GetCost(nts.GetDuration(), nts.GetGroupStart())
c1 := ts.RateInterval.GetCost(ts.GetDuration(), ts.GetGroupStart())
c2 := nts.RateInterval.GetCost(nts.GetDuration(), nts.GetGroupStart())
if c1 != 1800 || c2 != 900 {
t.Error("Wrong costs: ", c1, c2)
}
if ts.GetDuration().Seconds() != 15*60 || nts.GetDuration().Seconds() != 15*60 {
t.Error("Wrong durations.for Intervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
t.Error("Wrong durations.for RateIntervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
}
if ts.GetDuration().Seconds()+nts.GetDuration().Seconds() != oldDuration.Seconds() {
t.Errorf("The duration has changed: %v + %v != %v", ts.GetDuration().Seconds(), nts.GetDuration().Seconds(), oldDuration.Seconds())
@@ -364,15 +364,15 @@ func TestTimespanSplitGroupedRates(t *testing.T) {
}
func TestTimespanSplitGroupedRatesIncrements(t *testing.T) {
i := &Interval{
i := &RateInterval{
EndTime: "17:59:00",
Prices: PriceGroups{&Price{0, 2, 1 * time.Second, 1 * time.Second}, &Price{30 * time.Second, 1, 60 * time.Second, 1 * time.Second}},
Rates: RateGroups{&Rate{0, 2, 1 * time.Second, 1 * time.Second}, &Rate{30 * time.Second, 1, 60 * time.Second, 1 * time.Second}},
}
t1 := time.Date(2012, time.February, 3, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 3, 17, 31, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 60 * time.Second}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(i)
nts := ts.SplitByRateInterval(i)
splitTime := time.Date(2012, time.February, 3, 17, 30, 30, 0, time.UTC)
if ts.TimeStart != t1 || ts.TimeEnd != splitTime {
t.Error("Incorrect first half", ts)
@@ -380,17 +380,17 @@ func TestTimespanSplitGroupedRatesIncrements(t *testing.T) {
if nts.TimeStart != splitTime || nts.TimeEnd != t2 {
t.Error("Incorrect second half", nts)
}
if ts.Interval != i {
t.Error("Interval not attached correctly")
if ts.RateInterval != i {
t.Error("RateInterval not attached correctly")
}
c1 := ts.Interval.GetCost(ts.GetDuration(), ts.GetGroupStart())
c2 := nts.Interval.GetCost(nts.GetDuration(), nts.GetGroupStart())
c1 := ts.RateInterval.GetCost(ts.GetDuration(), ts.GetGroupStart())
c2 := nts.RateInterval.GetCost(nts.GetDuration(), nts.GetGroupStart())
if c1 != 60 || c2 != 60 {
t.Error("Wrong costs: ", c1, c2)
}
if ts.GetDuration().Seconds() != 0.5*60 || nts.GetDuration().Seconds() != 0.5*60 {
t.Error("Wrong durations.for Intervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
t.Error("Wrong durations.for RateIntervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
}
if ts.GetDuration().Seconds()+nts.GetDuration().Seconds() != oldDuration.Seconds() {
t.Errorf("The duration has changed: %v + %v != %v", ts.GetDuration().Seconds(), nts.GetDuration().Seconds(), oldDuration.Seconds())
@@ -398,15 +398,15 @@ func TestTimespanSplitGroupedRatesIncrements(t *testing.T) {
}
func TestTimespanSplitRightHourMarginBeforeGroup(t *testing.T) {
i := &Interval{
i := &RateInterval{
EndTime: "17:00:30",
Prices: PriceGroups{&Price{0, 2, 1 * time.Second, 1 * time.Second}, &Price{60 * time.Second, 1, 60 * time.Second, 1 * time.Second}},
Rates: RateGroups{&Rate{0, 2, 1 * time.Second, 1 * time.Second}, &Rate{60 * time.Second, 1, 60 * time.Second, 1 * time.Second}},
}
t1 := time.Date(2012, time.February, 3, 17, 00, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 3, 17, 01, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(i)
nts := ts.SplitByRateInterval(i)
splitTime := time.Date(2012, time.February, 3, 17, 00, 30, 0, time.UTC)
if ts.TimeStart != t1 || ts.TimeEnd != splitTime {
t.Error("Incorrect first half", ts)
@@ -414,32 +414,32 @@ func TestTimespanSplitRightHourMarginBeforeGroup(t *testing.T) {
if nts.TimeStart != splitTime || nts.TimeEnd != t2 {
t.Error("Incorrect second half", nts)
}
if ts.Interval != i {
t.Error("Interval not attached correctly")
if ts.RateInterval != i {
t.Error("RateInterval not attached correctly")
}
if ts.GetDuration().Seconds() != 30 || nts.GetDuration().Seconds() != 30 {
t.Error("Wrong durations.for Intervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
t.Error("Wrong durations.for RateIntervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
}
if ts.GetDuration().Seconds()+nts.GetDuration().Seconds() != oldDuration.Seconds() {
t.Errorf("The duration has changed: %v + %v != %v", ts.GetDuration().Seconds(), nts.GetDuration().Seconds(), oldDuration.Seconds())
}
nnts := nts.SplitByInterval(i)
nnts := nts.SplitByRateInterval(i)
if nnts != nil {
t.Error("Bad new split", nnts)
}
}
func TestTimespanSplitGroupSecondSplit(t *testing.T) {
i := &Interval{
i := &RateInterval{
EndTime: "17:03:30",
Prices: PriceGroups{&Price{0, 2, 1 * time.Second, 1 * time.Second}, &Price{60 * time.Second, 1, 1 * time.Second, 1 * time.Second}},
Rates: RateGroups{&Rate{0, 2, 1 * time.Second, 1 * time.Second}, &Rate{60 * time.Second, 1, 1 * time.Second, 1 * time.Second}},
}
t1 := time.Date(2012, time.February, 3, 17, 00, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 3, 17, 04, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 240 * time.Second}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(i)
nts := ts.SplitByRateInterval(i)
splitTime := time.Date(2012, time.February, 3, 17, 01, 00, 0, time.UTC)
if ts.TimeStart != t1 || ts.TimeEnd != splitTime {
t.Error("Incorrect first half", nts)
@@ -447,17 +447,17 @@ func TestTimespanSplitGroupSecondSplit(t *testing.T) {
if nts.TimeStart != splitTime || nts.TimeEnd != t2 {
t.Error("Incorrect second half", nts)
}
if ts.Interval != i {
t.Error("Interval not attached correctly")
if ts.RateInterval != i {
t.Error("RateInterval not attached correctly")
}
if ts.GetDuration().Seconds() != 60 || nts.GetDuration().Seconds() != 180 {
t.Error("Wrong durations.for Intervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
t.Error("Wrong durations.for RateIntervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
}
if ts.GetDuration().Seconds()+nts.GetDuration().Seconds() != oldDuration.Seconds() {
t.Errorf("The duration has changed: %v + %v != %v", ts.GetDuration().Seconds(), nts.GetDuration().Seconds(), oldDuration.Seconds())
}
nnts := nts.SplitByInterval(i)
nnts := nts.SplitByRateInterval(i)
nsplitTime := time.Date(2012, time.February, 3, 17, 03, 30, 0, time.UTC)
if nts.TimeStart != splitTime || nts.TimeEnd != nsplitTime {
t.Error("Incorrect first half", nts)
@@ -465,25 +465,25 @@ func TestTimespanSplitGroupSecondSplit(t *testing.T) {
if nnts.TimeStart != nsplitTime || nnts.TimeEnd != t2 {
t.Error("Incorrect second half", nnts)
}
if nts.Interval != i {
t.Error("Interval not attached correctly")
if nts.RateInterval != i {
t.Error("RateInterval not attached correctly")
}
if nts.GetDuration().Seconds() != 150 || nnts.GetDuration().Seconds() != 30 {
t.Error("Wrong durations.for Intervals", nts.GetDuration().Seconds(), nnts.GetDuration().Seconds())
t.Error("Wrong durations.for RateIntervals", nts.GetDuration().Seconds(), nnts.GetDuration().Seconds())
}
}
func TestTimespanSplitMultipleGroup(t *testing.T) {
i := &Interval{
i := &RateInterval{
EndTime: "17:05:00",
Prices: PriceGroups{&Price{0, 2, 1 * time.Second, 1 * time.Second}, &Price{60 * time.Second, 1, 1 * time.Second, 1 * time.Second}, &Price{180 * time.Second, 1, 1 * time.Second, 1 * time.Second}},
Rates: RateGroups{&Rate{0, 2, 1 * time.Second, 1 * time.Second}, &Rate{60 * time.Second, 1, 1 * time.Second, 1 * time.Second}, &Rate{180 * time.Second, 1, 1 * time.Second, 1 * time.Second}},
}
t1 := time.Date(2012, time.February, 3, 17, 00, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 3, 17, 04, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 240 * time.Second}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(i)
nts := ts.SplitByRateInterval(i)
splitTime := time.Date(2012, time.February, 3, 17, 01, 00, 0, time.UTC)
if ts.TimeStart != t1 || ts.TimeEnd != splitTime {
t.Error("Incorrect first half", nts)
@@ -491,17 +491,17 @@ func TestTimespanSplitMultipleGroup(t *testing.T) {
if nts.TimeStart != splitTime || nts.TimeEnd != t2 {
t.Error("Incorrect second half", nts)
}
if ts.Interval != i {
t.Error("Interval not attached correctly")
if ts.RateInterval != i {
t.Error("RateInterval not attached correctly")
}
if ts.GetDuration().Seconds() != 60 || nts.GetDuration().Seconds() != 180 {
t.Error("Wrong durations.for Intervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
t.Error("Wrong durations.for RateIntervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
}
if ts.GetDuration().Seconds()+nts.GetDuration().Seconds() != oldDuration.Seconds() {
t.Errorf("The duration has changed: %v + %v != %v", ts.GetDuration().Seconds(), nts.GetDuration().Seconds(), oldDuration.Seconds())
}
nnts := nts.SplitByInterval(i)
nnts := nts.SplitByRateInterval(i)
nsplitTime := time.Date(2012, time.February, 3, 17, 03, 00, 0, time.UTC)
if nts.TimeStart != splitTime || nts.TimeEnd != nsplitTime {
t.Error("Incorrect first half", nts)
@@ -509,12 +509,12 @@ func TestTimespanSplitMultipleGroup(t *testing.T) {
if nnts.TimeStart != nsplitTime || nnts.TimeEnd != t2 {
t.Error("Incorrect second half", nnts)
}
if nts.Interval != i {
t.Error("Interval not attached correctly")
if nts.RateInterval != i {
t.Error("RateInterval not attached correctly")
}
if nts.GetDuration().Seconds() != 120 || nnts.GetDuration().Seconds() != 60 {
t.Error("Wrong durations.for Intervals", nts.GetDuration().Seconds(), nnts.GetDuration().Seconds())
t.Error("Wrong durations.for RateIntervals", nts.GetDuration().Seconds(), nnts.GetDuration().Seconds())
}
}
@@ -523,8 +523,8 @@ func TestTimespanExpandingPastEnd(t *testing.T) {
&TimeSpan{
TimeStart: time.Date(2013, 9, 10, 14, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 10, 14, 30, 30, 0, time.UTC),
Interval: &Interval{Prices: PriceGroups{
&Price{RateIncrement: 60 * time.Second},
RateInterval: &RateInterval{Rates: RateGroups{
&Rate{RateIncrement: 60 * time.Second},
}},
},
&TimeSpan{
@@ -542,13 +542,37 @@ func TestTimespanExpandingPastEnd(t *testing.T) {
}
}
func TestTimespanExpandingRoundingPastEnd(t *testing.T) {
timespans := []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 10, 14, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 10, 14, 30, 20, 0, time.UTC),
RateInterval: &RateInterval{Rates: RateGroups{
&Rate{RateIncrement: 15 * time.Second},
}},
},
&TimeSpan{
TimeStart: time.Date(2013, 9, 10, 14, 30, 20, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 10, 14, 30, 40, 0, time.UTC),
},
}
cd := &CallDescriptor{}
timespans = cd.expandTimeSpans(timespans)
if len(timespans) != 2 {
t.Error("Error removing overlaped intervals: ", timespans)
}
if !timespans[0].TimeEnd.Equal(time.Date(2013, 9, 10, 14, 30, 30, 0, time.UTC)) {
t.Error("Error expanding timespan: ", timespans[0])
}
}
func TestTimespanExpandingPastEndMultiple(t *testing.T) {
timespans := []*TimeSpan{
&TimeSpan{
TimeStart: time.Date(2013, 9, 10, 14, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 10, 14, 30, 30, 0, time.UTC),
Interval: &Interval{Prices: PriceGroups{
&Price{RateIncrement: 60 * time.Second},
RateInterval: &RateInterval{Rates: RateGroups{
&Rate{RateIncrement: 60 * time.Second},
}},
},
&TimeSpan{
@@ -575,8 +599,8 @@ func TestTimespanExpandingPastEndMultipleEqual(t *testing.T) {
&TimeSpan{
TimeStart: time.Date(2013, 9, 10, 14, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 10, 14, 30, 30, 0, time.UTC),
Interval: &Interval{Prices: PriceGroups{
&Price{RateIncrement: 60 * time.Second},
RateInterval: &RateInterval{Rates: RateGroups{
&Rate{RateIncrement: 60 * time.Second},
}},
},
&TimeSpan{
@@ -603,8 +627,8 @@ func TestTimespanExpandingBeforeEnd(t *testing.T) {
&TimeSpan{
TimeStart: time.Date(2013, 9, 10, 14, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 10, 14, 30, 30, 0, time.UTC),
Interval: &Interval{Prices: PriceGroups{
&Price{RateIncrement: 45 * time.Second},
RateInterval: &RateInterval{Rates: RateGroups{
&Rate{RateIncrement: 45 * time.Second},
}},
},
&TimeSpan{
@@ -629,8 +653,8 @@ func TestTimespanExpandingBeforeEndMultiple(t *testing.T) {
&TimeSpan{
TimeStart: time.Date(2013, 9, 10, 14, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2013, 9, 10, 14, 30, 30, 0, time.UTC),
Interval: &Interval{Prices: PriceGroups{
&Price{RateIncrement: 45 * time.Second},
RateInterval: &RateInterval{Rates: RateGroups{
&Rate{RateIncrement: 45 * time.Second},
}},
},
&TimeSpan{

View File

@@ -24,7 +24,6 @@ import (
"io/ioutil"
"log"
"strconv"
"time"
)
// Import tariff plan from csv into storDb
@@ -139,11 +138,11 @@ func (self *TPCSVImporter) importRates(fn string) error {
}
continue
}
rt, err := NewRate(record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7], record[8])
rt, err := NewLoadRate(record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7], record[8])
if err != nil {
return err
}
if err := self.StorDb.SetTPRates(self.TPid, map[string][]*Rate{record[0]: []*Rate{rt}}); err != nil {
if err := self.StorDb.SetTPRates(self.TPid, map[string][]*LoadRate{record[0]: []*LoadRate{rt}}); err != nil {
if self.Verbose {
log.Printf("Ignoring line %d, storDb operational error: <%s> ", lineNr, err.Error())
}
@@ -294,16 +293,6 @@ func (self *TPCSVImporter) importActions(fn string) error {
}
continue
}
var expiryTime time.Time // Empty initialized time represents never expire
if record[5] != "*unlimited" { // ToDo: Expand here for other meta tags or go way of adding time for expiry
expiryTime, err = time.Parse(time.RFC3339, record[5])
if err != nil {
if self.Verbose {
log.Printf("Ignoring line %d, warning: <%s> ", lineNr, err.Error())
}
continue
}
}
rateValue, _ := strconv.ParseFloat(record[8], 64) // Ignore errors since empty string is error, we can find out based on rateType if defined
minutesWeight, _ := strconv.ParseFloat(record[9], 64)
weight, err := strconv.ParseFloat(record[10], 64)
@@ -314,16 +303,18 @@ func (self *TPCSVImporter) importActions(fn string) error {
continue
}
act := &Action{
ActionType: actionType,
BalanceId: balanceType,
Direction: direction,
Units: units,
ExpirationDate: expiryTime,
DestinationTag: destTag,
RateType: rateType,
RateValue: rateValue,
MinutesWeight: minutesWeight,
Weight: weight,
ActionType: actionType,
BalanceId: balanceType,
Direction: direction,
ExpirationString: record[5],
Balance: &Balance{
Value: units,
DestinationId: destTag,
SpecialPriceType: rateType,
SpecialPrice: rateValue,
Weight: minutesWeight,
},
Weight: weight,
}
if err := self.StorDb.SetTPActions(self.TPid, map[string][]*Action{actId: []*Action{act}}); err != nil {
if self.Verbose {

View File

@@ -285,7 +285,7 @@ func (sm *FSSessionManager) OnChannelHangupComplete(ev Event) {
Account: lastCC.Account,
Destination: lastCC.Destination,
Amount: -cost,
// FallbackSubject: lastCC.FallbackSubject, // ToDo: check how to best add it
// FallbackSubject: lastCC.FallbackSubject, // TODO: check how to best add it
}
var response float64
err := sm.connector.DebitCents(*cd, &response)

View File

@@ -121,13 +121,14 @@ func ParseDate(date string) (expDate time.Time, err error) {
return expDate, err
}
// returns a number equeal or larger than the peram that exactly
// is divisible to 60
func RoundToMinute(seconds float64) float64 {
if math.Mod(seconds, 60) == 0 {
return seconds
// returns a number equeal or larger than the amount that exactly
// is divisible to whole
func RoundTo(whole, amount time.Duration) time.Duration {
a, w := float64(amount), float64(whole)
if math.Mod(a, w) == 0 {
return amount
}
return (60 - math.Mod(seconds, 60)) + seconds
return time.Duration((w - math.Mod(a, w)) + a)
}
func SplitPrefix(prefix string) []string {

View File

@@ -195,33 +195,34 @@ func TestMissingStructFieldsIncorrect(t *testing.T) {
}
}
func TestRoundToMinute(t *testing.T) {
result := RoundToMinute(0)
expected := 0.0
func TestRound(t *testing.T) {
minute := time.Minute
result := RoundTo(minute, 0*time.Second)
expected := 0 * time.Second
if result != expected {
t.Errorf("Error rounding to minute1: expected %v was %v", expected, result)
}
result = RoundToMinute(1)
expected = 60.0
result = RoundTo(minute, 1*time.Second)
expected = minute
if result != expected {
t.Errorf("Error rounding to minute2: expected %v was %v", expected, result)
}
result = RoundToMinute(59)
expected = 60.0
result = RoundTo(minute, 5*time.Second)
expected = minute
if result != expected {
t.Errorf("Error rounding to minute3: expected %v was %v", expected, result)
}
result = RoundToMinute(60)
expected = 60.0
result = RoundTo(minute, minute)
expected = minute
if result != expected {
t.Errorf("Error rounding to minute4: expected %v was %v", expected, result)
}
result = RoundToMinute(90)
expected = 120.0
result = RoundTo(minute, 90*time.Second)
expected = 120 * time.Second
if result != expected {
t.Errorf("Error rounding to minute5: expected %v was %v", expected, result)
}
result = RoundToMinute(120)
result = RoundTo(60, 120)
expected = 120.0
if result != expected {
t.Errorf("Error rounding to minute5: expected %v was %v", expected, result)