mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-23 16:18:44 +05:00
start shared groups cvs loading
modified shared group structure
This commit is contained in:
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -45,6 +45,7 @@ type Action struct {
|
||||
ExpirationString string
|
||||
Weight float64
|
||||
Balance *Balance
|
||||
SharedGroup string
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.])"},
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user