From 64a529ebd15cf101ac7e229aba9a62248398035b Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 10 Jul 2012 19:00:12 +0300 Subject: [PATCH] added reset counter action --- docs/importing.rst | 81 ++++++++++++++++++++++++++++------- timespans/accountlock_test.go | 2 +- timespans/action.go | 37 +++++++++++----- timespans/actions_test.go | 49 ++++++++++++++++++++- timespans/units_counter.go | 1 + 5 files changed, 140 insertions(+), 30 deletions(-) diff --git a/docs/importing.rst b/docs/importing.rst index e8cd9421a..dd02d938b 100644 --- a/docs/importing.rst +++ b/docs/importing.rst @@ -34,7 +34,7 @@ ActivationTime Multiple rates timings/prices can be created for one profile with different activation times. When a call is made the appropriate profile(s) will be used to rate the call. Rates timing -------------- +~~~~~~~~~~~~ This file makes links between a ratings and timings so each of them can be described once and various combinations are made possible. @@ -57,7 +57,8 @@ Weight Rates ------ +~~~~~ +Defines price groups for various destinations which will be associated to various timings. +---------------------+-----------------+------------+-------+-------------+ | Tag | DestinationsTag | ConnectFee | Price | BillingUnit | @@ -80,25 +81,34 @@ BillingUnit The billing unit expressed in seconds Timings -------- +~~~~~~~ +Describes the time periods that have different rates attached to them. -+-------------+--------+-----------+-----------+----------+ -| Tag | Months | MonthDays | WeekDays | StartTime| -+=============+========+===========+===========+==========+ -| WORKDAYS_00 | *all | *all | 1;2;3;4;5 | 00:00:00 | -+-------------+--------+-----------+-----------+----------+ -| WORKDAYS_18 | *all | *all | 1;2;3;4;5 | 18:00:00 | -+-------------+--------+-----------+-----------+----------+ ++-----------------+--------+-----------+-----------+----------+ +| Tag | Months | MonthDays | WeekDays | StartTime| ++=================+========+===========+===========+==========+ +| WORKDAYS | *all | *all | 1;2;3;4;5 | 00:00:00 | ++-----------------+--------+-----------+-----------+----------+ +| WEEKENDS | *all | *all | 6,7 | 00:00:00 | ++-----------------+--------+-----------+-----------+----------+ +| DAILY_SAME_TIME | *all | *all | *all | *now | ++-----------------+--------+-----------+-----------+----------+ +| ONE_TIME_RUN | *none | *none | *none | *now | ++-----------------+--------+-----------+-----------+----------+ Tag A string by witch this timing will be referenced in other places by. Months + Integers from 1=January to 12=December separated by semicolons (;) specifying the months for this time period. MonthDays + Integers from 1 to 31 separated by semicolons (;) specifying the month days for this time period. WeekDays + Integers from 1=Monday to 7=Sunday separated by semicolons (;) specifying the week days for this time period. StartTime + The start time for this time period. *now will be replaced with the time of the data importing. Destinations ------------- +~~~~~~~~~~~~ The destinations are binding together various prefixes / caller ids to define a logical destination group. A prefix can appear in multiple destination groups. @@ -118,6 +128,18 @@ Prefix Account actions --------------- +Describes the actions to be applied to the clients/users accounts. There are two kinds of actions: timed and triggered. For the timed actions there is a scheduler application that reads them from the database and executes them at the appropriate timings. The triggered actions are executed when the specified balance counters reach certain thresholds. + +The accounts hold the various balances and counters to activate the triggered actions for each the client. + +Balance types are: + MONETARY + SMS + The integer number of SMSeses + INTERNET + INTERNET_TIME + MINUTES + +------------+---------+-----------+------------------+------------------+ |Tenant | Account | Direction | ActionTimingsTag | ActionTriggersTag| +============+=========+===========+==================+==================+ @@ -127,32 +149,43 @@ Account actions +------------+---------+-----------+------------------+------------------+ Tenant + Used to distinguish between carriers if more than one share the same database in the CGRates system. Account + The identifier for the user's account. Direction + Can be IN or OUT for the INBOUND and OUTBOUND calls. ActionTimingsTag + Forwards to a timed action group that will be used on this account. ActionTriggersTag + Forwards to a triggered action group that will be applied to this account. Action triggers ---------------- +~~~~~~~~~~~~~~~ +For each account there are counters that record the activity on various balances. Action triggers allow when a counter reaches a threshold to activate a group of actions. After the execution the action trigger is marked as used and will no longer be evaluated until the triggers are reset. See actions for action trigger resetting. +------------------+------------+----------------+----------------+------------+--------+ | Tag | BalanceTag | ThresholdValue | DestinationTag | ActionsTag | Weight | +==================+============+================+================+============+========+ | STANDARD_TRIGGER | MONETARY | 30 | *all | SOME_1 | 10 | +------------------+------------+----------------+----------------+------------+--------+ -| STANDARD_TRIGGER | SMS | 30 | *all |SOME_2 | 10 | +| STANDARD_TRIGGER | SMS | 30 | *all | SOME_2 | 10 | +------------------+------------+----------------+----------------+------------+--------+ Tag A string by witch this action trigger will be referenced in other places by. BalanceTag + Specifies the balance counter by which this action will be triggered. Can be MONETARY, SMS, INTERNET, INTERNET_TIME, MINUTES. ThresholdValue + The value of the balance counter that will trigger this action. DestinationTag -ActionsTag + This field is used only if the balanceTag is MINUTES. If the balance counter monitors call minutes this field indicates the destination of the calls for which the minutes are recorded. +ActionsTag + Forwards to an action group to be executed when the threshold is reached. Weight + Specifies the order for these triggers to be evaluated. If there are multiple triggers are fired in the same time the ones with the lower weight will be executed first. Action timings --------------- +~~~~~~~~~~~~~~ +--------------+------------+------------------+--------+ | Tag | ActionsTag | TimingTag | Weight | @@ -165,11 +198,14 @@ Action timings Tag A string by witch this action timing will be referenced in other places by. ActionsTag + Forwards to an action group to be executed when the timing is right. TimingTag + A timing (one time or recurrent) at which the action group will be executed Weight + Specifies the order for these timings to be evaluated. If there are multiple action timings set to be execute on the same time the ones with the lower weight will be executed first. Actions -------- +~~~~~~~ +--------+-------------+------------+-------+----------------+-----------+------------+---------------+--------+ | Tag | Action | BalanceTag | Units | DestinationTag | PriceType | PriceValue | MinutesWeight | Weight | @@ -182,6 +218,19 @@ Actions Tag A string by witch this action will be referenced in other places by. Action + The action type. Can have one of the following: + LOG: + RESET_TRIGGERS + SET_POSTPAID: Sets account to postpaid, maintains it's balances. + RESET_POSTPAID: Set account to postpaid, reset all it's balances. + SET_PREPAID: Sets account to prepaid, maintains it's balances. Makes sense after an account was set to POSTPAID and admin wants it back. + RESET_PREPAID: Set account to prepaid, reset all it's balances. + TOPUP_RESET: Add account balance. If previous balance found of the same type, reset it before adding. + TOPUP: Add account balance. If the specific balance is not defined, define it (eg: minutes per destination). + DEBIT: Debit account balance. + RESET_COUNTER: Sets the counter for the BalanceId to 0 + RESET_ALL_COUNTER: Sets all counters to 0 + BalanceTag Units DestinationTag diff --git a/timespans/accountlock_test.go b/timespans/accountlock_test.go index 69100a1ab..55a3e058e 100644 --- a/timespans/accountlock_test.go +++ b/timespans/accountlock_test.go @@ -24,7 +24,7 @@ import ( "log" ) -func TestAccountLock(t *testing.T) { +func ATestAccountLock(t *testing.T) { go AccLock.Guard("1", func() (float64, error) { log.Print("first 1") time.Sleep(1 * time.Second) diff --git a/timespans/action.go b/timespans/action.go index a2e7018ec..643a0b23a 100644 --- a/timespans/action.go +++ b/timespans/action.go @@ -40,16 +40,17 @@ type actionTypeFunc func(*UserBalance, *Action) error var ( actionTypeFuncMap = map[string]actionTypeFunc{ - "LOG": logAction, - "RESET_TRIGGERS": resetTriggersAction, - "SET_POSTPAID": setPostpaidAction, - "RESET_POSTPAID": resetPostpaidAction, - "SET_PREPAID": setPrepaidAction, - "RESET_PREPAID": resetPrepaidAction, - "TOPUP_RESET": topupResetAction, - "TOPUP": topupAction, - "DEBIT": debitAction, - "RESET_COUNTERS": resetCountersAction, + "LOG": logAction, + "RESET_TRIGGERS": resetTriggersAction, + "SET_POSTPAID": setPostpaidAction, + "RESET_POSTPAID": resetPostpaidAction, + "SET_PREPAID": setPrepaidAction, + "RESET_PREPAID": resetPrepaidAction, + "TOPUP_RESET": topupResetAction, + "TOPUP": topupAction, + "DEBIT": debitAction, + "RESET_COUNTER": resetCounterAction, + "RESET_ALL_COUNTERS": resetAllCountersAction, } ) @@ -104,7 +105,21 @@ func debitAction(ub *UserBalance, a *Action) (err error) { return genericDebit(ub, a) } -func resetCountersAction(ub *UserBalance, a *Action) (err error) { +func resetCounterAction(ub *UserBalance, a *Action) (err error) { + uc := ub.getUnitCounter(a) + if uc == nil { + uc = &UnitsCounter{BalanceId: MINUTES} + ub.UnitCounters = append(ub.UnitCounters, uc) + } + if a.BalanceId == MINUTES { + uc.initMinuteBuckets(ub.ActionTriggers) + } else { + uc.Units = 0 + } + return +} + +func resetAllCountersAction(ub *UserBalance, a *Action) (err error) { ub.UnitCounters = make([]*UnitsCounter, 0) uc := &UnitsCounter{BalanceId: MINUTES} uc.initMinuteBuckets(ub.ActionTriggers) diff --git a/timespans/actions_test.go b/timespans/actions_test.go index de089a487..b7bd37b67 100644 --- a/timespans/actions_test.go +++ b/timespans/actions_test.go @@ -561,7 +561,7 @@ func TestActionDebitMinutes(t *testing.T) { } } -func TestActionResetCounters(t *testing.T) { +func TestActionResetAllCounters(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", Type: UB_TYPE_POSTPAID, @@ -570,7 +570,7 @@ func TestActionResetCounters(t *testing.T) { 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, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - resetCountersAction(ub, nil) + resetAllCountersAction(ub, nil) if ub.Type != UB_TYPE_POSTPAID || ub.BalanceMap[CREDIT] != 100 || len(ub.UnitCounters) != 1 || @@ -584,3 +584,48 @@ func TestActionResetCounters(t *testing.T) { t.Errorf("Minute bucked cloned incorrectly: %v!", mb) } } + +func TestActionResetCounterMinutes(t *testing.T) { + ub := &UserBalance{ + Id: "TEST_UB", + Type: UB_TYPE_POSTPAID, + BalanceMap: map[string]float64{CREDIT: 100}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, 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, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + } + a := &Action{BalanceId: MINUTES} + resetCounterAction(ub, a) + if ub.Type != UB_TYPE_POSTPAID || + ub.BalanceMap[CREDIT] != 100 || + len(ub.UnitCounters) != 2 || + len(ub.UnitCounters[1].MinuteBuckets) != 1 || + len(ub.MinuteBuckets) != 2 || + ub.ActionTriggers[0].Executed != true { + t.Error("Reset counters action failed!", ub.UnitCounters[1].MinuteBuckets) + } + mb := ub.UnitCounters[1].MinuteBuckets[0] + if mb.Weight != 20 || mb.Price != 1 || mb.Seconds != 10 || mb.DestinationId != "NAT" { + t.Errorf("Minute bucked cloned incorrectly: %v!", mb) + } +} + +func TestActionResetCounterCREDIT(t *testing.T) { + ub := &UserBalance{ + Id: "TEST_UB", + Type: UB_TYPE_POSTPAID, + BalanceMap: map[string]float64{CREDIT: 100}, + UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}, &UnitsCounter{BalanceId: SMS, 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, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + } + a := &Action{BalanceId: CREDIT} + resetCounterAction(ub, a) + if ub.Type != UB_TYPE_POSTPAID || + ub.BalanceMap[CREDIT] != 100 || + len(ub.UnitCounters) != 2 || + len(ub.MinuteBuckets) != 2 || + ub.ActionTriggers[0].Executed != true { + t.Error("Reset counters action failed!") + } +} diff --git a/timespans/units_counter.go b/timespans/units_counter.go index a52a53a44..0d75814d1 100644 --- a/timespans/units_counter.go +++ b/timespans/units_counter.go @@ -33,6 +33,7 @@ type UnitsCounter struct { } func (uc *UnitsCounter) initMinuteBuckets(ats []*ActionTrigger) { + uc.MinuteBuckets = make(bucketsorter, 0) for _, at := range ats { acs, err := storageGetter.GetActions(at.ActionsId) if err != nil {