actions sorters

This commit is contained in:
Radu Ioan Fericean
2012-07-03 16:38:43 +03:00
parent 4dce1c216f
commit d8b86cb270
12 changed files with 324 additions and 240 deletions

View File

@@ -27,21 +27,25 @@ import (
"log"
"runtime"
"time"
"sync"
)
var (
raterAddress = flag.String("rateraddr", "127.0.0.1:2000", "Rater server address (localhost:2000)")
jsonRpcAddress = flag.String("jsonrpcaddr", "127.0.0.1:2001", "Json RPC server address (localhost:2001)")
httpApiAddress = flag.String("httpapiaddr", "127.0.0.1:8000", "Http API server address (localhost:2002)")
freeswitchsrv = flag.String("freeswitchsrv", "localhost:8021", "freeswitch address host:port")
freeswitchpass = flag.String("freeswitchpass", "ClueCon", "freeswitch address host:port")
bal *balancer.Balancer
raterAddress = flag.String("rateraddr", "127.0.0.1:2000", "Rater server address (localhost:2000)")
jsonRpcAddress = flag.String("jsonrpcaddr", "127.0.0.1:2001", "Json RPC server address (localhost:2001)")
httpApiAddress = flag.String("httpapiaddr", "127.0.0.1:8000", "Http API server address (localhost:2002)")
freeswitchsrv = flag.String("freeswitchsrv", "localhost:8021", "freeswitch address host:port")
freeswitchpass = flag.String("freeswitchpass", "ClueCon", "freeswitch address host:port")
bal *balancer.Balancer
balancerRWMutex sync.RWMutex
)
/*
The function that gets the information from the raters using balancer.
*/
func GetCallCost(key *timespans.CallDescriptor, method string) (reply *timespans.CallCost) {
balancerRWMutex.RLock()
defer balancerRWMutex
err := errors.New("") //not nil value
for err != nil {
client := bal.Balance()

View File

@@ -40,25 +40,8 @@ var (
s = scheduler{}
)
/*
Structure to store action timings according to next activation time.
*/
type actiontimingqueue []*timespans.ActionTiming
func (atq actiontimingqueue) Len() int {
return len(atq)
}
func (atq actiontimingqueue) Swap(i, j int) {
atq[i], atq[j] = atq[j], atq[i]
}
func (atq actiontimingqueue) Less(j, i int) bool {
return atq[j].GetNextStartTime().Before(atq[i].GetNextStartTime())
}
type scheduler struct {
queue actiontimingqueue
queue timespans.ActionTimingPriotityList
}
func (s scheduler) loop() {
@@ -110,7 +93,7 @@ func loadActionTimings() {
log.Fatalf("Cannot get action timings:", err)
}
// recreate the queue
s.queue = actiontimingqueue{}
s.queue = timespans.ActionTimingPriotityList{}
for _, at := range actionTimings {
if at.IsOneTimeRun() {
log.Print("Executing: ", at)

View File

@@ -1,7 +1,7 @@
Tag,ActionsTag,TimingTag
STANDARD_ABO,SOME,WEEKLY_SAME_TIME
STANDARD_ABO,SOME,WEEKLY_SAME_TIME
STANDARD_ABO,SOME,WEEKLY_SAME_TIME
STANDARD_ABO,SOME,ONE_TIME_RUN
STANDARD_ABO,SOME,FIRST_DAY_OF_MONTH
MORE_MINUTES,MINI,ONE_TIME_RUN
Tag,ActionsTag,TimingTag, Weight
STANDARD_ABO,SOME,WEEKLY_SAME_TIME,10
STANDARD_ABO,SOME,WEEKLY_SAME_TIME,10
STANDARD_ABO,SOME,WEEKLY_SAME_TIME,10
STANDARD_ABO,SOME,ONE_TIME_RUN,10
STANDARD_ABO,SOME,FIRST_DAY_OF_MONTH,10
MORE_MINUTES,MINI,ONE_TIME_RUN,10
1 Tag ActionsTag TimingTag Weight
2 STANDARD_ABO SOME WEEKLY_SAME_TIME 10
3 STANDARD_ABO SOME WEEKLY_SAME_TIME 10
4 STANDARD_ABO SOME WEEKLY_SAME_TIME 10
5 STANDARD_ABO SOME ONE_TIME_RUN 10
6 STANDARD_ABO SOME FIRST_DAY_OF_MONTH 10
7 MORE_MINUTES MINI ONE_TIME_RUN 10

View File

@@ -1,5 +1,5 @@
Tag,BalanceTag,ThresholdValue,DestinationTag,ActionsTag
STANDARD_TRIGGER,MONETARY,30,*all,SOME_1
STANDARD_TRIGGER,SMS,30,*all,SOME_2
STANDARD_TRIGGER,MINUTES,10,GERMANY_O2,SOME_1
STANDARD_TRIGGER,MINUTES,200,GERMANY,SOME_2
Tag,BalanceTag,ThresholdValue,DestinationTag,ActionsTag,Weight
STANDARD_TRIGGER,MONETARY,30,*all,SOME_1,10
STANDARD_TRIGGER,SMS,30,*all,SOME_2,10
STANDARD_TRIGGER,MINUTES,10,GERMANY_O2,SOME_1,10
STANDARD_TRIGGER,MINUTES,200,GERMANY,SOME_2,10
1 Tag BalanceTag ThresholdValue DestinationTag ActionsTag Weight
2 STANDARD_TRIGGER MONETARY 30 *all SOME_1 10
3 STANDARD_TRIGGER SMS 30 *all SOME_2 10
4 STANDARD_TRIGGER MINUTES 10 GERMANY_O2 SOME_1 10
5 STANDARD_TRIGGER MINUTES 200 GERMANY SOME_2 10

View File

@@ -1,9 +1,9 @@
Tag,Action,BalanceTag,Units,DestinationTag,PriceType,PriceValue,Weight
SOME,TOPUP_RESET,MONETARY,10,*all,,,
SOME,TOPUP_RESET,SMS,100,*all,,,
SOME,TOPUP_RESET,INTERNET,1000,*all,,,
SOME,POSTPAID_RESET,MONETARY,10,*all,,,
SOME,DEBIT,MONETARY,5,*all,,,
SOME_1,DEBIT,MINUTES,10,GERMANY_O2,PERCENT,25,10
SOME_2,TOPUP_RESET,MINUTES,1000,GERMANY,ABSOLUTE,0.2,10
MINI,TOPUP,MINUTES,100,NAT,ABSOLUTE,0,10
Tag,Action,BalanceTag,Units,DestinationTag,PriceType,PriceValue,MinutesWeight,Weight
SOME,TOPUP_RESET,MONETARY,10,*all,,,,10
SOME,TOPUP_RESET,SMS,100,*all,,,,10
SOME,TOPUP_RESET,INTERNET,1000,*all,,,,10
SOME,POSTPAID_RESET,MONETARY,10,*all,,,,10
SOME,DEBIT,MONETARY,5,*all,,,,10
SOME_1,DEBIT,MINUTES,10,GERMANY_O2,PERCENT,25,10,10
SOME_2,TOPUP_RESET,MINUTES,1000,GERMANY,ABSOLUTE,0.2,10,10
MINI,TOPUP,MINUTES,100,NAT,ABSOLUTE,0,10,10
1 Tag Action BalanceTag Units DestinationTag PriceType PriceValue MinutesWeight Weight
2 SOME TOPUP_RESET MONETARY 10 *all 10
3 SOME TOPUP_RESET SMS 100 *all 10
4 SOME TOPUP_RESET INTERNET 1000 *all 10
5 SOME POSTPAID_RESET MONETARY 10 *all 10
6 SOME DEBIT MONETARY 5 *all 10
7 SOME_1 DEBIT MINUTES 10 GERMANY_O2 PERCENT 25 10 10
8 SOME_2 TOPUP_RESET MINUTES 1000 GERMANY ABSOLUTE 0.2 10 10
9 MINI TOPUP MINUTES 100 NAT ABSOLUTE 0 10 10

127
timespans/action.go Normal file
View File

@@ -0,0 +1,127 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2012 Radu Ioan Fericean
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package timespans
import (
"log"
"sort"
)
/*
Structure to be filled for each tariff plan with the bonus value for received calls minutes.
*/
type Action struct {
ActionType string
BalanceId string
Units float64
Weight float64
MinuteBucket *MinuteBucket
}
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,
}
)
func logAction(ub *UserBalance, a *Action) (err error) {
log.Printf("%v %v %v", a.BalanceId, a.Units, a.MinuteBucket)
return
}
func resetTriggersAction(ub *UserBalance, a *Action) (err error) {
ub.resetActionTriggers()
return
}
func setPostpaidAction(ub *UserBalance, a *Action) (err error) {
ub.Type = UB_TYPE_POSTPAID
return
}
func resetPostpaidAction(ub *UserBalance, a *Action) (err error) {
ub.Type = UB_TYPE_POSTPAID
return
}
func setPrepaidAction(ub *UserBalance, a *Action) (err error) {
ub.Type = UB_TYPE_PREPAID
return
}
func resetPrepaidAction(ub *UserBalance, a *Action) (err error) {
ub.Type = UB_TYPE_PREPAID
return
}
func topupResetAction(ub *UserBalance, a *Action) (err error) {
if ub.BalanceMap == nil {
ub.BalanceMap = make(map[string]float64)
}
ub.BalanceMap[a.BalanceId] = a.Units
return
}
func topupAction(ub *UserBalance, a *Action) (err error) {
if ub.BalanceMap == nil {
ub.BalanceMap = make(map[string]float64)
}
ub.BalanceMap[a.BalanceId] += a.Units
ub.addMinuteBucket(a.MinuteBucket)
return
}
func debitAction(ub *UserBalance, a *Action) (err error) {
return
}
func resetCountersAction(ub *UserBalance, a *Action) (err error) {
//ub.UnitsCounters
return
}
// Structure to store actions according to weight
type ActionPriotityList []*Action
func (apl ActionPriotityList) Len() int {
return len(apl)
}
func (apl ActionPriotityList) Swap(i, j int) {
apl[i], apl[j] = apl[j], apl[i]
}
func (apl ActionPriotityList) Less(i, j int) bool {
return apl[i].Weight < apl[j].Weight
}
func (apl ActionPriotityList) Sort() {
sort.Sort(apl)
}

View File

@@ -29,183 +29,13 @@ const (
FORMAT = "2006-1-2 15:04:05 MST"
)
// Amount of a trafic of a certain type
type UnitsCounter struct {
Direction string
BalanceId string
Units float64
Weight float64
MinuteBuckets []*MinuteBucket
}
// Structure to store actions according to weight
type countersorter []*UnitsCounter
func (s countersorter) Len() int {
return len(s)
}
func (s countersorter) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s countersorter) Less(i, j int) bool {
return s[i].Weight < s[j].Weight
}
/*
Structure to be filled for each tariff plan with the bonus value for received calls minutes.
*/
type Action struct {
ActionType string
BalanceId string
Units float64
MinuteBucket *MinuteBucket
}
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,
}
)
func logAction(ub *UserBalance, a *Action) (err error) {
log.Printf("%v %v %v", a.BalanceId, a.Units, a.MinuteBucket)
return
}
func resetTriggersAction(ub *UserBalance, a *Action) (err error) {
ub.resetActionTriggers()
return
}
func setPostpaidAction(ub *UserBalance, a *Action) (err error) {
ub.Type = UB_TYPE_POSTPAID
return
}
func resetPostpaidAction(ub *UserBalance, a *Action) (err error) {
ub.Type = UB_TYPE_POSTPAID
return
}
func setPrepaidAction(ub *UserBalance, a *Action) (err error) {
ub.Type = UB_TYPE_PREPAID
return
}
func resetPrepaidAction(ub *UserBalance, a *Action) (err error) {
ub.Type = UB_TYPE_PREPAID
return
}
func topupResetAction(ub *UserBalance, a *Action) (err error) {
if ub.BalanceMap == nil {
ub.BalanceMap = make(map[string]float64)
}
ub.BalanceMap[a.BalanceId] = a.Units
return storageGetter.SetUserBalance(ub)
}
func topupAction(ub *UserBalance, a *Action) (err error) {
if ub.BalanceMap == nil {
ub.BalanceMap = make(map[string]float64)
}
ub.BalanceMap[a.BalanceId] += a.Units
ub.addMinuteBucket(a.MinuteBucket)
return storageGetter.SetUserBalance(ub)
}
func debitAction(ub *UserBalance, a *Action) (err error) {
return
}
func resetCountersAction(ub *UserBalance, a *Action) (err error) {
//ub.UnitsCounters
return
}
type ActionTrigger struct {
BalanceId string
ThresholdValue float64
DestinationId string
Priority float64
ActionsId string
executed bool
}
func (at *ActionTrigger) Execute(ub *UserBalance) (err error) {
aac, err := storageGetter.GetActions(at.ActionsId)
if err != nil {
log.Print("Failed to get actions: ", err)
return
}
for _, a := range aac {
actionFunction, exists := actionTypeFuncMap[a.ActionType]
if !exists {
log.Printf("Function type %v not available, aborting execution!", a.ActionType)
return
}
err = actionFunction(ub, a)
}
at.executed = true
return
}
// Structure to store actions according to weight
type ActionTriggerPriotityList []*ActionTrigger
func (atpl ActionTriggerPriotityList) Len() int {
return len(atpl)
}
func (atpl ActionTriggerPriotityList) Swap(i, j int) {
atpl[i], atpl[j] = atpl[j], atpl[i]
}
func (atpl ActionTriggerPriotityList) Less(i, j int) bool {
return atpl[i].Priority < atpl[j].Priority
}
func (atpl ActionTriggerPriotityList) Sort() {
sort.Sort(atpl)
}
type ActionTiming struct {
Tag string // informative purpos only
UserBalanceIds []string
Timing *Interval
Weight float64
ActionsId string
actions []*Action
}
func (at *ActionTiming) getActions() (as []*Action, err error) {
if at.actions == nil {
at.actions, err = storageGetter.GetActions(at.ActionsId)
}
return at.actions, err
}
func (at *ActionTiming) getUserBalances() (ubs []*UserBalance) {
for _, ubId := range at.UserBalanceIds {
ub, err := storageGetter.GetUserBalance(ubId)
if err != nil {
log.Printf("Could not get user balances for therse id: %v. Skipping!", ubId)
}
ubs = append(ubs, ub)
}
return
actions ActionPriotityList
}
func (at *ActionTiming) GetNextStartTime() (t time.Time) {
@@ -298,7 +128,28 @@ MONTHS:
return
}
func (at *ActionTiming) getActions() (as []*Action, err error) {
if at.actions == nil {
at.actions, err = storageGetter.GetActions(at.ActionsId)
}
at.actions.Sort()
return at.actions, err
}
func (at *ActionTiming) getUserBalances() (ubs []*UserBalance) {
for _, ubId := range at.UserBalanceIds {
ub, err := storageGetter.GetUserBalance(ubId)
if err != nil {
log.Printf("Could not get user balances for therse id: %v. Skipping!", ubId)
}
ubs = append(ubs, ub)
}
return
}
func (at *ActionTiming) Execute() (err error) {
userBalancesRWMutex.Lock()
defer userBalancesRWMutex.Unlock()
aac, err := at.getActions()
if err != nil {
log.Print("Failed to get actions: ", err)
@@ -312,6 +163,7 @@ func (at *ActionTiming) Execute() (err error) {
}
for _, ub := range at.getUserBalances() {
err = actionFunction(ub, a)
storageGetter.SetUserBalance(ub)
}
}
return
@@ -324,3 +176,22 @@ func (at *ActionTiming) IsOneTimeRun() bool {
}
return len(i.Months) == 0 && len(i.MonthDays) == 0 && len(i.WeekDays) == 0
}
// Structure to store actions according to weight
type ActionTimingPriotityList []*ActionTiming
func (atpl ActionTimingPriotityList) Len() int {
return len(atpl)
}
func (atpl ActionTimingPriotityList) Swap(i, j int) {
atpl[i], atpl[j] = atpl[j], atpl[i]
}
func (atpl ActionTimingPriotityList) Less(i, j int) bool {
return atpl[i].GetNextStartTime().Before(atpl[j].GetNextStartTime()) || atpl[i].Weight < atpl[j].Weight
}
func (atpl ActionTimingPriotityList) Sort() {
sort.Sort(atpl)
}

