rounding methods in csv loader and implemented triggered type

This commit is contained in:
Radu Ioan Fericean
2013-07-18 13:43:41 +03:00
parent 2eb8c093ad
commit d5c4c717f3
9 changed files with 119 additions and 45 deletions

View File

@@ -29,6 +29,7 @@ type ActionTrigger struct {
BalanceId string
Direction string
ThresholdValue float64
ThresholdType string
DestinationId string
Weight float64
ActionsId string

View File

@@ -235,7 +235,7 @@ func (csvr *CSVReader) LoadRates() (err error) {
continue
}
var r *Rate
r, err = NewRate(record[0], record[1], record[2], record[3], record[4], record[5])
r, err = NewRate(record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7])
if err != nil {
return err
}
@@ -492,7 +492,7 @@ func (csvr *CSVReader) LoadActionTriggers() (err error) {
if err != nil {
return errors.New(fmt.Sprintf("Could not parse action trigger value: %v", err))
}
weight, err := strconv.ParseFloat(record[6], 64)
weight, err := strconv.ParseFloat(record[7], 64)
if err != nil {
return errors.New(fmt.Sprintf("Could not parse action trigger weight: %v", err))
}
@@ -501,8 +501,9 @@ func (csvr *CSVReader) LoadActionTriggers() (err error) {
BalanceId: record[1],
Direction: record[2],
ThresholdValue: value,
DestinationId: record[4],
ActionsId: record[5],
ThresholdType: record[4],
DestinationId: record[5],
ActionsId: record[6],
Weight: weight,
}
csvr.actionsTriggers[tag] = append(csvr.actionsTriggers[tag], at)

View File

@@ -44,11 +44,11 @@ WEEKENDS,*all,*all,*all,6;7,00:00:00
ONE_TIME_RUN,2012,,,,*asap
`
rates = `
R1,0,0.2,60,1,10
R2,0,0.1,60,1,10
R3,0,0.05,60,1,10
R4,1,1,1,1,10
R5,0,0.5,1,1,10
R1,0,0.2,60,1,*middle,2,10
R2,0,0.1,60,1,*middle,2,10
R3,0,0.05,60,1,*middle,2,10
R4,1,1,1,1,*up,2,10
R5,0,0.5,1,1,*down,2,10
`
destinationRates = `
RT_STANDARD,GERMANY,R1
@@ -90,8 +90,8 @@ MINI,TOPUP,MINUTES,OUT,100,NAT,ABSOLUTE,0,10,10
MORE_MINUTES,MINI,ONE_TIME_RUN,10
`
actionTriggers = `
STANDARD_TRIGGER,MINUTES,OUT,10,GERMANY_O2,SOME_1,10
STANDARD_TRIGGER,MINUTES,OUT,200,GERMANY,SOME_2,10
STANDARD_TRIGGER,MINUTES,OUT,10,COUNTER,GERMANY_O2,SOME_1,10
STANDARD_TRIGGER,MINUTES,OUT,200,BALANCE,GERMANY,SOME_2,10
`
accountActions = `
vdf,minitsboy,OUT,MORE_MINUTES,STANDARD_TRIGGER

View File

@@ -50,7 +50,7 @@ type Rate struct {
Weight float64
}
func NewRate(tag, connectFee, price, pricedUnits, rateIncrements, weight string) (r *Rate, err error) {
func NewRate(tag, connectFee, price, pricedUnits, rateIncrements, roundingMethod, roundingDecimals, weight string) (r *Rate, err error) {
cf, err := strconv.ParseFloat(connectFee, 64)
if err != nil {
log.Printf("Error parsing connect fee from: %v", connectFee)
@@ -76,13 +76,21 @@ func NewRate(tag, connectFee, price, pricedUnits, rateIncrements, weight string)
log.Printf("Error parsing rates increments from: %s", weight)
return
}
rd, err := strconv.Atoi(roundingDecimals)
if err != nil {
log.Printf("Error parsing rounding decimals: %s", roundingDecimals)
return
}
r = &Rate{
Tag: tag,
ConnectFee: cf,
Price: p,
PricedUnits: pu,
RateIncrements: ri,
Weight: wght,
Tag: tag,
ConnectFee: cf,
Price: p,
PricedUnits: pu,
RateIncrements: ri,
Weight: wght,
RoundingMethod: roundingMethod,
RoundingDecimals: rd,
}
return
}

View File

@@ -242,6 +242,7 @@ func (at *ActionTrigger) Store() (result string, err error) {
result += at.DestinationId + ";"
result += at.ActionsId + ";"
result += strconv.FormatFloat(at.ThresholdValue, 'f', -1, 64) + ";"
result += at.ThresholdType + ";"
result += strconv.FormatFloat(at.Weight, 'f', -1, 64) + ";"
result += strconv.FormatBool(at.Executed)
return
@@ -249,7 +250,7 @@ func (at *ActionTrigger) Store() (result string, err error) {
func (at *ActionTrigger) Restore(input string) error {
elements := strings.Split(input, ";")
if len(elements) != 8 {
if len(elements) != 9 {
return notEnoughElements
}
at.Id = elements[0]
@@ -258,8 +259,9 @@ func (at *ActionTrigger) Restore(input string) error {
at.DestinationId = elements[3]
at.ActionsId = elements[4]
at.ThresholdValue, _ = strconv.ParseFloat(elements[5], 64)
at.Weight, _ = strconv.ParseFloat(elements[6], 64)
at.Executed, _ = strconv.ParseBool(elements[7])
at.ThresholdType = elements[6]
at.Weight, _ = strconv.ParseFloat(elements[7], 64)
at.Executed, _ = strconv.ParseBool(elements[8])
return nil
}

View File

@@ -117,12 +117,13 @@ func TestActionTriggerStoreRestore(t *testing.T) {
BalanceId: CREDIT,
Direction: OUTBOUND,
ThresholdValue: 100.0,
ThresholdType: "MAX_COUNTER",
DestinationId: "NAT",
Weight: 10.0,
ActionsId: "Commando",
}
r, err := at.Store()
if err != nil || r != "some_uuid;MONETARY;OUT;NAT;Commando;100;10;false" {
if err != nil || r != "some_uuid;MONETARY;OUT;NAT;Commando;100;MAX_COUNTER;10;false" {
t.Errorf("Error serializing action trigger: %v", string(r))
}
o := &ActionTrigger{}

View File

@@ -977,8 +977,8 @@ func (self *SQLStorage) GetTpActionTriggers(tpid, tag string) (map[string][]*Act
for rows.Next() {
var id int
var threshold, weight float64
var tpid, tag, balances_tag, direction, destinations_tag, actions_tag string
if err := rows.Scan(&id, &tpid, &tag, &balances_tag, &direction, &threshold, &destinations_tag, &actions_tag, &weight); err != nil {
var tpid, tag, balances_tag, direction, destinations_tag, actions_tag, thresholdType string
if err := rows.Scan(&id, &tpid, &tag, &balances_tag, &direction, &threshold, &thresholdType, &destinations_tag, &actions_tag, &weight); err != nil {
return nil, err
}
@@ -987,6 +987,7 @@ func (self *SQLStorage) GetTpActionTriggers(tpid, tag string) (map[string][]*Act
BalanceId: balances_tag,
Direction: direction,
ThresholdValue: threshold,
ThresholdType: thresholdType,
DestinationId: destinations_tag,
ActionsId: actions_tag,
Weight: weight,

View File

@@ -21,6 +21,7 @@ package rater
import (
"errors"
"sort"
"strings"
"time"
)
@@ -261,19 +262,65 @@ func (ub *UserBalance) executeActionTriggers() {
// the next reset (see RESET_TRIGGERS action type)
continue
}
for _, uc := range ub.UnitCounters {
if uc.BalanceId == at.BalanceId {
if at.BalanceId == MINUTES && at.DestinationId != "" { // last check adds safty
for _, mb := range uc.MinuteBuckets {
if mb.DestinationId == at.DestinationId && mb.Seconds >= at.ThresholdValue {
// run the actions
at.Execute(ub)
if strings.Contains(at.ThresholdType, "COUNTER") {
for _, uc := range ub.UnitCounters {
if uc.BalanceId == at.BalanceId {
if at.BalanceId == MINUTES && at.DestinationId != "" { // last check adds safety
for _, mb := range uc.MinuteBuckets {
if strings.Contains(at.ThresholdType, "MAX") {
if mb.DestinationId == at.DestinationId && mb.Seconds >= at.ThresholdValue {
// run the actions
at.Execute(ub)
}
} else { //MIN
if mb.DestinationId == at.DestinationId && mb.Seconds <= at.ThresholdValue {
// run the actions
at.Execute(ub)
}
}
}
} else {
if strings.Contains(at.ThresholdType, "MAX") {
if uc.Units >= at.ThresholdValue {
// run the actions
at.Execute(ub)
}
} else { //MIN
if uc.Units <= at.ThresholdValue {
// run the actions
at.Execute(ub)
}
}
}
}
}
} else { // BALANCE
for _, b := range ub.BalanceMap[at.BalanceId] {
if at.BalanceId == MINUTES && at.DestinationId != "" { // last check adds safety
for _, mb := range ub.MinuteBuckets {
if strings.Contains(at.ThresholdType, "MAX") {
if mb.DestinationId == at.DestinationId && mb.Seconds >= at.ThresholdValue {
// run the actions
at.Execute(ub)
}
} else { //MIN
if mb.DestinationId == at.DestinationId && mb.Seconds <= at.ThresholdValue {
// run the actions
at.Execute(ub)
}
}
}
} else {
if uc.Units >= at.ThresholdValue {
// run the actions
at.Execute(ub)
if strings.Contains(at.ThresholdType, "MAX") {
if b.Value >= at.ThresholdValue {
// run the actions
at.Execute(ub)
}
} else { //MIN
if b.Value <= at.ThresholdValue {
// run the actions
at.Execute(ub)
}
}
}
}

View File

@@ -417,38 +417,51 @@ func TestUserBalanceAddMinutBucketEmpty(t *testing.T) {
}
}
/*
func TestUserBalanceExecuteTriggeredActions(t *testing.T) {
ub := &UserBalance{
Id: "TEST_UB",
BalanceMap: map[string]float64{CREDIT + OUTBOUND: 100},
BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}},
UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}},
MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}},
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS"}},
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ThresholdType: "MAX_COUNTER", ActionsId: "TEST_ACTIONS"}},
}
ub.countUnits(&Action{BalanceId: CREDIT, Units: 1})
if ub.BalanceMap[CREDIT+OUTBOUND] != 110 || ub.MinuteBuckets[0].Seconds != 20 {
t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND], ub.MinuteBuckets[0].Seconds)
if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 110 || ub.MinuteBuckets[0].Seconds != 20 {
t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.MinuteBuckets[0].Seconds)
}
// are set to executed
ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1})
if ub.BalanceMap[CREDIT+OUTBOUND] != 110 || ub.MinuteBuckets[0].Seconds != 20 {
t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND], ub.MinuteBuckets[0].Seconds)
if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 110 || ub.MinuteBuckets[0].Seconds != 20 {
t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.MinuteBuckets[0].Seconds)
}
// we can reset them
ub.resetActionTriggers()
ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1})
if ub.BalanceMap[CREDIT+OUTBOUND] != 120 || ub.MinuteBuckets[0].Seconds != 30 {
t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND], ub.MinuteBuckets[0].Seconds)
if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 120 || ub.MinuteBuckets[0].Seconds != 30 {
t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.MinuteBuckets[0].Seconds)
}
}*/
}
func TestUserBalanceExecuteTriggeredActionsBalance(t *testing.T) {
ub := &UserBalance{
Id: "TEST_UB",
BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}},
UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}},
MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}},
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 100, ThresholdType: "MIN_COUNTER", ActionsId: "TEST_ACTIONS"}},
}
ub.countUnits(&Action{BalanceId: CREDIT, Units: 1})
if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 110 || ub.MinuteBuckets[0].Seconds != 20 {
t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.MinuteBuckets[0].Seconds)
}
}
func TestUserBalanceExecuteTriggeredActionsOrder(t *testing.T) {
ub := &UserBalance{
Id: "TEST_UB_OREDER",
BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}},
UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}},
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS_ORDER"}},
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ThresholdType: "MAX_COUNTER", ActionsId: "TEST_ACTIONS_ORDER"}},
}
ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1})
if len(ub.BalanceMap[CREDIT+OUTBOUND]) != 1 || ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 10 {