This commit is contained in:
DanB
2013-08-01 08:20:45 +02:00
23 changed files with 477 additions and 231 deletions

View File

@@ -21,9 +21,8 @@ package apier
import (
"errors"
"fmt"
"time"
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Creates a new Actions profile within a tariff plan
@@ -48,16 +47,16 @@ func (self *Apier) SetTPActions(attrs utils.TPActions, reply *string) error {
acts := make([]*engine.Action, len(attrs.Actions))
for idx, act := range attrs.Actions {
acts[idx] = &engine.Action{
ActionType: act.Identifier,
BalanceId: act.BalanceType,
Direction: act.Direction,
Units: act.Units,
ExpirationDate: time.Unix(act.ExpiryTime,0),
DestinationTag: act.DestinationId,
RateType: act.RateType,
RateValue: act.Rate,
MinutesWeight: act.MinutesWeight,
Weight: act.Weight,
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,
}
}
if err := self.StorDb.SetTPActions(attrs.TPid, map[string][]*engine.Action{attrs.ActionsId: acts}); err != nil {

View File

@@ -98,7 +98,7 @@ CREATE TABLE `tp_rating_profiles` (
`tor` varchar(16) NOT NULL,
`direction` varchar(8) NOT NULL,
`subject` varchar(64) NOT NULL,
`activation_time` int(11) NOT NULL,
`activation_time` varchar(24) NOT NULL,
`destrates_timing_tag` varchar(24) NOT NULL,
`rates_fallback_subject` varchar(64),
PRIMARY KEY (`id`),
@@ -118,7 +118,7 @@ CREATE TABLE `tp_actions` (
`balance_type` varchar(24) NOT NULL,
`direction` varchar(8) NOT NULL,
`units` DECIMAL(8,4) NOT NULL,
`expiry_time` int(16) NOT NULL,
`expiry_time` varchar(24) NOT NULL,
`destination_tag` varchar(24) NOT NULL,
`rate_type` varchar(8) NOT NULL,
`rate` DECIMAL(8,4) NOT NULL,

View File

@@ -28,16 +28,17 @@ import (
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
ExpirationDate time.Time
Units float64
Weight float64
MinuteBucket *MinuteBucket
DestinationTag, RateType string // From here for import/load purposes only
RateValue, MinutesWeight float64
Id string
ActionType string
BalanceId string
Direction string
ExpirationString string
ExpirationDate time.Time
Units float64
Weight float64
MinuteBucket *MinuteBucket
DestinationTag, RateType string // From here for import/load purposes only
RateValue, MinutesWeight float64
}
const (

View File

@@ -20,6 +20,7 @@ package engine
import (
"fmt"
"github.com/cgrates/cgrates/utils"
"sort"
"strconv"
"strings"
@@ -206,6 +207,10 @@ func (at *ActionTiming) Execute() (err error) {
return
}
for _, a := range aac {
a.ExpirationDate, _ = utils.ParseDate(a.ExpirationString)
if a.MinuteBucket != nil {
a.MinuteBucket.ExpirationDate = a.ExpirationDate
}
actionFunction, exists := getActionFunc(a.ActionType)
if !exists {
Logger.Crit(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType))

View File

@@ -20,7 +20,7 @@ package engine
import (
"fmt"
//"log"
"github.com/cgrates/cgrates/utils"
"sort"
)
@@ -46,6 +46,10 @@ func (at *ActionTrigger) Execute(ub *UserBalance) (err error) {
return
}
for _, a := range aac {
a.ExpirationDate, _ = utils.ParseDate(a.ExpirationString)
if a.MinuteBucket != nil {
a.MinuteBucket.ExpirationDate = a.ExpirationDate
}
actionFunction, exists := getActionFunc(a.ActionType)
if !exists {
Logger.Warning(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType))

View File

@@ -750,16 +750,15 @@ func TestActionTriggerLogging(t *testing.T) {
func TestActionTimingLogging(t *testing.T) {
i := &Interval{
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},
StartTime: "18:00:00",
EndTime: "00:00:00",
Weight: 10.0,
ConnectFee: 0.0,
Prices: PriceGroups{&Price{0, 1.0}},
PricedUnits: 60,
RateIncrements: 1,
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},
StartTime: "18:00:00",
EndTime: "00:00:00",
Weight: 10.0,
ConnectFee: 0.0,
Prices: PriceGroups{&Price{0, 1.0, 1}},
PricedUnits: 60,
}
at := &ActionTiming{
Id: "some uuid",

View File

@@ -48,10 +48,6 @@ func TestApStoreRestoreJson(t *testing.T) {
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
result, _ := json.Marshal(ap)
expected := "{\"ActivationTime\":\"2012-02-01T14:30:01Z\",\"Intervals\":[{\"Years\":null,\"Months\":[2],\"MonthDays\":[1],\"WeekDays\":[3,4],\"StartTime\":\"14:30:00\",\"EndTime\":\"15:00:00\",\"Weight\":0,\"ConnectFee\":0,\"PricedUnits\":0,\"RateIncrements\":0,\"Prices\":null,\"RoundingMethod\":\"\",\"RoundingDecimals\":0}]}"
if string(result) != expected {
t.Errorf("Expected %q was %q", expected, result)
}
ap1 := &ActivationPeriod{}
json.Unmarshal(result, ap1)
if !reflect.DeepEqual(ap, ap1) {
@@ -65,10 +61,6 @@ func TestApStoreRestoreBlank(t *testing.T) {
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
result, _ := json.Marshal(ap)
expected := "{\"ActivationTime\":\"2012-02-01T14:30:01Z\",\"Intervals\":[{\"Years\":null,\"Months\":null,\"MonthDays\":null,\"WeekDays\":null,\"StartTime\":\"\",\"EndTime\":\"\",\"Weight\":0,\"ConnectFee\":0,\"PricedUnits\":0,\"RateIncrements\":0,\"Prices\":null,\"RoundingMethod\":\"\",\"RoundingDecimals\":0}]}"
if string(result) != expected {
t.Errorf("Expected %q was %q", expected, result)
}
ap1 := ActivationPeriod{}
json.Unmarshal(result, &ap1)
if reflect.DeepEqual(ap, ap1) {
@@ -154,13 +146,13 @@ func TestApAddIntervalIfNotPresent(t *testing.T) {
func TestApAddIntervalGroups(t *testing.T) {
i1 := &Interval{
Prices: PriceGroups{&Price{0, 1}},
Prices: PriceGroups{&Price{0, 1, 1}},
}
i2 := &Interval{
Prices: PriceGroups{&Price{30, 2}},
Prices: PriceGroups{&Price{30, 2, 1}},
}
i3 := &Interval{
Prices: PriceGroups{&Price{30, 2}},
Prices: PriceGroups{&Price{30, 2, 1}},
}
ap := &ActivationPeriod{}
ap.AddInterval(i1)

View File

@@ -33,24 +33,25 @@ import (
Defines a time interval for which a certain set of prices will apply
*/
type Interval struct {
Years Years
Months Months
MonthDays MonthDays
WeekDays WeekDays
StartTime, EndTime string // ##:##:## format
Weight, ConnectFee, PricedUnits, RateIncrements float64
Prices PriceGroups // GroupInterval (start time): Price
RoundingMethod string
RoundingDecimals int
Years Years
Months Months
MonthDays MonthDays
WeekDays WeekDays
StartTime, EndTime string // ##:##:## format
Weight, ConnectFee, PricedUnits float64
Prices PriceGroups // GroupInterval (start time): Price
RoundingMethod string
RoundingDecimals int
}
type Price struct {
StartSecond float64
Value float64
StartSecond float64
Value float64
RateIncrements float64
}
func (p *Price) Equal(o *Price) bool {
return p.StartSecond == o.StartSecond && p.Value == o.Value
return p.StartSecond == o.StartSecond && p.Value == o.Value && p.RateIncrements == o.RateIncrements
}
type PriceGroups []*Price
@@ -193,15 +194,17 @@ func (i *Interval) Equal(o *Interval) bool {
}
func (i *Interval) GetCost(duration, startSecond float64) (cost float64) {
price := i.GetPrice(startSecond)
rateIncrements := i.GetRateIncrements(startSecond)
if i.PricedUnits != 0 {
cost = math.Ceil(duration/i.RateIncrements) * i.RateIncrements * (i.GetPrice(startSecond) / i.PricedUnits)
cost = math.Ceil(duration/rateIncrements) * rateIncrements * (price / i.PricedUnits)
} else {
cost = math.Ceil(duration/i.RateIncrements) * i.RateIncrements * i.GetPrice(startSecond)
cost = math.Ceil(duration/rateIncrements) * rateIncrements * price
}
return utils.Round(cost, i.RoundingDecimals, i.RoundingMethod)
}
// gets the price for a the provided start second
// Gets the price for a the provided start second
func (i *Interval) GetPrice(startSecond float64) float64 {
i.Prices.Sort()
for index, price := range i.Prices {
@@ -213,6 +216,20 @@ func (i *Interval) GetPrice(startSecond float64) float64 {
return -1
}
func (i *Interval) GetRateIncrements(startSecond float64) float64 {
i.Prices.Sort()
for index, price := range i.Prices {
if price.StartSecond <= startSecond && (index == len(i.Prices)-1 ||
i.Prices[index+1].StartSecond > startSecond) {
if price.RateIncrements == 0 {
price.RateIncrements = 1
}
return price.RateIncrements
}
}
return 1
}
// Structure to store intervals according to weight
type IntervalList []*Interval

View File

@@ -27,7 +27,6 @@ import (
"os"
"strconv"
"strings"
"time"
)
type CSVReader struct {
@@ -306,7 +305,7 @@ func (csvr *CSVReader) LoadRatingProfiles() (err error) {
}
for record, err := csvReader.Read(); err == nil; record, err = csvReader.Read() {
tenant, tor, direction, subject, fallbacksubject := record[0], record[1], record[2], record[3], record[6]
at, err := time.Parse(time.RFC3339, record[4])
at, err := utils.ParseDate(record[4])
if err != nil {
return errors.New(fmt.Sprintf("Cannot parse activation time from %v", record[4]))
}
@@ -349,21 +348,17 @@ func (csvr *CSVReader) LoadActions() (err error) {
if err != nil {
return errors.New(fmt.Sprintf("Could not parse action units: %v", err))
}
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 {
return errors.New(fmt.Sprintf("Could not parse expiry time: %v", err))
}
}
var a *Action
if record[2] != MINUTES {
a = &Action{
ActionType: record[1],
BalanceId: record[2],
Direction: record[3],
Units: units,
ExpirationDate: expiryTime, //ToDo: Fix ExpirationDate as string to have ability of storing + reported on run time
ActionType: record[1],
BalanceId: record[2],
Direction: record[3],
Units: units,
ExpirationString: record[5],
}
if _, err := utils.ParseDate(a.ExpirationString); err != nil {
return errors.New(fmt.Sprintf("Could not parse expiration time: %v", err))
}
} else {
value, err := strconv.ParseFloat(record[8], 64)
@@ -379,21 +374,24 @@ func (csvr *CSVReader) LoadActions() (err error) {
return errors.New(fmt.Sprintf("Could not parse action weight: %v", err))
}
a = &Action{
Id: utils.GenUUID(),
ActionType: record[1],
BalanceId: record[2],
Direction: record[3],
Weight: weight,
ExpirationDate: expiryTime,
Id: utils.GenUUID(),
ActionType: record[1],
BalanceId: record[2],
Direction: record[3],
Weight: weight,
ExpirationString: record[5],
MinuteBucket: &MinuteBucket{
Seconds: units,
Weight: minutesWeight,
Price: value,
PriceType: record[7],
DestinationId: record[6],
ExpirationDate: expiryTime,
Seconds: units,
Weight: minutesWeight,
Price: value,
PriceType: record[7],
DestinationId: record[6],
},
}
if _, err := utils.ParseDate(a.ExpirationString); err != nil {
return errors.New(fmt.Sprintf("Could not parse expiration time: %v", err))
}
}
csvr.actions[tag] = append(csvr.actions[tag], a)
}
@@ -403,7 +401,7 @@ func (csvr *CSVReader) LoadActions() (err error) {
func (csvr *CSVReader) LoadActionTimings() (err error) {
csvReader, fp, err := csvr.readerFunc(csvr.actiontimingsFn, csvr.sep, utils.ACTION_TIMINGS_NRCOLS)
if err != nil {
log.Print("Could not load action triggers file: ", err)
log.Print("Could not load action timings file: ", err)
// allow writing of the other values
return nil
}

View File

@@ -85,7 +85,7 @@ vdf,0,*out,inf,2012-02-28T00:00:00Z,STANDARD,inf
vdf,0,*out,fall,2012-02-28T00:00:00Z,PREMIUM,rif
`
actions = `
MINI,TOPUP,MINUTES,*out,100,2013-07-19T13:03:22Z,NAT,*absolute,0,10,10
MINI,TOPUP,MINUTES,*out,100,*unlimited,NAT,*absolute,0,10,10
`
actionTimings = `
MORE_MINUTES,MINI,ONE_TIME_RUN,10

View File

@@ -23,7 +23,6 @@ import (
"fmt"
"github.com/cgrates/cgrates/utils"
"log"
"time"
)
type DbReader struct {
@@ -184,7 +183,10 @@ func (dbr *DbReader) LoadRatingProfiles() error {
return err
}
for _, rp := range rpfs {
at := time.Unix(rp.ActivationTime, 0)
at, err := utils.ParseDate(rp.ActivationTime)
if err != nil {
return errors.New(fmt.Sprintf("Cannot parse activation time from %v", rp.ActivationTime))
}
for _, d := range dbr.destinations {
ap, exists := dbr.activationPeriods[rp.DestRatesTimingTag]
if !exists {
@@ -211,7 +213,10 @@ func (dbr *DbReader) LoadRatingProfileByTag(tag string) error {
}
for _, ratingProfile := range rpm {
resultRatingProfile.FallbackKey = ratingProfile.FallbackKey // it will be the last fallback key
at := time.Unix(ratingProfile.ActivationTime, 0)
at, err := utils.ParseDate(ratingProfile.ActivationTime)
if err != nil {
return errors.New(fmt.Sprintf("Cannot parse activation time from %v", ratingProfile.ActivationTime))
}
drtm, err := dbr.storDb.GetTpDestinationRateTimings(dbr.tpid, ratingProfile.DestRatesTimingTag)
if err != nil {
return err

View File

@@ -155,16 +155,19 @@ func NewDestinationRateTiming(destinationRatesTag string, timing *Timing, weight
func (rt *DestinationRateTiming) GetInterval(dr *DestinationRate) (i *Interval) {
i = &Interval{
Years: rt.timing.Years,
Months: rt.timing.Months,
MonthDays: rt.timing.MonthDays,
WeekDays: rt.timing.WeekDays,
StartTime: rt.timing.StartTime,
Weight: rt.Weight,
ConnectFee: dr.Rate.ConnectFee,
Prices: PriceGroups{&Price{dr.Rate.GroupInterval, dr.Rate.Price}},
PricedUnits: dr.Rate.PricedUnits,
RateIncrements: dr.Rate.RateIncrements,
Years: rt.timing.Years,
Months: rt.timing.Months,
MonthDays: rt.timing.MonthDays,
WeekDays: rt.timing.WeekDays,
StartTime: rt.timing.StartTime,
Weight: rt.Weight,
ConnectFee: dr.Rate.ConnectFee,
Prices: PriceGroups{&Price{
StartSecond: dr.Rate.GroupInterval,
Value: dr.Rate.Price,
RateIncrements: dr.Rate.RateIncrements,
}},
PricedUnits: dr.Rate.PricedUnits,
}
return
}

View File

@@ -64,7 +64,8 @@ func (mb *MinuteBucket) Equal(o *MinuteBucket) bool {
return mb.DestinationId == o.DestinationId &&
mb.Weight == o.Weight &&
mb.Price == o.Price &&
mb.PriceType == o.PriceType
mb.PriceType == o.PriceType &&
mb.ExpirationDate.Equal(o.ExpirationDate)
}
func (mb *MinuteBucket) IsExpired() bool {

View File

@@ -29,11 +29,10 @@ const (
)
type RatingProfile struct {
Id string
FallbackKey string // FallbackKey is used as complete combination of Tenant:TOR:Direction:Subject
DestinationMap map[string][]*ActivationPeriod
Tag, Tenant, TOR, Direction, Subject, DestRatesTimingTag, RatesFallbackSubject string // used only for loading
ActivationTime int64
Id string
FallbackKey string // FallbackKey is used as complete combination of Tenant:TOR:Direction:Subject
DestinationMap map[string][]*ActivationPeriod
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.

View File

@@ -112,6 +112,7 @@ func (a *Action) Store() (result string, err error) {
result += a.ActionType + "|"
result += a.BalanceId + "|"
result += a.Direction + "|"
result += a.ExpirationString + "|"
result += a.ExpirationDate.Format(time.RFC3339) + "|"
result += strconv.FormatFloat(a.Units, 'f', -1, 64) + "|"
result += strconv.FormatFloat(a.Weight, 'f', -1, 64)
@@ -128,22 +129,23 @@ func (a *Action) Store() (result string, err error) {
func (a *Action) Restore(input string) (err error) {
elements := strings.Split(input, "|")
if len(elements) < 7 {
if len(elements) < 8 {
return notEnoughElements
}
a.Id = elements[0]
a.ActionType = elements[1]
a.BalanceId = elements[2]
a.Direction = elements[3]
a.ExpirationDate, err = time.Parse(time.RFC3339, elements[4])
a.ExpirationString = elements[4]
a.ExpirationDate, err = time.Parse(time.RFC3339, elements[5])
if err != nil {
return err
}
a.Units, _ = strconv.ParseFloat(elements[5], 64)
a.Weight, _ = strconv.ParseFloat(elements[6], 64)
if len(elements) == 8 {
a.Units, _ = strconv.ParseFloat(elements[6], 64)
a.Weight, _ = strconv.ParseFloat(elements[7], 64)
if len(elements) == 9 {
a.MinuteBucket = &MinuteBucket{}
if err := a.MinuteBucket.Restore(elements[7]); err != nil {
if err := a.MinuteBucket.Restore(elements[8]); err != nil {
return err
}
}
@@ -458,7 +460,10 @@ func (d *Destination) Restore(input string) error {
func (pg PriceGroups) Store() (result string, err error) {
for _, p := range pg {
result += strconv.FormatFloat(p.StartSecond, 'f', -1, 64) + ":" + strconv.FormatFloat(p.Value, 'f', -1, 64) + ","
result += strconv.FormatFloat(p.StartSecond, 'f', -1, 64) +
":" + strconv.FormatFloat(p.Value, 'f', -1, 64) +
":" + strconv.FormatFloat(p.RateIncrements, 'f', -1, 64) +
","
}
result = strings.TrimRight(result, ",")
return
@@ -468,7 +473,7 @@ func (pg *PriceGroups) Restore(input string) error {
elements := strings.Split(input, ",")
for _, element := range elements {
priceElements := strings.Split(element, ":")
if len(priceElements) != 2 {
if len(priceElements) != 3 {
continue
}
ss, err := strconv.ParseFloat(priceElements[0], 64)
@@ -479,9 +484,14 @@ func (pg *PriceGroups) Restore(input string) error {
if err != nil {
return err
}
ri, err := strconv.ParseFloat(priceElements[2], 64)
if err != nil {
return err
}
price := &Price{
StartSecond: ss,
Value: v,
StartSecond: ss,
Value: v,
RateIncrements: ri,
}
*pg = append(*pg, price)
}
@@ -519,7 +529,6 @@ func (i *Interval) Store() (result string, err error) {
}
result += ps + ";"
result += strconv.FormatFloat(i.PricedUnits, 'f', -1, 64) + ";"
result += strconv.FormatFloat(i.RateIncrements, 'f', -1, 64) + ";"
result += i.RoundingMethod + ";"
result += strconv.Itoa(i.RoundingDecimals)
return
@@ -527,7 +536,7 @@ func (i *Interval) Store() (result string, err error) {
func (i *Interval) Restore(input string) error {
is := strings.Split(input, ";")
if len(is) != 13 {
if len(is) != 12 {
return notEnoughElements
}
if err := i.Years.Restore(is[0]); err != nil {
@@ -551,9 +560,8 @@ func (i *Interval) Restore(input string) error {
return err
}
i.PricedUnits, _ = strconv.ParseFloat(is[9], 64)
i.RateIncrements, _ = strconv.ParseFloat(is[10], 64)
i.RoundingMethod = is[11]
i.RoundingDecimals, _ = strconv.Atoi(is[12])
i.RoundingMethod = is[10]
i.RoundingDecimals, _ = strconv.Atoi(is[11])
return nil
}

View File

@@ -43,7 +43,7 @@ func TestSimpleMarshallerApStoreRestore(t *testing.T) {
}
func TestSimpleMarshallerApRestoreFromString(t *testing.T) {
s := "2012-02-01T14:30:01Z|;1,2,3,4,5,6,7,8,9,10,11,12;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;1,2,3,4,5,6,0;00:00:00;;10;0;0.2;60;1;;0\n"
s := "2012-02-01T14:30:01Z|;1,2,3,4,5,6,7,8,9,10,11,12;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;1,2,3,4,5,6,0;00:00:00;;10;0;0:0.2:1;60;1;\n"
ap := &ActivationPeriod{}
err := ap.Restore(s)
if err != nil || len(ap.Intervals) != 1 {
@@ -73,16 +73,15 @@ func TestRpStoreRestore(t *testing.T) {
func TestActionTimingStoreRestore(t *testing.T) {
i := &Interval{
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},
StartTime: "18:00:00",
EndTime: "00:00:00",
Weight: 10.0,
ConnectFee: 0.0,
Prices: PriceGroups{&Price{0, 1.0}},
PricedUnits: 60,
RateIncrements: 1,
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},
StartTime: "18:00:00",
EndTime: "00:00:00",
Weight: 10.0,
ConnectFee: 0.0,
Prices: PriceGroups{&Price{0, 1.0, 1}},
PricedUnits: 60,
}
at := &ActionTiming{
Id: "some uuid",
@@ -124,16 +123,15 @@ func TestActionTriggerStoreRestore(t *testing.T) {
func TestIntervalStoreRestore(t *testing.T) {
i := &Interval{
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},
StartTime: "18:00:00",
EndTime: "00:00:00",
Weight: 10.0,
ConnectFee: 0.0,
Prices: PriceGroups{&Price{0, 1777.0}},
PricedUnits: 60,
RateIncrements: 1,
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},
StartTime: "18:00:00",
EndTime: "00:00:00",
Weight: 10.0,
ConnectFee: 0.0,
Prices: PriceGroups{&Price{0, 1777.0, 1}},
PricedUnits: 60,
}
r, err := i.Store()
o := &Interval{}
@@ -144,10 +142,10 @@ func TestIntervalStoreRestore(t *testing.T) {
}
func TestIntervalRestoreFromString(t *testing.T) {
s := ";1,2,3,4,5,6,7,8,9,10,11,12;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;1,2,3,4,5,6,0;00:00:00;;10;0;0:0.2;60;0;;1"
s := ";1,2,3,4,5,6,7,8,9,10,11,12;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;1,2,3,4,5,6,0;00:00:00;;10;0;0:0.2:1;60;0;"
i := Interval{}
err := i.Restore(s)
if err != nil || !i.Prices.Equal(PriceGroups{&Price{0, 0.2}}) {
if err != nil || !i.Prices.Equal(PriceGroups{&Price{0, 0.2, 1}}) {
t.Errorf("Error restoring inteval period from string %+v", i)
}
}

View File

@@ -23,8 +23,6 @@ import (
"encoding/json"
"fmt"
"github.com/cgrates/cgrates/utils"
"strconv"
"time"
)
type SQLStorage struct {
@@ -480,8 +478,7 @@ func (self *SQLStorage) GetTPRatingProfile(tpid, rpId string) (*utils.TPRatingPr
i := 0
for rows.Next() {
i++ //Keep here a reference so we know we got at least one result
var tenant, tor, direction, subject, drtId, fallbackSubj string
var aTime int64
var tenant, tor, direction, subject, drtId, fallbackSubj, aTime string
err = rows.Scan(&tenant, &tor, &direction, &subject, &aTime, &drtId, &fallbackSubj)
if err != nil {
return nil, err
@@ -562,7 +559,7 @@ func (self *SQLStorage) SetTPActions(tpid string, acts map[string][]*Action) err
expTime = act.ExpirationDate.Unix()
}
qry += fmt.Sprintf("('%s','%s','%s','%s','%s',%f,%d,'%s','%s',%f,%f,%f)",
tpid, actId, act.ActionType, act.BalanceId, act.Direction, act.Units, expTime,
tpid, actId, act.ActionType, act.BalanceId, act.Direction, act.Units, expTime,
act.DestinationTag, act.RateType, act.RateValue, act.MinutesWeight, act.Weight)
i++
}
@@ -583,8 +580,7 @@ func (self *SQLStorage) GetTPActions(tpid, actsId string) (*utils.TPActions, err
i := 0
for rows.Next() {
i++ //Keep here a reference so we know we got at least one result
var action, balanceId, dir, destId, rateType string
var expTime int64
var action, balanceId, dir, destId, rateType, expTime string
var units, rate, minutesWeight, weight float64
if err = rows.Scan(&action, &balanceId, &dir, &units, &expTime, &destId, &rateType, &rate, &minutesWeight, &weight); err != nil {
return nil, err
@@ -1079,8 +1075,7 @@ func (self *SQLStorage) GetTpRatingProfiles(tpid, tag string) (map[string]*Ratin
}
defer rows.Close()
for rows.Next() {
var tag, tenant, tor, direction, subject, fallback_subject, destrates_timing_tag string
var activation_time int64
var tag, tenant, tor, direction, subject, fallback_subject, destrates_timing_tag, activation_time string
if err := rows.Scan(&tag, &tenant, &tor, &direction, &subject, &activation_time, &destrates_timing_tag, &fallback_subject); err != nil {
return nil, err
}
@@ -1116,36 +1111,30 @@ func (self *SQLStorage) GetTpActions(tpid, tag string) (map[string][]*Action, er
if err := rows.Scan(&id, &tpid, &tag, &action, &balance_type, &direction, &units, &expirationDate, &destinations_tag, &rate_type, &rate, &minutes_weight, &weight); err != nil {
return nil, err
}
unix, err := strconv.ParseInt(expirationDate, 10, 64)
if err != nil {
return nil, err
}
expDate := time.Unix(unix, 0)
var a *Action
if balance_type != MINUTES {
a = &Action{
ActionType: action,
BalanceId: balance_type,
Direction: direction,
Units: units,
ExpirationDate: expDate,
ActionType: action,
BalanceId: balance_type,
Direction: direction,
Units: units,
ExpirationString: expirationDate,
}
} else {
var price float64
a = &Action{
Id: utils.GenUUID(),
ActionType: action,
BalanceId: balance_type,
Direction: direction,
Weight: weight,
ExpirationDate: expDate,
Id: utils.GenUUID(),
ActionType: action,
BalanceId: balance_type,
Direction: direction,
Weight: weight,
ExpirationString: expirationDate,
MinuteBucket: &MinuteBucket{
Seconds: units,
Weight: minutes_weight,
Price: price,
PriceType: rate_type,
DestinationId: destinations_tag,
ExpirationDate: expDate,
Seconds: units,
Weight: minutes_weight,
Price: price,
PriceType: rate_type,
DestinationId: destinations_tag,
},
}
}

View File

@@ -20,6 +20,7 @@ package engine
import (
"fmt"
"math"
"time"
)
@@ -61,9 +62,6 @@ func (ts *TimeSpan) getCost(cd *CallDescriptor) (cost float64) {
}
duration := ts.GetDuration().Seconds()
i := ts.Interval
if i.RateIncrements == 0 {
i.RateIncrements = 1
}
cost = i.GetCost(duration, ts.GetGroupStart())
// if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
// userBalance.mux.RLock()
@@ -117,10 +115,11 @@ func (ts *TimeSpan) SplitByInterval(i *Interval) (nts *TimeSpan) {
ts.SetInterval(i)
splitTime := ts.TimeStart.Add(time.Duration(price.StartSecond-ts.GetGroupStart()) * time.Second)
nts = &TimeSpan{TimeStart: splitTime, TimeEnd: ts.TimeEnd}
ts.TimeEnd = splitTime
nts.SetInterval(i)
nts.CallDuration = ts.CallDuration
ts.CallDuration = ts.CallDuration - nts.GetDuration().Seconds()
ts.TimeEnd = splitTime
ts.CallDuration = math.Max(0, ts.CallDuration-nts.GetDuration().Seconds())
return
}
}
@@ -131,7 +130,7 @@ func (ts *TimeSpan) SplitByInterval(i *Interval) (nts *TimeSpan) {
ts.SetInterval(i)
return
}
// if only the start time is in the interval split the interval
// 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)
@@ -141,10 +140,12 @@ func (ts *TimeSpan) SplitByInterval(i *Interval) (nts *TimeSpan) {
}
nts = &TimeSpan{TimeStart: splitTime, TimeEnd: ts.TimeEnd}
ts.TimeEnd = splitTime
nts.CallDuration = ts.CallDuration
ts.CallDuration = math.Max(0, ts.CallDuration-nts.GetDuration().Seconds())
return
}
// if only the end time is in the interval split the interval
// if only the end time is in the interval split the interval to the left
if i.Contains(ts.TimeEnd) {
//Logger.Debug("End in interval")
splitTime := i.getLeftMargin(ts.TimeEnd)
@@ -155,6 +156,9 @@ func (ts *TimeSpan) SplitByInterval(i *Interval) (nts *TimeSpan) {
ts.TimeEnd = splitTime
nts.SetInterval(i)
nts.CallDuration = ts.CallDuration
ts.CallDuration = math.Max(0, ts.CallDuration-nts.GetDuration().Seconds())
return
}
return
@@ -206,10 +210,7 @@ func (ts *TimeSpan) SplitByMinuteBucket(mb *MinuteBucket) (newTs *TimeSpan) {
}
func (ts *TimeSpan) GetGroupStart() float64 {
if ts.CallDuration == 0 {
return 0
}
return ts.CallDuration - ts.GetDuration().Seconds()
return math.Max(0, ts.CallDuration-ts.GetDuration().Seconds())
}
func (ts *TimeSpan) GetGroupEnd() float64 {

View File

@@ -192,21 +192,20 @@ 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}}}
ts1.Interval = &Interval{Prices: PriceGroups{&Price{0, 1.0, 1}}}
if ts1.getCost(cd) != 600 {
t.Error("Expected 10 got ", ts1.getCost(cd))
}
ts1.Interval.PricedUnits = 60
ts1.Interval.RateIncrements = 1
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}}}
i1 := &Interval{Prices: PriceGroups{&Price{0, 1.0, 1}}}
ts1 := TimeSpan{Interval: i1}
i2 := &Interval{Prices: PriceGroups{&Price{0, 2.0}}}
i2 := &Interval{Prices: PriceGroups{&Price{0, 2.0, 1}}}
ts1.SetInterval(i2)
if ts1.Interval != i1 {
t.Error("Smaller price interval should win")
@@ -332,19 +331,19 @@ func TestTimespanSplitByMinuteBucketScarceExpiringDifferentScarceFirst(t *testin
func TestTimespanSplitGroupedRates(t *testing.T) {
i := &Interval{
EndTime: "17:59:00",
Prices: PriceGroups{&Price{0, 1}, &Price{900, 2}},
RateIncrements: 1,
EndTime: "17:59:00",
Prices: PriceGroups{&Price{0, 2, 1}, &Price{900, 1, 1}},
}
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}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(i)
if ts.TimeStart != t1 || ts.TimeEnd != time.Date(2012, time.February, 3, 17, 45, 00, 0, time.UTC) {
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)
}
if nts.TimeStart != time.Date(2012, time.February, 3, 17, 45, 00, 0, time.UTC) || nts.TimeEnd != t2 {
if nts.TimeStart != splitTime || nts.TimeEnd != t2 {
t.Error("Incorrect second half", nts)
}
if ts.Interval != i {
@@ -352,7 +351,7 @@ func TestTimespanSplitGroupedRates(t *testing.T) {
}
c1 := ts.Interval.GetCost(ts.GetDuration().Seconds(), ts.GetGroupStart())
c2 := nts.Interval.GetCost(nts.GetDuration().Seconds(), nts.GetGroupStart())
if c1 != 900 || c2 != 1800 {
if c1 != 1800 || c2 != 900 {
t.Error("Wrong costs: ", c1, c2)
}
@@ -363,3 +362,158 @@ func TestTimespanSplitGroupedRates(t *testing.T) {
t.Errorf("The duration has changed: %v + %v != %v", ts.GetDuration().Seconds(), nts.GetDuration().Seconds(), oldDuration.Seconds())
}
}
func TestTimespanSplitGroupedRatesIncrements(t *testing.T) {
i := &Interval{
EndTime: "17:59:00",
Prices: PriceGroups{&Price{0, 2, 1}, &Price{30, 1, 60}},
}
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}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(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)
}
if nts.TimeStart != splitTime || nts.TimeEnd != t2 {
t.Error("Incorrect second half", nts)
}
if ts.Interval != i {
t.Error("Interval not attached correctly")
}
c1 := ts.Interval.GetCost(ts.GetDuration().Seconds(), ts.GetGroupStart())
c2 := nts.Interval.GetCost(nts.GetDuration().Seconds(), 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())
}
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())
}
}
func TestTimespanSplitRightHourMarginBeforeGroup(t *testing.T) {
i := &Interval{
EndTime: "17:00:30",
Prices: PriceGroups{&Price{0, 2, 1}, &Price{60, 1, 60}},
}
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)
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)
}
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.GetDuration().Seconds() != 30 || nts.GetDuration().Seconds() != 30 {
t.Error("Wrong durations.for Intervals", 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)
if nnts != nil {
t.Error("Bad new split", nnts)
}
}
func TestTimespanSplitGroupSecondSplit(t *testing.T) {
i := &Interval{
EndTime: "17:03:30",
Prices: PriceGroups{&Price{0, 2, 1}, &Price{60, 1, 1}},
}
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}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(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)
}
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.GetDuration().Seconds() != 60 || nts.GetDuration().Seconds() != 180 {
t.Error("Wrong durations.for Intervals", 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)
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)
}
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.GetDuration().Seconds() != 150 || nnts.GetDuration().Seconds() != 30 {
t.Error("Wrong durations.for Intervals", nts.GetDuration().Seconds(), nnts.GetDuration().Seconds())
}
}
func TestTimespanSplitMultipleGroup(t *testing.T) {
i := &Interval{
EndTime: "17:05:00",
Prices: PriceGroups{&Price{0, 2, 1}, &Price{60, 1, 1}, &Price{180, 1, 1}},
}
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}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(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)
}
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.GetDuration().Seconds() != 60 || nts.GetDuration().Seconds() != 180 {
t.Error("Wrong durations.for Intervals", 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)
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)
}
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.GetDuration().Seconds() != 120 || nnts.GetDuration().Seconds() != 60 {
t.Error("Wrong durations.for Intervals", nts.GetDuration().Seconds(), nnts.GetDuration().Seconds())
}
}

View File

@@ -239,11 +239,12 @@ func (self *TPCSVImporter) importRatingProfiles(fn string) error {
continue
}
tenant, tor, direction, subject, destRatesTimingTag, fallbacksubject := record[0], record[1], record[2], record[3], record[5], record[6]
at, err := time.Parse(time.RFC3339, record[4])
_, err = utils.ParseDate(record[4])
if err != nil {
if self.Verbose {
log.Printf("Ignoring line %d, warning: <%s> ", lineNr, err.Error())
}
continue
}
rpTag := "TPCSV" //Autogenerate rating profile id
if self.ImportId != "" {
@@ -254,7 +255,7 @@ func (self *TPCSVImporter) importRatingProfiles(fn string) error {
TOR: tor,
Direction: direction,
Subject: subject,
ActivationTime: at.Unix(),
ActivationTime: record[4],
DestRatesTimingTag: destRatesTimingTag,
RatesFallbackSubject: fallbacksubject,
}
@@ -303,7 +304,7 @@ func (self *TPCSVImporter) importActions(fn string) error {
continue
}
}
rateValue, _ := strconv.ParseFloat(record[8], 64) // Ignore errors since empty string is error, we can find out based on rateType if defined
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)
if err != nil {
@@ -313,16 +314,16 @@ func (self *TPCSVImporter) importActions(fn string) error {
continue
}
act := &Action{
ActionType: actionType,
BalanceId: balanceType,
Direction: direction,
ActionType: actionType,
BalanceId: balanceType,
Direction: direction,
Units: units,
ExpirationDate: expiryTime,
DestinationTag: destTag,
RateType: rateType,
RateValue: rateValue,
MinutesWeight: minutesWeight,
Weight: weight,
RateType: rateType,
RateValue: rateValue,
MinutesWeight: minutesWeight,
Weight: weight,
}
if err := self.StorDb.SetTPActions(self.TPid, map[string][]*Action{actId: []*Action{act}}); err != nil {
if self.Verbose {
@@ -359,11 +360,11 @@ func (self *TPCSVImporter) importActionTimings(fn string) error {
}
continue
}
at := &ActionTiming{
Tag: tag,
at := &ActionTiming{
Tag: tag,
ActionsTag: actionsTag,
TimingsTag: timingTag,
Weight: weight,
TimingsTag: timingTag,
Weight: weight,
}
if err := self.StorDb.SetTPActionTimings(self.TPid, map[string][]*ActionTiming{tag: []*ActionTiming{at}}); err != nil {
if self.Verbose {
@@ -407,14 +408,14 @@ func (self *TPCSVImporter) importActionTriggers(fn string) error {
}
continue
}
at := &ActionTrigger{
BalanceId: balanceType,
Direction: direction,
ThresholdType: thresholdType,
at := &ActionTrigger{
BalanceId: balanceType,
Direction: direction,
ThresholdType: thresholdType,
ThresholdValue: threshold,
DestinationId: destinationTag,
Weight: weight,
ActionsId: actionsTag,
DestinationId: destinationTag,
Weight: weight,
ActionsId: actionsTag,
}
if err := self.StorDb.SetTPActionTriggers(self.TPid, map[string][]*ActionTrigger{tag: []*ActionTrigger{at}}); err != nil {
if self.Verbose {

View File

@@ -72,7 +72,7 @@ type TPRatingProfile struct {
}
type RatingActivation struct {
ActivationTime int64 // Time when this profile will become active, defined as unix epoch time
ActivationTime string // Time when this profile will become active, defined as unix epoch time
DestRateTimingId string // Id of DestRateTiming profile
}
@@ -91,16 +91,16 @@ type TPActions struct {
}
type Action struct {
Identifier string // Identifier mapped in the code
BalanceType string // Type of balance the action will operate on
Direction string // Balance direction
Units float64 // Number of units to add/deduct
ExpiryTime int64 // Time when the units will expire
DestinationId string // Destination profile id
RateType string // Type of rate <*absolute|*percent>
Rate float64 // Price value
MinutesWeight float64 // Minutes weight
Weight float64 // Action's weight
Identifier string // Identifier mapped in the code
BalanceType string // Type of balance the action will operate on
Direction string // Balance direction
Units float64 // Number of units to add/deduct
ExpiryTime string // Time when the units will expire
DestinationId string // Destination profile id
RateType string // Type of rate <*absolute|*percent>
Rate float64 // Price value
MinutesWeight float64 // Minutes weight
Weight float64 // Action's weight
}
type ApiTPActionTimings struct {
@@ -123,7 +123,7 @@ type ApiTPActionTriggers struct {
}
type ApiActionTrigger struct {
BalanceType string // Type of balance this trigger monitors
BalanceType string // Type of balance this trigger monitors
Direction string // Traffic direction
ThresholdType string // This threshold type
ThresholdValue float64 // Threshold

View File

@@ -25,6 +25,7 @@ import (
"fmt"
"math"
"strconv"
"strings"
"time"
)
@@ -94,3 +95,27 @@ func Round(x float64, prec int, method string) float64 {
return rounder / pow
}
func ParseDate(date string) (expDate time.Time, err error) {
switch {
case date == "*unlimited" || date == "":
// leave it at zero
case string(date[0]) == "+":
d, err := time.ParseDuration(date[1:])
if err != nil {
return expDate, err
}
expDate = time.Now().Add(d)
case date == "*monthly":
expDate = time.Now().AddDate(0, 1, 0) // add one month
case strings.Contains(date, "Z"):
expDate, err = time.Parse(time.RFC3339, date)
default:
unix, err := strconv.ParseInt(date, 10, 64)
if err != nil {
return expDate, err
}
expDate = time.Unix(unix, 0)
}
return expDate, err
}

View File

@@ -20,6 +20,7 @@ package utils
import (
"testing"
"time"
)
func TestFirstNonEmpty(t *testing.T) {
@@ -119,3 +120,49 @@ func TestRoundByMethodDown2(t *testing.T) {
t.Errorf("Error rounding up: sould be %v was %v", expected, result)
}
}
func TestParseDateUnix(t *testing.T) {
date, err := ParseDate("1375212790")
expected := time.Date(2013, 7, 30, 19, 33, 10, 0, time.UTC)
if err != nil || !date.Equal(expected) {
t.Error("error parsing date: ", expected.Sub(date))
}
}
func TestParseDateUnlimited(t *testing.T) {
date, err := ParseDate("*unlimited")
if err != nil || !date.IsZero() {
t.Error("error parsing unlimited date!: ")
}
}
func TestParseDateEmpty(t *testing.T) {
date, err := ParseDate("")
if err != nil || !date.IsZero() {
t.Error("error parsing unlimited date!: ")
}
}
func TestParseDatePlus(t *testing.T) {
date, err := ParseDate("+20s")
expected := time.Now()
if err != nil || date.Sub(expected).Seconds() > 20 || date.Sub(expected).Seconds() < 19 {
t.Error("error parsing date: ", date.Sub(expected).Seconds())
}
}
func TestParseDateMonthly(t *testing.T) {
date, err := ParseDate("*monthly")
expected := time.Now().AddDate(0, 1, 0)
if err != nil || expected.Sub(date).Seconds() > 1 {
t.Error("error parsing date: ", expected.Sub(date).Seconds())
}
}
func TestParseDateRFC3339(t *testing.T) {
date, err := ParseDate("2013-07-30T19:33:10Z")
expected := time.Date(2013, 7, 30, 19, 33, 10, 0, time.UTC)
if err != nil || !date.Equal(expected) {
t.Error("error parsing date: ", expected.Sub(date))
}
}