View File

@@ -0,0 +1,75 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2012 Radu Ioan Fericean
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package timespans
import (
"log"
"sort"
)
type ActionTrigger struct {
BalanceId string
ThresholdValue float64
DestinationId string
Weight float64
ActionsId string
executed bool
}
func (at *ActionTrigger) Execute(ub *UserBalance) (err error) {
userBalancesRWMutex.Lock()
defer userBalancesRWMutex.Unlock()
var aac ActionPriotityList
aac, err = storageGetter.GetActions(at.ActionsId)
aac.Sort()
if err != nil {
log.Print("Failed to get actions: ", err)
return
}
for _, a := range aac {
actionFunction, exists := actionTypeFuncMap[a.ActionType]
if !exists {
log.Printf("Function type %v not available, aborting execution!", a.ActionType)
return
}
err = actionFunction(ub, a)
}
at.executed = true
storageGetter.SetUserBalance(ub)
return
}
// Structure to store actions according to weight
type ActionTriggerPriotityList []*ActionTrigger
func (atpl ActionTriggerPriotityList) Len() int {
return len(atpl)
}
func (atpl ActionTriggerPriotityList) Swap(i, j int) {
atpl[i], atpl[j] = atpl[j], atpl[i]
}
func (atpl ActionTriggerPriotityList) Less(i, j int) bool {
return atpl[i].Weight < atpl[j].Weight
}
func (atpl ActionTriggerPriotityList) Sort() {
sort.Sort(atpl)
}

