start shared groups cvs loading

modified shared group structure
This commit is contained in:
Radu Ioan Fericean
2014-02-03 18:50:26 +02:00
parent c7ea2cd263
commit 727337e617
11 changed files with 95 additions and 34 deletions

View File

@@ -543,6 +543,7 @@ func (self *ApierV1) LoadTariffPlanFromFolder(attrs AttrLoadTPFromFolder, reply
path.Join(attrs.FolderPath, utils.DESTINATION_RATES_CSV),
path.Join(attrs.FolderPath, utils.RATING_PLANS_CSV),
path.Join(attrs.FolderPath, utils.RATING_PROFILES_CSV),
path.Join(attrs.FolderPath, utils.SHARED_GROUPS_CSV),
path.Join(attrs.FolderPath, utils.ACTIONS_CSV),
path.Join(attrs.FolderPath, utils.ACTION_PLANS_CSV),
path.Join(attrs.FolderPath, utils.ACTION_TRIGGERS_CSV),

View File

@@ -19,20 +19,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package apier
import (
"path"
"reflect"
"os/exec"
"testing"
"time"
"strings"
"net/url"
"net/http"
"net/rpc"
"flag"
"fmt"
"net/http"
"net/rpc"
"net/url"
"os/exec"
"path"
"reflect"
"strings"
"testing"
"time"
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// ToDo: Replace rpc.Client with internal rpc server and Apier using internal map as both data and stor so we can run the tests non-local

View File

@@ -45,6 +45,7 @@ type Action struct {
ExpirationString string
Weight float64
Balance *Balance
SharedGroup string
}
const (

View File

@@ -45,12 +45,13 @@ type CSVReader struct {
destinationRates map[string]*utils.TPDestinationRate
ratingPlans map[string]*RatingPlan
ratingProfiles map[string]*RatingProfile
sharedGroups map[string]*SharedGroup
// file names
destinationsFn, ratesFn, destinationratesFn, timingsFn, destinationratetimingsFn, ratingprofilesFn,
actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn string
sharedGroupsFn, actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn string
}
func NewFileCSVReader(dataStorage RatingStorage, accountingStorage AccountingStorage, sep rune, destinationsFn, timingsFn, ratesFn, destinationratesFn, destinationratetimingsFn, ratingprofilesFn, actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn string) *CSVReader {
func NewFileCSVReader(dataStorage RatingStorage, accountingStorage AccountingStorage, sep rune, destinationsFn, timingsFn, ratesFn, destinationratesFn, destinationratetimingsFn, ratingprofilesFn, sharedGroupsFn, actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn string) *CSVReader {
c := new(CSVReader)
c.sep = sep
c.dataStorage = dataStorage
@@ -63,15 +64,16 @@ func NewFileCSVReader(dataStorage RatingStorage, accountingStorage AccountingSto
c.timings = make(map[string]*utils.TPTiming)
c.ratingPlans = make(map[string]*RatingPlan)
c.ratingProfiles = make(map[string]*RatingProfile)
c.sharedGroups = make(map[string]*SharedGroup)
c.readerFunc = openFileCSVReader
c.destinationsFn, c.timingsFn, c.ratesFn, c.destinationratesFn, c.destinationratetimingsFn, c.ratingprofilesFn,
c.actionsFn, c.actiontimingsFn, c.actiontriggersFn, c.accountactionsFn = destinationsFn, timingsFn,
ratesFn, destinationratesFn, destinationratetimingsFn, ratingprofilesFn, actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn
c.sharedGroupsFn, c.actionsFn, c.actiontimingsFn, c.actiontriggersFn, c.accountactionsFn = destinationsFn, timingsFn,
ratesFn, destinationratesFn, destinationratetimingsFn, ratingprofilesFn, sharedGroupsFn, actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn
return c
}
func NewStringCSVReader(dataStorage RatingStorage, accountingStorage AccountingStorage, sep rune, destinationsFn, timingsFn, ratesFn, destinationratesFn, destinationratetimingsFn, ratingprofilesFn, actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn string) *CSVReader {
c := NewFileCSVReader(dataStorage, accountingStorage, sep, destinationsFn, timingsFn, ratesFn, destinationratesFn, destinationratetimingsFn, ratingprofilesFn, actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn)
func NewStringCSVReader(dataStorage RatingStorage, accountingStorage AccountingStorage, sep rune, destinationsFn, timingsFn, ratesFn, destinationratesFn, destinationratetimingsFn, ratingprofilesFn, sharedGroupsFn, actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn string) *CSVReader {
c := NewFileCSVReader(dataStorage, accountingStorage, sep, destinationsFn, timingsFn, ratesFn, destinationratesFn, destinationratetimingsFn, ratingprofilesFn, sharedGroupsFn, actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn)
c.readerFunc = openStringCSVReader
return c
}
@@ -441,6 +443,40 @@ func (csvr *CSVReader) LoadRatingProfiles() (err error) {
return
}
func (csvr *CSVReader) LoadSharedGroups() (err error) {
csvReader, fp, err := csvr.readerFunc(csvr.sharedGroupsFn, csvr.sep, utils.SHARED_GROUPS_NRCOLS)
if err != nil {
log.Print("Could not load shared groups file: ", err)
// allow writing of the other values
return nil
}
if fp != nil {
defer fp.Close()
}
for record, err := csvReader.Read(); err == nil; record, err = csvReader.Read() {
tag := record[0]
sg, found := csvr.sharedGroups[tag]
if found {
sg.AccountParameters[record[1]] = &SharingParameters{
Strategy: record[2],
RateSubject: record[3],
}
} else {
sg = &SharedGroup{
Id: tag,
AccountParameters: map[string]*SharingParameters{
record[1]: &SharingParameters{
Strategy: record[2],
RateSubject: record[3],
},
},
}
}
csvr.sharedGroups[tag] = sg
}
return
}
func (csvr *CSVReader) LoadActions() (err error) {
csvReader, fp, err := csvr.readerFunc(csvr.actionsFn, csvr.sep, utils.ACTIONS_NRCOLS)
if err != nil {
@@ -471,7 +507,7 @@ func (csvr *CSVReader) LoadActions() (err error) {
return errors.New(fmt.Sprintf("Could not parse action balance weight: %v", err))
}
}
weight, err := strconv.ParseFloat(record[10], 64)
weight, err := strconv.ParseFloat(record[11], 64)
if err != nil {
return errors.New(fmt.Sprintf("Could not parse action weight: %v", err))
}
@@ -482,7 +518,7 @@ func (csvr *CSVReader) LoadActions() (err error) {
Direction: record[3],
Weight: weight,
ExpirationString: record[5],
ExtraParameters: record[9],
ExtraParameters: record[10],
Balance: &Balance{
Uuid: utils.GenUUID(),
Value: units,
@@ -490,6 +526,7 @@ func (csvr *CSVReader) LoadActions() (err error) {
DestinationId: record[6],
RateSubject: record[7],
},
SharedGroup: record[9],
}
if _, err := utils.ParseDate(a.ExpirationString); err != nil {
return errors.New(fmt.Sprintf("Could not parse expiration time: %v", err))

View File

@@ -110,9 +110,12 @@ vdf,0,*out,fallback1,2013-11-18T13:46:00Z,G,fallback2
vdf,0,*out,fallback1,2013-11-18T13:47:00Z,G,fallback2
vdf,0,*out,fallback2,2013-11-18T13:45:00Z,R,rif
`
sharedGroups = `
`
actions = `
MINI,*topup_reset,*monetary,*out,10,*unlimited,,,10,,10
MINI,*topup,*minutes,*out,100,*unlimited,NAT,test,10,,10
MINI,*topup_reset,*monetary,*out,10,*unlimited,,,10,,,10
MINI,*topup,*minutes,*out,100,*unlimited,NAT,test,10,,,10
`
actionTimings = `
MORE_MINUTES,MINI,ONE_TIME_RUN,10
@@ -129,13 +132,14 @@ vdf,minitsboy,*out,MORE_MINUTES,STANDARD_TRIGGER
var csvr *CSVReader
func init() {
csvr = NewStringCSVReader(dataStorage, accountingStorage, ',', destinations, timings, rates, destinationRates, destinationRateTimings, ratingProfiles, actions, actionTimings, actionTriggers, accountActions)
csvr = NewStringCSVReader(dataStorage, accountingStorage, ',', destinations, timings, rates, destinationRates, destinationRateTimings, ratingProfiles, sharedGroups, actions, actionTimings, actionTriggers, accountActions)
csvr.LoadDestinations()
csvr.LoadTimings()
csvr.LoadRates()
csvr.LoadDestinationRates()
csvr.LoadRatingPlans()
csvr.LoadRatingProfiles()
csvr.LoadSharedGroups()
csvr.LoadActions()
csvr.LoadActionTimings()
csvr.LoadActionTriggers()

View File

@@ -40,6 +40,7 @@ type TPLoader interface {
LoadTimings() error
LoadRatingPlans() error
LoadRatingProfiles() error
LoadSharedGroups() error
LoadActions() error
LoadActionTimings() error
LoadActionTriggers() error
@@ -198,6 +199,9 @@ var FileValidators = map[string]*FileLineRegexValidator{
utils.RATING_PROFILES_CSV: &FileLineRegexValidator{utils.RATE_PROFILES_NRCOLS,
regexp.MustCompile(`(?:\w+\s*,\s*){2}(?:\*out\s*,\s*){1}(?:\*any\s*,\s*|\w+\s*,\s*){1}(?:\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z){1}(?:\w*\s*,?\s*){2}$`),
"Tenant([0-9A-Za-z_]),TOR([0-9A-Za-z_]),Direction(*out),Subject([0-9A-Za-z_]|*all),RatesFallbackSubject([0-9A-Za-z_]|<empty>),RatesTimingTag([0-9A-Za-z_]),ActivationTime([0-9T:X])"},
utils.SHARED_GROUPS_CSV: &FileLineRegexValidator{utils.SHARED_GROUPS_NRCOLS,
regexp.MustCompile(``),
""},
utils.ACTIONS_CSV: &FileLineRegexValidator{utils.ACTIONS_NRCOLS,
regexp.MustCompile(`(?:\w+\s*),(?:\*\w+\s*),(?:\*\w+\s*)?,(?:\*out\s*)?,(?:\d+\s*)?,(?:\*\w+\s*|\+\d+[smh]\s*|\d+\s*)?,(?:\*any|\w+\s*)?,(?:\*\w+\s*)?,(?:\d+\.?\d*\s*)?,(?:\S+\s*)?,(?:\d+\.?\d*\s*)$`),
"Tag([0-9A-Za-z_]),Action([0-9A-Za-z_]),BalanceType([*a-z_]),Direction(*out),Units([0-9]),ExpiryTime(*[a-z_]|+[0-9][smh]|[0-9])DestinationTag([0-9A-Za-z_]|*all),RatingSubject([0-9A-Za-z_]),BalanceWeight([0-9.]),ExtraParameters([0-9A-Za-z_:;]),Weight([0-9.])"},

View File

@@ -126,6 +126,7 @@ func TestLoadFromCSV(t *testing.T) {
path.Join(*dataDir, "tariffplans", *tpCsvScenario, utils.DESTINATION_RATES_CSV),
path.Join(*dataDir, "tariffplans", *tpCsvScenario, utils.RATING_PLANS_CSV),
path.Join(*dataDir, "tariffplans", *tpCsvScenario, utils.RATING_PROFILES_CSV),
path.Join(*dataDir, "tariffplans", *tpCsvScenario, utils.SHARED_GROUPS_CSV),
path.Join(*dataDir, "tariffplans", *tpCsvScenario, utils.ACTIONS_CSV),
path.Join(*dataDir, "tariffplans", *tpCsvScenario, utils.ACTION_PLANS_CSV),
path.Join(*dataDir, "tariffplans", *tpCsvScenario, utils.ACTION_TRIGGERS_CSV),

View File

@@ -31,12 +31,14 @@ const (
)
type SharedGroup struct {
Id string
Id string
AccountParameters map[string]*SharingParameters
Members []string
}
type SharingParameters struct {
Strategy string
Account string
RateSubject string
Weight float64
Members []string
}
func (sg *SharedGroup) GetMembersExceptUser(ubId string) []string {
@@ -51,19 +53,21 @@ func (sg *SharedGroup) GetMembersExceptUser(ubId string) []string {
return sg.Members
}
func (sg *SharedGroup) PopBalanceByStrategy(balanceChain *BalanceChain) (bal *Balance) {
func (sg *SharedGroup) PopBalanceByStrategy(account string, balanceChain *BalanceChain) (bal *Balance) {
bc := *balanceChain
if len(bc) == 0 {
return
}
index := 0
switch sg.Strategy {
sharingParameters := sg.AccountParameters[account]
switch sharingParameters.Strategy {
case STRATEGY_RANDOM:
rand.Seed(time.Now().Unix())
index = rand.Intn(len(bc))
case STRATEGY_LOWEST_FIRST:
minVal := math.MaxFloat64
for i, b := range bc {
b.RateSubject = sharingParameters.RateSubject
if b.Value < minVal {
minVal = b.Value
index = i
@@ -72,6 +76,7 @@ func (sg *SharedGroup) PopBalanceByStrategy(balanceChain *BalanceChain) (bal *Ba
case STRATEGY_HIGHEST_FIRST:
maxVal := math.SmallestNonzeroFloat64
for i, b := range bc {
b.RateSubject = sharingParameters.RateSubject
if b.Value > maxVal {
maxVal = b.Value
index = i

View File

@@ -44,8 +44,10 @@ func TestSharedPopBalanceByStrategyLow(t *testing.T) {
&Balance{Value: 1.0},
&Balance{Value: 3.0},
}
sg := &SharedGroup{Strategy: STRATEGY_LOWEST_FIRST}
b := sg.PopBalanceByStrategy(&bc)
sg := &SharedGroup{AccountParameters: map[string]*SharingParameters{
"test": &SharingParameters{Strategy: STRATEGY_LOWEST_FIRST}},
}
b := sg.PopBalanceByStrategy("test", &bc)
if b.Value != 1.0 {
t.Error("Error popping the right balance according to strategy: ", b, bc)
}
@@ -62,8 +64,10 @@ func TestSharedPopBalanceByStrategyHigh(t *testing.T) {
&Balance{Value: 1.0},
&Balance{Value: 3.0},
}
sg := &SharedGroup{Strategy: STRATEGY_HIGHEST_FIRST}
b := sg.PopBalanceByStrategy(&bc)
sg := &SharedGroup{AccountParameters: map[string]*SharingParameters{
"test": &SharingParameters{Strategy: STRATEGY_HIGHEST_FIRST}},
}
b := sg.PopBalanceByStrategy("test", &bc)
if b.Value != 3.0 {
t.Error("Error popping the right balance according to strategy: ", b, bc)
}

View File

@@ -281,7 +281,8 @@ func (ub *UserBalance) debitMinutesFromSharedBalances(sharedGroupName string, cc
sharedMinuteBalances := nUb.getBalancesForPrefix(cc.Destination, nUb.BalanceMap[MINUTES+cc.Direction], sharedGroupName)
allMinuteSharedBalances = append(allMinuteSharedBalances, sharedMinuteBalances...)
}
for sharedBalance := sharedGroup.PopBalanceByStrategy(&allMinuteSharedBalances); sharedBalance != nil; sharedBalance = sharedGroup.PopBalanceByStrategy(&allMinuteSharedBalances) {
for sharedBalance := sharedGroup.PopBalanceByStrategy(ub.Id, &allMinuteSharedBalances); sharedBalance != nil; sharedBalance = sharedGroup.PopBalanceByStrategy(ub.Id,
&allMinuteSharedBalances) {
initialValue := sharedBalance.Value
sharedBalance.DebitMinutes(cc, count, sharedBalance.userBalance, moneyBalances)
if sharedBalance.Value != initialValue {
@@ -320,7 +321,7 @@ func (ub *UserBalance) debitMoneyFromSharedBalances(sharedGroupName string, cc *
sharedMoneyBalances := nUb.getBalancesForPrefix(cc.Destination, nUb.BalanceMap[CREDIT+cc.Direction], sharedGroupName)
allMoneySharedBalances = append(allMoneySharedBalances, sharedMoneyBalances...)
}
for sharedBalance := sharedGroup.PopBalanceByStrategy(&allMoneySharedBalances); sharedBalance != nil; sharedBalance = sharedGroup.PopBalanceByStrategy(&allMoneySharedBalances) {
for sharedBalance := sharedGroup.PopBalanceByStrategy(ub.Id, &allMoneySharedBalances); sharedBalance != nil; sharedBalance = sharedGroup.PopBalanceByStrategy(ub.Id, &allMoneySharedBalances) {
initialValue := sharedBalance.Value
sharedBalance.DebitMoney(cc, count, sharedBalance.userBalance)
if sharedBalance.Value != initialValue {

View File

@@ -40,6 +40,7 @@ const (
DESTINATION_RATES_CSV = "DestinationRates.csv"
RATING_PLANS_CSV = "RatingPlans.csv"
RATING_PROFILES_CSV = "RatingProfiles.csv"
SHARED_GROUPS_CSV = "SharedGroups.csv"
ACTIONS_CSV = "Actions.csv"
ACTION_PLANS_CSV = "ActionPlans.csv"
ACTION_TRIGGERS_CSV = "ActionTriggers.csv"
@@ -50,7 +51,8 @@ const (
DESTINATION_RATES_NRCOLS = 3
DESTRATE_TIMINGS_NRCOLS = 4
RATE_PROFILES_NRCOLS = 7
ACTIONS_NRCOLS = 11
SHARED_GROUPS_NRCOLS = 5
ACTIONS_NRCOLS = 12
ACTION_PLANS_NRCOLS = 4
ACTION_TRIGGERS_NRCOLS = 8
ACCOUNT_ACTIONS_NRCOLS = 5