This commit is contained in:
DanB
2015-08-03 21:20:48 +02:00
7 changed files with 166 additions and 116 deletions

View File

@@ -109,7 +109,7 @@ func (b *Balance) IsActiveAt(t time.Time) bool {
return true
}
for _, tim := range b.Timings {
if tim.IsActiveAt(t, false) {
if tim.IsActiveAt(t) {
return true
}
}

View File

@@ -55,6 +55,7 @@ func TestMultipleResultMerge(t *testing.T) {
cd := &CallDescriptor{Direction: OUTBOUND, Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cc1, _ := cd.getCost()
if cc1.Cost != 61 {
//ils.LogFull(cc1)
t.Errorf("expected 61 was %v", cc1.Cost)
for _, ts := range cc1.Timespans {
t.Log(ts.RateInterval)

View File

@@ -338,6 +338,7 @@ func (cd *CallDescriptor) splitInTimeSpans() (timespans []*TimeSpan) {
for i := 0; i < len(timespans); i++ {
newTs := timespans[i].SplitByRatingPlan(rp)
if newTs != nil {
//log.Print("NEW TS", newTs.TimeStart)
timespans = append(timespans, newTs)
} else {
afterEnd = true
@@ -346,8 +347,23 @@ func (cd *CallDescriptor) splitInTimeSpans() (timespans []*TimeSpan) {
}
}
}
}
// split on days
/*for i := 0; i < len(timespans); i++ {
if timespans[i].TimeStart.Day() != timespans[i].TimeEnd.Day() {
//log.Print("TS: ", timespans[i].TimeStart, timespans[i].TimeEnd)
start := timespans[i].TimeStart
newTs := timespans[i].SplitByTime(time.Date(start.Year(), start.Month(), start.Day(), 0, 0, 0, 0, start.Location()).Add(24 * time.Hour))
if newTs != nil {
//log.Print("NEW TS: ", newTs.TimeStart, newTs.TimeEnd)
// insert the new timespan
index := i + 1
timespans = append(timespans, nil)
copy(timespans[index+1:], timespans[index:])
timespans[index] = newTs
}
}
}*/
// Logger.Debug(fmt.Sprintf("After SplitByRatingPlan: %+v", timespans))
// split on rate intervals
for i := 0; i < len(timespans); i++ {
@@ -357,16 +373,20 @@ func (cd *CallDescriptor) splitInTimeSpans() (timespans []*TimeSpan) {
// Logger.Debug(fmt.Sprintf("rp: %+v", rp))
//timespans[i].RatingPlan = nil
rp.RateIntervals.Sort()
/*for _, interval := range rp.RateIntervals {
if !timespans[i].hasBetterRateIntervalThan(interval) {
timespans[i].SetRateInterval(interval)
}
}*/
//log.Print("ORIG TS: ", timespans[i].TimeStart, timespans[i].TimeEnd)
//log.Print(timespans[i].RateInterval)
for _, interval := range rp.RateIntervals {
//log.Printf("\tINTERVAL: %+v", interval.Timing)
if timespans[i].hasBetterRateIntervalThan(interval) {
//log.Print("continue")
continue // if the timespan has an interval than it already has a heigher weight
}
newTs := timespans[i].SplitByRateInterval(interval, cd.TOR != utils.VOICE)
//utils.PrintFull(timespans[i])
//utils.PrintFull(newTs)
if newTs != nil {
//log.Print("NEW TS: ", newTs.TimeStart, newTs.TimeEnd)
newTs.setRatingInfo(rp)
// insert the new timespan
index := i + 1
@@ -378,6 +398,8 @@ func (cd *CallDescriptor) splitInTimeSpans() (timespans []*TimeSpan) {
}
}
}
//log.Print("TS: ", timespans[i].TimeStart, timespans[i].TimeEnd)
//log.Print(timespans[i].RateInterval.Timing)
}
//Logger.Debug(fmt.Sprintf("After SplitByRateInterval: %+v", timespans))
@@ -421,6 +443,7 @@ func (cd *CallDescriptor) GetDuration() time.Duration {
Creates a CallCost structure with the cost information calculated for the received CallDescriptor.
*/
func (cd *CallDescriptor) GetCost() (*CallCost, error) {
cd.account = nil // make sure it's not cached
cc, err := cd.getCost()
if err != nil {
return nil, err
@@ -585,6 +608,7 @@ func (origCD *CallDescriptor) getMaxSessionDuration(origAcc *Account) (time.Dura
}
func (cd *CallDescriptor) GetMaxSessionDuration() (duration time.Duration, err error) {
cd.account = nil // make sure it's not cached
if account, err := cd.getAccount(); err != nil || account == nil {
Logger.Err(fmt.Sprintf("Could not get user balance for <%s>: %s.", cd.GetAccountKey(), err.Error()))
return 0, err
@@ -638,6 +662,7 @@ func (cd *CallDescriptor) debit(account *Account, dryRun bool, goNegative bool)
}
func (cd *CallDescriptor) Debit() (cc *CallCost, err error) {
cd.account = nil // make sure it's not cached
// lock all group members
if account, err := cd.getAccount(); err != nil || account == nil {
Logger.Err(fmt.Sprintf("Could not get user balance for <%s>: %s.", cd.GetAccountKey(), err.Error()))
@@ -660,6 +685,7 @@ func (cd *CallDescriptor) Debit() (cc *CallCost, err error) {
// This methods combines the Debit and GetMaxSessionDuration and will debit the max available time as returned
// by the GetMaxSessionDuration method. The amount filed has to be filled in call descriptor.
func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) {
cd.account = nil // make sure it's not cached
if account, err := cd.getAccount(); err != nil || account == nil {
Logger.Err(fmt.Sprintf("Could not get user balance for <%s>: %s.", cd.GetAccountKey(), err.Error()))
return nil, err
@@ -691,6 +717,7 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) {
}
func (cd *CallDescriptor) RefundIncrements() (left float64, err error) {
cd.account = nil // make sure it's not cached
accountsCache := make(map[string]*Account)
for _, increment := range cd.Increments {
account, found := accountsCache[increment.BalanceInfo.AccountId]
@@ -769,6 +796,7 @@ func (cd *CallDescriptor) GetLCRFromStorage() (*LCR, error) {
}
func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) {
cd.account = nil // make sure it's not cached
lcr, err := cd.GetLCRFromStorage()
if err != nil {
return nil, err

View File

@@ -195,7 +195,6 @@ func TestSplitSpansWeekend(t *testing.T) {
},
}
//log.Print("=============================")
timespans := cd.splitInTimeSpans()
if len(timespans) != 2 {
t.Log(cd.RatingInfos)
@@ -404,9 +403,10 @@ 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", Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0257308200", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
if result.Cost != 1200 || result.GetConnectFee() != 0 {
t.Errorf("Expected %v was %v", 1200, result)
cc, _ := cd.GetCost()
if cc.Cost != 2100 || cc.GetConnectFee() != 0 {
utils.LogFull(cc)
t.Errorf("Expected %v was %v (%v)", 2100, cc, cc.GetConnectFee())
}
}
@@ -977,7 +977,9 @@ func TestDebitNegatve(t *testing.T) {
t.Errorf("Error debiting from empty share: %+v", balanceMap[0].GetValue())
}
cc, err = cd.MaxDebit()
//utils.PrintFull(cc)
acc, _ = cd.getAccount()
balanceMap = acc.BalanceMap[utils.MONETARY+OUTBOUND]
//utils.LogFull(balanceMap)
if err != nil || cc.Cost != 2.5 {
t.Errorf("Debit from empty share error: %+v, %v", cc, err)
}

View File

@@ -116,13 +116,44 @@ func (rit *RITiming) CronString() string {
return rit.cronString
}
// Returns wheter the Timing is active at the specified time
func (rit *RITiming) IsActiveAt(t time.Time, endTime bool) bool {
// if the received time represents an endtime consider it 24 instead of 0
hour := t.Hour()
if endTime && hour == 0 {
hour = 24
/*
Returns a time object that represents the end of the interval realtive to the received time
*/
func (rit *RITiming) 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()
if rit.EndTime != "" {
split := strings.Split(rit.EndTime, ":")
hour, _ = strconv.Atoi(split[0])
min, _ = strconv.Atoi(split[1])
sec, _ = strconv.Atoi(split[2])
//log.Print("RIGHT1: ", time.Date(year, month, day, hour, min, sec, nsec, loc))
return time.Date(year, month, day, hour, min, sec, nsec, loc)
}
//log.Print("RIGHT2: ", time.Date(year, month, day, hour, min, sec, nsec, loc).Add(time.Second))
return time.Date(year, month, day, hour, min, sec, nsec, loc).Add(time.Second)
}
/*
Returns a time object that represents the start of the interval realtive to the received time
*/
func (rit *RITiming) 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()
if rit.StartTime != "" {
split := strings.Split(rit.StartTime, ":")
hour, _ = strconv.Atoi(split[0])
min, _ = strconv.Atoi(split[1])
sec, _ = strconv.Atoi(split[2])
}
//log.Print("LEFT: ", time.Date(year, month, day, hour, min, sec, nsec, loc))
return time.Date(year, month, day, hour, min, sec, nsec, loc)
}
// Returns wheter the Timing is active at the specified time
func (rit *RITiming) IsActiveAt(t time.Time) bool {
// check for years
if len(rit.Years) > 0 && !rit.Years.Contains(t.Year()) {
return false
@@ -139,38 +170,25 @@ func (rit *RITiming) IsActiveAt(t time.Time, endTime bool) bool {
if len(rit.WeekDays) > 0 && !rit.WeekDays.Contains(t.Weekday()) {
return false
}
//log.Print("Time: ", t)
//log.Print("Left Margin: ", rit.getLeftMargin(t))
// check for start hour
if rit.StartTime != "" {
split := strings.Split(rit.StartTime, ":")
sh, _ := strconv.Atoi(split[0])
sm, _ := strconv.Atoi(split[1])
ss, _ := strconv.Atoi(split[2])
// if the hour result before or result the same hour but the minute result before
if hour < sh ||
(hour == sh && t.Minute() < sm) ||
(hour == sh && t.Minute() == sm && t.Second() < ss) {
return false
}
if t.Before(rit.getLeftMargin(t)) {
return false
}
//log.Print("Right Margin: ", rit.getRightMargin(t))
// check for end hour
if rit.EndTime != "" {
split := strings.Split(rit.EndTime, ":")
eh, _ := strconv.Atoi(split[0])
em, _ := strconv.Atoi(split[1])
es, _ := strconv.Atoi(split[2])
// if the hour result after or result the same hour but the minute result after
if hour > eh ||
(hour == eh && t.Minute() > em) ||
(hour == eh && t.Minute() == em && t.Second() > es) {
return false
}
if t.After(rit.getRightMargin(t)) {
return false
}
return true
}
// IsActive returns wheter the Timing is active now
func (rit *RITiming) IsActive() bool {
return rit.IsActiveAt(time.Now(), false)
return rit.IsActiveAt(time.Now())
}
func (rit *RITiming) IsBlank() bool {
@@ -271,43 +289,12 @@ func (pg *RateGroups) AddRate(ps ...*Rate) {
Returns true if the received time result inside the interval
*/
func (i *RateInterval) Contains(t time.Time, endTime bool) bool {
return i.Timing.IsActiveAt(t, endTime)
}
/*
Returns a time object that represents the end of the interval realtive to the received 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()
if i.Timing.EndTime != "" {
split := strings.Split(i.Timing.EndTime, ":")
hour, _ = strconv.Atoi(split[0])
min, _ = strconv.Atoi(split[1])
sec, _ = strconv.Atoi(split[2])
//log.Print("RIGHT1: ", time.Date(year, month, day, hour, min, sec, nsec, loc))
return time.Date(year, month, day, hour, min, sec, nsec, loc)
if endTime {
if t.Hour() == 0 && t.Minute() == 0 && t.Second() == 0 { // back one second to 23:59:59
t = t.Add(-1 * time.Second)
}
}
//log.Print("RIGHT2: ", time.Date(year, month, day, hour, min, sec, nsec, loc).Add(time.Second))
return time.Date(year, month, day, hour, min, sec, nsec, loc).Add(time.Second)
}
/*
Returns a time object that represents the start of the interval realtive to the received 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()
if i.Timing.StartTime != "" {
split := strings.Split(i.Timing.StartTime, ":")
hour, _ = strconv.Atoi(split[0])
min, _ = strconv.Atoi(split[1])
sec, _ = strconv.Atoi(split[2])
}
//log.Print("LEFT: ", time.Date(year, month, day, hour, min, sec, nsec, loc))
return time.Date(year, month, day, hour, min, sec, nsec, loc)
return i.Timing.IsActiveAt(t)
}
func (i *RateInterval) String_DISABLED() string {
@@ -376,7 +363,7 @@ func (il RateIntervalList) Swap(i, j int) {
// we need higher weights earlyer in the list
func (il RateIntervalList) Less(j, i int) bool {
return il[i].Weight < il[j].Weight
return il[i].Weight < il[j].Weight //|| il[i].Timing.StartTime > il[j].Timing.StartTime
}
func (il RateIntervalList) Sort() {

View File

@@ -242,24 +242,13 @@ func (ts *TimeSpan) Contains(t time.Time) bool {
return t.After(ts.TimeStart) && t.Before(ts.TimeEnd)
}
/*
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) SetRateInterval(i *RateInterval) {
//log.Printf("SETRATEINTERVAL: %+v", i.Timing)
// higher weights are better
if ts.RateInterval == nil || ts.RateInterval.Weight < i.Weight {
ts.RateInterval = i
//log.Printf("RET TS: %+v", ts.RateInterval.Timing)
func (ts *TimeSpan) SetRateInterval(interval *RateInterval) {
if interval == nil {
return
}
iPrice, _, _ := i.GetRateParameters(ts.GetGroupStart())
tsPrice, _, _ := ts.RateInterval.GetRateParameters(ts.GetGroupStart())
if ts.RateInterval.Weight == i.Weight && iPrice <= tsPrice {
ts.RateInterval = i
if !ts.hasBetterRateIntervalThan(interval) {
ts.RateInterval = interval
}
//log.Printf("END TS: %+v", ts.RateInterval.Timing)
}
// Returns the cost of the timespan according to the relevant cost interval.
@@ -354,7 +343,7 @@ func (ts *TimeSpan) SplitByRateInterval(i *RateInterval, data bool) (nts *TimeSp
for _, rate := range i.Rating.Rates {
//Logger.Debug(fmt.Sprintf("Rate: %+v", rate))
if ts.GetGroupStart() < rate.GroupIntervalStart && ts.GetGroupEnd() > rate.GroupIntervalStart {
// Logger.Debug(fmt.Sprintf("Splitting"))
//log.Print("Splitting")
ts.SetRateInterval(i)
splitTime := ts.TimeStart.Add(rate.GroupIntervalStart - ts.GetGroupStart())
nts = &TimeSpan{
@@ -387,8 +376,7 @@ func (ts *TimeSpan) SplitByRateInterval(i *RateInterval, data bool) (nts *TimeSp
// if only the start time is in the interval split the interval to the right
if i.Contains(ts.TimeStart, false) {
//log.Print("Start in interval")
splitTime := i.getRightMargin(ts.TimeStart)
splitTime := i.Timing.getRightMargin(ts.TimeStart)
ts.SetRateInterval(i)
if splitTime == ts.TimeStart || splitTime.Equal(ts.TimeEnd) {
return
@@ -408,7 +396,7 @@ func (ts *TimeSpan) SplitByRateInterval(i *RateInterval, data bool) (nts *TimeSp
if i.Contains(ts.TimeEnd, true) {
//log.Print("End in interval")
//tmpTime := time.Date(ts.TimeStart.)
splitTime := i.getLeftMargin(ts.TimeEnd)
splitTime := i.Timing.getLeftMargin(ts.TimeEnd)
splitTime = utils.CopyHour(splitTime, ts.TimeStart)
if splitTime.Equal(ts.TimeEnd) {
return
@@ -419,7 +407,6 @@ func (ts *TimeSpan) SplitByRateInterval(i *RateInterval, data bool) (nts *TimeSp
}
nts.copyRatingInfo(ts)
ts.TimeEnd = splitTime
nts.SetRateInterval(i)
nts.DurationIndex = ts.DurationIndex
ts.SetNewDurationIndex(nts)
@@ -429,6 +416,22 @@ func (ts *TimeSpan) SplitByRateInterval(i *RateInterval, data bool) (nts *TimeSp
return
}
/*func (ts *TimeSpan) SplitByTime(splitTime time.Time) (nts *TimeSpan) {
if splitTime.Equal(ts.TimeEnd) {
return
}
nts = &TimeSpan{
TimeStart: splitTime,
TimeEnd: ts.TimeEnd,
}
nts.copyRatingInfo(ts)
ts.TimeEnd = splitTime
nts.SetRateInterval(ts.RateInterval)
nts.DurationIndex = ts.DurationIndex
ts.SetNewDurationIndex(nts)
return
}*/
// Split the timespan at the given increment start
func (ts *TimeSpan) SplitByIncrement(index int) *TimeSpan {
if index <= 0 || index >= len(ts.Increments) {
@@ -552,30 +555,46 @@ func (ts *TimeSpan) AddIncrement(inc *Increment) {
}
func (ts *TimeSpan) hasBetterRateIntervalThan(interval *RateInterval) bool {
if interval.Timing == nil {
return false
}
otherLeftMargin := interval.Timing.getLeftMargin(ts.TimeStart)
otherDistance := ts.TimeStart.Sub(otherLeftMargin)
//log.Print("OTHER LEFT: ", otherLeftMargin)
//log.Print("OTHER DISTANCE: ", otherDistance)
// if the distance is negative it's not usable
if otherDistance < 0 {
return true
}
//log.Print("RI: ", ts.RateInterval)
if ts.RateInterval == nil {
return false
}
//log.Print("StartTime: ", ts.TimeStart)
//log.Printf("OWN: %+v", ts.RateInterval)
//log.Printf("OTHER: %+v", interval)
// the higher the weight the better
if ts.RateInterval != nil &&
ts.RateInterval.Weight > interval.Weight {
return true
ts.RateInterval.Weight < interval.Weight {
return false
}
// check interval is closer than the new one
ownLeftMargin := ts.RateInterval.getLeftMargin(ts.TimeStart)
otherLeftMargin := interval.getLeftMargin(ts.TimeStart)
ownLeftMargin := ts.RateInterval.Timing.getLeftMargin(ts.TimeStart)
ownDistance := ts.TimeStart.Sub(ownLeftMargin)
otherDistance := ts.TimeStart.Sub(otherLeftMargin)
endOtherDistance := ts.TimeEnd.Sub(otherLeftMargin)
// if thr distance is negative relative to both ends it's not usable
if otherDistance < 0 && endOtherDistance < 0 {
return true
}
//log.Print("OWN LEFT: ", otherLeftMargin)
//log.Print("OWN DISTANCE: ", otherDistance)
//endOtherDistance := ts.TimeEnd.Sub(otherLeftMargin)
// if own interval is closer than its better
if ownDistance <= otherDistance {
//log.Print(ownDistance)
if ownDistance > otherDistance {
return false
}
ownPrice, _, _ := ts.RateInterval.GetRateParameters(ts.GetGroupStart())
otherPrice, _, _ := interval.GetRateParameters(ts.GetGroupStart())
// if own price is smaller than it's better
//log.Print(ownPrice, otherPrice)
if ownPrice < otherPrice {
return true
}
return false
return true
}

View File

@@ -217,7 +217,12 @@ func TestTimespanGetCost(t *testing.T) {
if ts1.getCost() != 0 {
t.Error("No interval and still kicking")
}
ts1.SetRateInterval(&RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{0, 1.0, 1 * time.Second, 1 * time.Second}}}})
ts1.SetRateInterval(
&RateInterval{
Timing: &RITiming{},
Rating: &RIRate{Rates: RateGroups{&Rate{0, 1.0, 1 * time.Second, 1 * time.Second}}},
},
)
if ts1.getCost() != 600 {
t.Error("Expected 10 got ", ts1.Cost)
}
@@ -240,10 +245,18 @@ func TestTimespanGetCostIntervals(t *testing.T) {
}
func TestSetRateInterval(t *testing.T) {
i1 := &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{0, 1.0, 1 * time.Second, 1 * time.Second}}}}
i1 := &RateInterval{
Timing: &RITiming{},
Rating: &RIRate{Rates: RateGroups{&Rate{0, 1.0, 1 * time.Second, 1 * time.Second}}},
}
ts1 := TimeSpan{RateInterval: i1}
i2 := &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{0, 2.0, 1 * time.Second, 1 * time.Second}}}}
ts1.SetRateInterval(i2)
i2 := &RateInterval{
Timing: &RITiming{},
Rating: &RIRate{Rates: RateGroups{&Rate{0, 2.0, 1 * time.Second, 1 * time.Second}}},
}
if !ts1.hasBetterRateIntervalThan(i2) {
ts1.SetRateInterval(i2)
}
if ts1.RateInterval != i1 {
t.Error("Smaller price interval should win")
}