View File

@@ -232,9 +232,9 @@ func TestActionTimingLogFunction(t *testing.T) {
}
func TestActionTriggerPriotityList(t *testing.T) {
at1 := &ActionTrigger{Priority: 10}
at2 := &ActionTrigger{Priority: 20}
at3 := &ActionTrigger{Priority: 30}
at1 := &ActionTrigger{Weight: 10}
at2 := &ActionTrigger{Weight: 20}
at3 := &ActionTrigger{Weight: 30}
var atpl ActionTriggerPriotityList
atpl = append(atpl, at2, at1, at3)
atpl.Sort()

View File

@@ -175,6 +175,8 @@ func (cd *CallDescriptor) splitInTimeSpans() (timespans []*TimeSpan) {
Splits the received timespan into sub time spans according to the activation periods intervals.
*/
func (cd *CallDescriptor) splitTimeSpan(firstSpan *TimeSpan) (timespans []*TimeSpan) {
userBalancesRWMutex.RLock()
defer userBalancesRWMutex.RUnlock()
timespans = append(timespans, firstSpan)
// split on (free) minute buckets
if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
@@ -289,6 +291,8 @@ and will decrease it by 10% for nine times. So if the user has little credit it
If the user has no credit then it will return 0.
*/
func (cd *CallDescriptor) GetMaxSessionTime() (seconds float64, err error) {
userBalancesRWMutex.RLock()
defer userBalancesRWMutex.RUnlock()
_, err = cd.SearchStorageForPrefix()
now := time.Now()
availableCredit, availableSeconds := 0.0, 0.0
@@ -331,6 +335,8 @@ func (cd *CallDescriptor) GetMaxSessionTime() (seconds float64, err error) {
// Interface method used to add/substract an amount of cents or bonus seconds (as returned by GetCost method)
// from user's money balance.
func (cd *CallDescriptor) Debit() (cc *CallCost, err error) {
userBalancesRWMutex.Lock()
defer userBalancesRWMutex.Unlock()
cc, err = cd.GetCost()
if err != nil {
log.Printf("error getting cost %v", err)
@@ -353,6 +359,8 @@ Interface method used to add/substract an amount of cents from user's money bala
The amount filed has to be filled in call descriptor.
*/
func (cd *CallDescriptor) DebitCents() (left float64, err error) {
userBalancesRWMutex.Lock()
defer userBalancesRWMutex.Unlock()
if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
return userBalance.debitMoneyBalance(cd.Amount), nil
}
@@ -364,6 +372,8 @@ Interface method used to add/substract an amount of units from user's sms balanc
The amount filed has to be filled in call descriptor.
*/
func (cd *CallDescriptor) DebitSMS() (left float64, err error) {
userBalancesRWMutex.Lock()
defer userBalancesRWMutex.Unlock()
if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
return userBalance.debitSMSBuget(cd.Amount)
}
@@ -375,6 +385,8 @@ Interface method used to add/substract an amount of seconds from user's minutes
The amount filed has to be filled in call descriptor.
*/
func (cd *CallDescriptor) DebitSeconds() (err error) {
userBalancesRWMutex.Lock()
defer userBalancesRWMutex.Unlock()
if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
return userBalance.debitMinutesBalance(cd.Amount, cd.Destination)
}
@@ -388,6 +400,8 @@ specified in the tariff plan is applied.
The amount filed has to be filled in call descriptor.
*/
// func (cd *CallDescriptor) AddRecievedCallSeconds() (err error) {
// userBalancesRWMutex.Lock()
// defer userBalancesRWMutex.Unlock()
// if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
// return userBalance.addReceivedCallSeconds(INBOUND, cd.TOR, cd.Destination, cd.Amount)
// }
@@ -398,6 +412,8 @@ The amount filed has to be filled in call descriptor.
Resets user balances value to the amounts specified in the tariff plan.
*/
/*func (cd *CallDescriptor) ResetUserBalance() (err error) {
userBalancesRWMutex.Lock()
defer userBalancesRWMutex.Unlock()
if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
return userBalance.resetUserBalance()
}

View File

@@ -199,23 +199,6 @@ func TestMaxSessionTimeNoCredit(t *testing.T) {
}
}
/*func TestGetCostWithVolumeDiscount(t *testing.T) {
storageGetter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
defer storageGetter.Close()
vd1 := &VolumeDiscount{100, 10}
vd2 := &VolumeDiscount{500, 20}
seara := &TariffPlan{Id: "seara", SmsCredit: 100, VolumeDiscountThresholds: []*VolumeDiscount{vd1, vd2}}
rifsBalance := &UserBalance{Id: "rif", Credit: 21, tariffPlan: seara, ResetDayOfTheMonth: 10, VolumeDiscountSeconds: 105}
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{Tenant: "vdf", Subject: "rif", Destination: "0723", TimeStart: t1, TimeEnd: t2, userBalance: rifsBalance}
callCost, err := cd.GetCost()
if callCost.Cost != 54.0 || err != nil {
t.Errorf("Expected %v was %v", 54.0, callCost)
}
}
*/
func TestApAddAPIfNotPresent(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)}

View File

@@ -19,7 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package timespans
import (
// "log"
// "log"
"sync"
)
const (
@@ -40,7 +41,8 @@ const (
)
var (
storageGetter StorageGetter
storageGetter StorageGetter
userBalancesRWMutex sync.RWMutex
)
/*
@@ -263,3 +265,26 @@ Resets the user balance items to their tariff plan values.
return
}
*/
// Amount of a trafic of a certain type
type UnitsCounter struct {
Direction string
BalanceId string
Units float64
Weight float64
MinuteBuckets []*MinuteBucket
}
// Structure to store actions according to weight
type countersorter []*UnitsCounter
func (s countersorter) Len() int {
return len(s)
}
func (s countersorter) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s countersorter) Less(i, j int) bool {
return s[i].Weight < s[j].Weight
}