improved csv loading

This commit is contained in:
Radu Ioan Fericean
2013-09-25 21:52:06 +03:00
parent 17b982bdeb
commit 6c35f6e340
13 changed files with 304 additions and 329 deletions

View File

@@ -39,7 +39,10 @@ func (self *ApierV1) SetTPDestinationRate(attrs utils.TPDestinationRate, reply *
}
drs := make([]*engine.DestinationRate, len(attrs.DestinationRates))
for idx, dr := range attrs.DestinationRates {
drs[idx] = &engine.DestinationRate{attrs.DestinationRateId, dr.DestinationId, dr.RateId, nil}
drs[idx] = &engine.DestinationRate{
Tag: attrs.DestinationRateId,
DestinationsTag: dr.DestinationId,
RateTag: dr.RateId}
}
if err := self.StorDb.SetTPDestinationRates(attrs.TPid, map[string][]*engine.DestinationRate{attrs.DestinationRateId: drs}); err != nil {
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())

View File

@@ -42,7 +42,7 @@ func (self *ApierV1) SetTPDestRateTiming(attrs utils.TPDestRateTiming, reply *st
drts[idx] = &engine.DestinationRateTiming{Tag: attrs.DestRateTimingId,
DestinationRatesTag: drt.DestRatesId,
Weight: drt.Weight,
TimingsTag: drt.TimingId,
TimingTag: drt.TimingId,
}
}
if err := self.StorDb.SetTPDestRateTimings(attrs.TPid, map[string][]*engine.DestinationRateTiming{attrs.DestRateTimingId: drts}); err != nil {

View File

@@ -30,19 +30,19 @@ import (
)
type CSVReader struct {
sep rune
storage DataStorage
readerFunc func(string, rune, int) (*csv.Reader, *os.File, error)
actions map[string][]*Action
actionsTimings map[string][]*ActionTiming
actionsTriggers map[string][]*ActionTrigger
accountActions []*UserBalance
destinations []*Destination
timings map[string]*Timing
rates map[string]*LoadRate
destinationRates map[string][]*DestinationRate
ratingPlans map[string]*RatingPlan
ratingProfiles map[string]*RatingProfile
sep rune
storage DataStorage
readerFunc func(string, rune, int) (*csv.Reader, *os.File, error)
actions map[string][]*Action
actionsTimings map[string][]*ActionTiming
actionsTriggers map[string][]*ActionTrigger
accountActions []*UserBalance
destinations []*Destination
timings map[string]*Timing
rates map[string][]*LoadRate
destinationRates map[string][]*DestinationRate
destinationRateTimings map[string][]*DestinationRateTiming
ratingProfiles map[string]*RatingProfile
// file names
destinationsFn, ratesFn, destinationratesFn, timingsFn, destinationratetimingsFn, ratingprofilesFn,
actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn string
@@ -55,10 +55,10 @@ func NewFileCSVReader(storage DataStorage, sep rune, destinationsFn, timingsFn,
c.actions = make(map[string][]*Action)
c.actionsTimings = make(map[string][]*ActionTiming)
c.actionsTriggers = make(map[string][]*ActionTrigger)
c.rates = make(map[string]*LoadRate)
c.rates = make(map[string][]*LoadRate)
c.destinationRates = make(map[string][]*DestinationRate)
c.timings = make(map[string]*Timing)
c.ratingPlans = make(map[string]*RatingPlan)
c.destinationRateTimings = make(map[string][]*DestinationRateTiming)
c.ratingProfiles = make(map[string]*RatingProfile)
c.readerFunc = openFileCSVReader
c.destinationsFn, c.timingsFn, c.ratesFn, c.destinationratesFn, c.destinationratetimingsFn, c.ratingprofilesFn,
@@ -228,7 +228,14 @@ func (csvr *CSVReader) LoadRates() (err error) {
if err != nil {
return err
}
csvr.rates[tag] = r
// same tag only to create rate groups
existingRates, exists := csvr.rates[tag]
if exists {
if err := existingRates[len(existingRates)-1].ValidNextGroup(r); err != nil {
return err
}
}
csvr.rates[tag] = append(csvr.rates[tag], r)
}
return
}
@@ -249,11 +256,20 @@ func (csvr *CSVReader) LoadDestinationRates() (err error) {
if !exists {
return errors.New(fmt.Sprintf("Could not get rates for tag %v", record[2]))
}
//ToDo: Not checking presence of destinations?
destinationExists := false
for _, d := range csvr.destinations {
if d.Id == record[1] {
destinationExists = true
break
}
}
if !destinationExists {
return errors.New(fmt.Sprintf("Could not get destination for tag %v", record[1]))
}
dr := &DestinationRate{
Tag: tag,
DestinationsTag: record[1],
Rate: r,
rates: r,
}
csvr.destinationRates[tag] = append(csvr.destinationRates[tag], dr)
@@ -277,18 +293,12 @@ func (csvr *CSVReader) LoadDestinationRateTimings() (err error) {
if !exists {
return errors.New(fmt.Sprintf("Could not get timing for tag %v", record[2]))
}
rt := NewDestinationRateTiming(record[1], t, record[3])
drs, exists := csvr.destinationRates[record[1]]
if !exists {
return errors.New(fmt.Sprintf("Could not find destination rate for tag %v", record[1]))
}
for _, dr := range drs {
if _, exists := csvr.ratingPlans[tag]; !exists {
csvr.ratingPlans[tag] = &RatingPlan{}
}
csvr.ratingPlans[tag].AddRateInterval(rt.GetRateInterval(dr))
}
drt := NewDestinationRateTiming(drs, t, record[3])
csvr.destinationRateTimings[tag] = append(csvr.destinationRateTimings[tag], drt)
}
return
}
@@ -315,19 +325,22 @@ func (csvr *CSVReader) LoadRatingProfiles() (err error) {
rp = &RatingProfile{Id: key}
csvr.ratingProfiles[key] = rp
}
for _, d := range csvr.destinations {
ap, exists := csvr.ratingPlans[record[5]]
if !exists {
return errors.New(fmt.Sprintf("Could not load ratinTiming for tag: %v", record[5]))
}
newAP := &RatingPlan{ActivationTime: at}
//copy(newAP.Intervals, ap.Intervals)
newAP.RateIntervals = append(newAP.RateIntervals, ap.RateIntervals...)
rp.AddRatingPlanIfNotPresent(d.Id, newAP)
if fallbacksubject != "" {
rp.FallbackKey = fmt.Sprintf("%s:%s:%s:%s", direction, tenant, tor, fallbacksubject)
drts, exists := csvr.destinationRateTimings[record[5]]
if !exists {
return errors.New(fmt.Sprintf("Could not load destination rate timings for tag: %v", record[5]))
}
for _, drt := range drts {
plan := &RatingPlan{ActivationTime: at}
for _, dr := range drt.destinationRates {
plan.AddRateInterval(drt.GetRateInterval(dr))
rp.AddRatingPlanIfNotPresent(dr.DestinationsTag, plan)
}
}
if fallbacksubject != "" {
rp.FallbackKey = fmt.Sprintf("%s:%s:%s:%s", direction, tenant, tor, fallbacksubject)
}
}
return
}

View File

@@ -209,7 +209,7 @@ func TestLoadRates(t *testing.T) {
if len(csvr.rates) != 5 {
t.Error("Failed to load rates: ", csvr.rates)
}
rate := csvr.rates["R1"]
rate := csvr.rates["R1"][0]
if !reflect.DeepEqual(rate, &LoadRate{
Tag: "R1",
ConnectFee: 0,
@@ -221,9 +221,9 @@ func TestLoadRates(t *testing.T) {
RoundingDecimals: 2,
Weight: 10,
}) {
t.Error("Error loading rate: ", csvr.rates)
t.Error("Error loading rate: ", csvr.rates["R1"][0])
}
rate = csvr.rates["R2"]
rate = csvr.rates["R2"][0]
if !reflect.DeepEqual(rate, &LoadRate{
Tag: "R2",
ConnectFee: 0,
@@ -237,7 +237,7 @@ func TestLoadRates(t *testing.T) {
}) {
t.Error("Error loading rate: ", csvr.rates)
}
rate = csvr.rates["R3"]
rate = csvr.rates["R3"][0]
if !reflect.DeepEqual(rate, &LoadRate{
Tag: "R3",
ConnectFee: 0,
@@ -251,7 +251,7 @@ func TestLoadRates(t *testing.T) {
}) {
t.Error("Error loading rate: ", csvr.rates)
}
rate = csvr.rates["R4"]
rate = csvr.rates["R4"][0]
if !reflect.DeepEqual(rate, &LoadRate{
Tag: "R4",
ConnectFee: 1,
@@ -265,7 +265,7 @@ func TestLoadRates(t *testing.T) {
}) {
t.Error("Error loading rate: ", csvr.rates)
}
rate = csvr.rates["R5"]
rate = csvr.rates["R5"][0]
if !reflect.DeepEqual(rate, &LoadRate{
Tag: "R5",
ConnectFee: 0,
@@ -284,24 +284,24 @@ func TestLoadRates(t *testing.T) {
func TestLoadDestinationRates(t *testing.T) {
if len(csvr.destinationRates) != 5 {
t.Error("Failed to load rates: ", csvr.rates)
t.Error("Failed to load destinationrates: ", csvr.destinationRates)
}
drs := csvr.destinationRates["RT_STANDARD"]
if !reflect.DeepEqual(drs, []*DestinationRate{
&DestinationRate{
Tag: "RT_STANDARD",
DestinationsTag: "GERMANY",
Rate: csvr.rates["R1"],
rates: csvr.rates["R1"],
},
&DestinationRate{
Tag: "RT_STANDARD",
DestinationsTag: "GERMANY_O2",
Rate: csvr.rates["R2"],
rates: csvr.rates["R2"],
},
&DestinationRate{
Tag: "RT_STANDARD",
DestinationsTag: "GERMANY_PREMIUM",
Rate: csvr.rates["R2"],
rates: csvr.rates["R2"],
},
}) {
t.Error("Error loading destination rate: ", drs)
@@ -311,7 +311,7 @@ func TestLoadDestinationRates(t *testing.T) {
&DestinationRate{
Tag: "RT_DEFAULT",
DestinationsTag: "ALL",
Rate: csvr.rates["R2"],
rates: csvr.rates["R2"],
},
}) {
t.Error("Error loading destination rate: ", drs)
@@ -321,12 +321,12 @@ func TestLoadDestinationRates(t *testing.T) {
&DestinationRate{
Tag: "RT_STD_WEEKEND",
DestinationsTag: "GERMANY",
Rate: csvr.rates["R2"],
rates: csvr.rates["R2"],
},
&DestinationRate{
Tag: "RT_STD_WEEKEND",
DestinationsTag: "GERMANY_O2",
Rate: csvr.rates["R3"],
rates: csvr.rates["R3"],
},
}) {
t.Error("Error loading destination rate: ", drs)
@@ -336,7 +336,7 @@ func TestLoadDestinationRates(t *testing.T) {
&DestinationRate{
Tag: "P1",
DestinationsTag: "NAT",
Rate: csvr.rates["R4"],
rates: csvr.rates["R4"],
},
}) {
t.Error("Error loading destination rate: ", drs)
@@ -346,7 +346,7 @@ func TestLoadDestinationRates(t *testing.T) {
&DestinationRate{
Tag: "P2",
DestinationsTag: "NAT",
Rate: csvr.rates["R5"],
rates: csvr.rates["R5"],
},
}) {
t.Error("Error loading destination rate: ", drs)
@@ -354,228 +354,171 @@ func TestLoadDestinationRates(t *testing.T) {
}
func TestLoadDestinationRateTimings(t *testing.T) {
if len(csvr.ratingPlans) != 4 {
t.Error("Failed to load rate timings: ", csvr.ratingPlans)
if len(csvr.destinationRateTimings) != 4 {
t.Error("Failed to load rate timings: ", csvr.destinationRateTimings)
}
rplan := csvr.ratingPlans["STANDARD"]
expected := &RatingPlan{
ActivationTime: time.Time{},
RateIntervals: RateIntervalList{
&RateInterval{
Years: Years{},
Months: Months{},
MonthDays: MonthDays{},
WeekDays: WeekDays{1, 2, 3, 4, 5},
StartTime: "00:00:00",
EndTime: "",
Weight: 10,
ConnectFee: 0,
Rates: RateGroups{
&Rate{
GroupIntervalStart: 0,
Value: 0.2,
RateIncrement: time.Second,
RateUnit: time.Minute,
},
&Rate{
GroupIntervalStart: 0,
Value: 0.1,
RateIncrement: time.Second,
RateUnit: time.Minute,
rplan := csvr.destinationRateTimings["STANDARD"]
expected := []*DestinationRateTiming{
&DestinationRateTiming{
destinationRates: []*DestinationRate{
&DestinationRate{
Tag: "RT_STANDARD",
DestinationsTag: "GERMANY",
rates: []*LoadRate{
&LoadRate{
Tag: "R1",
ConnectFee: 0,
Price: 0.2,
RateUnit: time.Minute,
RateIncrement: time.Second,
GroupIntervalStart: 0,
RoundingMethod: "*middle",
RoundingDecimals: 2,
Weight: 10,
},
},
},
&DestinationRate{
Tag: "RT_STANDARD",
DestinationsTag: "GERMANY_O2",
rates: []*LoadRate{
&LoadRate{
Tag: "R2",
ConnectFee: 0,
Price: 0.1,
RateUnit: time.Minute,
RateIncrement: time.Second,
GroupIntervalStart: 0,
RoundingMethod: "*middle",
RoundingDecimals: 2,
Weight: 10,
},
},
},
&DestinationRate{
Tag: "RT_STANDARD",
DestinationsTag: "GERMANY_PREMIUM",
rates: []*LoadRate{
&LoadRate{
Tag: "R2",
ConnectFee: 0,
Price: 0.1,
RateUnit: time.Minute,
RateIncrement: time.Second,
GroupIntervalStart: 0,
RoundingMethod: "*middle",
RoundingDecimals: 2,
Weight: 10,
},
},
},
RoundingMethod: utils.ROUNDING_MIDDLE,
RoundingDecimals: 2,
},
&RateInterval{
Years: Years{},
Months: Months{},
MonthDays: MonthDays{},
WeekDays: WeekDays{1, 2, 3, 4, 5},
StartTime: "18:00:00",
EndTime: "",
Weight: 10,
ConnectFee: 0,
Rates: RateGroups{
&Rate{
GroupIntervalStart: 0,
Value: 0.1,
RateIncrement: time.Second,
RateUnit: time.Minute,
},
&Rate{
GroupIntervalStart: 0,
Value: 0.05,
RateIncrement: time.Second,
RateUnit: time.Minute,
Weight: 10,
timing: &Timing{
Id: "WORKDAYS_00",
Years: Years{},
Months: Months{},
MonthDays: MonthDays{},
WeekDays: WeekDays{1, 2, 3, 4, 5},
StartTime: "00:00:00"},
},
&DestinationRateTiming{
destinationRates: []*DestinationRate{
&DestinationRate{
Tag: "RT_STD_WEEKEND",
DestinationsTag: "GERMANY",
rates: []*LoadRate{
&LoadRate{
Tag: "R2",
ConnectFee: 0,
Price: 0.1,
RateUnit: time.Minute,
RateIncrement: time.Second,
GroupIntervalStart: 0,
RoundingMethod: "*middle",
RoundingDecimals: 2,
Weight: 10,
},
},
},
&DestinationRate{
Tag: "RT_STD_WEEKEND",
DestinationsTag: "GERMANY_O2",
rates: []*LoadRate{
&LoadRate{
Tag: "R3",
ConnectFee: 0,
Price: 0.05,
RateUnit: time.Minute,
RateIncrement: time.Second,
GroupIntervalStart: 0,
RoundingMethod: "*middle",
RoundingDecimals: 2,
Weight: 10,
},
},
},
RoundingMethod: utils.ROUNDING_MIDDLE,
RoundingDecimals: 2,
},
&RateInterval{
Years: Years{},
Months: Months{},
MonthDays: MonthDays{},
WeekDays: WeekDays{6, 0},
StartTime: "00:00:00",
EndTime: "",
Weight: 10,
ConnectFee: 0,
Rates: RateGroups{
&Rate{
GroupIntervalStart: 0,
Value: 0.1,
RateIncrement: time.Second,
RateUnit: time.Minute,
},
&Rate{
GroupIntervalStart: 0,
Value: 0.05,
RateIncrement: time.Second,
RateUnit: time.Minute,
Weight: 10,
timing: &Timing{
Id: "WORKDAYS_18",
Years: Years{},
Months: Months{},
MonthDays: MonthDays{},
WeekDays: WeekDays{1, 2, 3, 4, 5},
StartTime: "18:00:00",
},
},
&DestinationRateTiming{
destinationRates: []*DestinationRate{
&DestinationRate{
Tag: "RT_STD_WEEKEND",
DestinationsTag: "GERMANY",
rates: []*LoadRate{
&LoadRate{
Tag: "R2",
ConnectFee: 0,
Price: 0.1,
RateUnit: time.Minute,
RateIncrement: time.Second,
GroupIntervalStart: 0,
RoundingMethod: "*middle",
RoundingDecimals: 2,
Weight: 10,
},
},
},
RoundingMethod: utils.ROUNDING_MIDDLE,
RoundingDecimals: 2,
&DestinationRate{
Tag: "RT_STD_WEEKEND",
DestinationsTag: "GERMANY_O2",
rates: []*LoadRate{
&LoadRate{
Tag: "R3",
ConnectFee: 0,
Price: 0.05,
RateUnit: time.Minute,
RateIncrement: time.Second,
GroupIntervalStart: 0,
RoundingMethod: "*middle",
RoundingDecimals: 2,
Weight: 10,
},
},
},
},
Weight: 10,
timing: &Timing{
Id: "WEEKENDS",
Years: Years{},
Months: Months{},
MonthDays: MonthDays{},
WeekDays: WeekDays{6, 0},
StartTime: "00:00:00",
},
},
}
if !reflect.DeepEqual(rplan, expected) {
t.Errorf("Error loading rating plan: %#v", csvr.ratingPlans["STANDARD"])
}
rplan = csvr.ratingPlans["PREMIUM"]
expected = &RatingPlan{
ActivationTime: time.Time{},
RateIntervals: RateIntervalList{
&RateInterval{
Years: Years{},
Months: Months{},
MonthDays: MonthDays{},
WeekDays: WeekDays{6, 0},
StartTime: "00:00:00",
EndTime: "",
Weight: 10,
ConnectFee: 0,
Rates: RateGroups{
&Rate{
GroupIntervalStart: 0,
Value: 0.1,
RateIncrement: time.Second,
RateUnit: time.Minute,
},
&Rate{
GroupIntervalStart: 0,
Value: 0.05,
RateIncrement: time.Second,
RateUnit: time.Minute,
},
},
RoundingMethod: utils.ROUNDING_MIDDLE,
RoundingDecimals: 2,
},
},
}
if !reflect.DeepEqual(rplan, expected) {
t.Errorf("Error loading rating plan: %#v", csvr.ratingPlans["PREMIUM"])
}
rplan = csvr.ratingPlans["DEFAULT"]
expected = &RatingPlan{
ActivationTime: time.Time{},
RateIntervals: RateIntervalList{
&RateInterval{
Years: Years{},
Months: Months{},
MonthDays: MonthDays{},
WeekDays: WeekDays{1, 2, 3, 4, 5},
StartTime: "00:00:00",
EndTime: "",
Weight: 10,
ConnectFee: 0,
Rates: RateGroups{
&Rate{
GroupIntervalStart: 0,
Value: 0.1,
RateIncrement: time.Second,
RateUnit: time.Minute,
},
},
RoundingMethod: utils.ROUNDING_MIDDLE,
RoundingDecimals: 2,
},
},
}
if !reflect.DeepEqual(rplan, expected) {
t.Errorf("Error loading rating plan: %#v", csvr.ratingPlans["DEFAULT"])
}
rplan = csvr.ratingPlans["EVENING"]
expected = &RatingPlan{
ActivationTime: time.Time{},
RateIntervals: RateIntervalList{
&RateInterval{
Years: Years{},
Months: Months{},
MonthDays: MonthDays{},
WeekDays: WeekDays{1, 2, 3, 4, 5},
StartTime: "00:00:00",
EndTime: "",
Weight: 10,
ConnectFee: 1,
Rates: RateGroups{
&Rate{
GroupIntervalStart: 0,
Value: 1,
RateIncrement: time.Second,
RateUnit: time.Second,
},
},
RoundingMethod: utils.ROUNDING_UP,
RoundingDecimals: 2,
},
&RateInterval{
Years: Years{},
Months: Months{},
MonthDays: MonthDays{},
WeekDays: WeekDays{1, 2, 3, 4, 5},
StartTime: "18:00:00",
EndTime: "",
Weight: 10,
ConnectFee: 0,
Rates: RateGroups{
&Rate{
GroupIntervalStart: 0,
Value: 0.5,
RateIncrement: time.Second,
RateUnit: time.Second,
},
},
RoundingMethod: utils.ROUNDING_DOWN,
RoundingDecimals: 2,
},
&RateInterval{
Years: Years{},
Months: Months{},
MonthDays: MonthDays{},
WeekDays: WeekDays{6, 0},
StartTime: "00:00:00",
EndTime: "",
Weight: 10,
ConnectFee: 0,
Rates: RateGroups{
&Rate{
GroupIntervalStart: 0,
Value: 0.5,
RateIncrement: time.Second,
RateUnit: time.Second,
},
},
RoundingMethod: utils.ROUNDING_DOWN,
RoundingDecimals: 2,
},
},
}
if !reflect.DeepEqual(rplan, expected) {
t.Errorf("Error loading rating plan: %#v", csvr.ratingPlans["EVENING"])
t.Errorf("Error loading destination rate timing: %#v", rplan)
}
}
@@ -586,22 +529,13 @@ func TestLoadRatingProfiles(t *testing.T) {
rp := csvr.ratingProfiles["*out:CUSTOMER_1:0:rif:from:tm"]
expected := &RatingProfile{}
if reflect.DeepEqual(rp, expected) {
t.Error("Error loading rating profile: ", csvr.ratingProfiles["*out:CUSTOMER_1:0:rif:from:tm"])
t.Errorf("Error loading rating profile: %#v", rp)
}
}
/*
CUSTOMER_1,0,*out,rif:from:tm,2012-01-01T00:00:00Z,PREMIUM,danb
CUSTOMER_1,0,*out,rif:from:tm,2012-02-28T00:00:00Z,STANDARD,danb
CUSTOMER_2,0,*out,danb:87.139.12.167,2012-01-01T00:00:00Z,STANDARD,danb
CUSTOMER_1,0,*out,danb,2012-01-01T00:00:00Z,PREMIUM,
vdf,0,*out,rif,2012-01-01T00:00:00Z,EVENING,
vdf,0,*out,rif,2012-02-28T00:00:00Z,EVENING,
vdf,0,*out,minu,2012-01-01T00:00:00Z,EVENING,
vdf,0,*out,*any,2012-02-28T00:00:00Z,EVENING,
vdf,0,*out,one,2012-02-28T00:00:00Z,STANDARD,
vdf,0,*out,inf,2012-02-28T00:00:00Z,STANDARD,inf
vdf,0,*out,fall,2012-02-28T00:00:00Z,PREMIUM,rif
*/
func TestLoadActions(t *testing.T) {

View File

@@ -35,7 +35,7 @@ type DbReader struct {
accountActions []*UserBalance
destinations []*Destination
timings map[string]*Timing
rates map[string]*LoadRate
rates map[string][]*LoadRate
destinationRates map[string][]*DestinationRate
activationPeriods map[string]*RatingPlan
ratingProfiles map[string]*RatingProfile
@@ -141,11 +141,11 @@ func (dbr *DbReader) LoadDestinationRates() (err error) {
}
for _, drs := range dbr.destinationRates {
for _, dr := range drs {
rate, exists := dbr.rates[dr.RateTag]
rates, exists := dbr.rates[dr.RateTag]
if !exists {
return errors.New(fmt.Sprintf("Could not find rate for tag %v", dr.RateTag))
}
dr.Rate = rate
dr.rates = rates
}
}
return nil
@@ -157,9 +157,9 @@ func (dbr *DbReader) LoadDestinationRateTimings() error {
return err
}
for _, rt := range rts {
t, exists := dbr.timings[rt.TimingsTag]
t, exists := dbr.timings[rt.TimingTag]
if !exists {
return errors.New(fmt.Sprintf("Could not get timing for tag %v", rt.TimingsTag))
return errors.New(fmt.Sprintf("Could not get timing for tag %v", rt.TimingTag))
}
rt.timing = t
drs, exists := dbr.destinationRates[rt.DestinationRatesTag]
@@ -223,12 +223,12 @@ func (dbr *DbReader) LoadRatingProfileByTag(tag string) error {
}
for _, destrateTiming := range drtm {
Logger.Debug(fmt.Sprintf("Destination rate timing: %v", rpm))
tm, err := dbr.storDb.GetTpTimings(dbr.tpid, destrateTiming.TimingsTag)
tm, err := dbr.storDb.GetTpTimings(dbr.tpid, destrateTiming.TimingTag)
Logger.Debug(fmt.Sprintf("Timing: %v", rpm))
if err != nil || len(tm) == 0 {
return fmt.Errorf("No Timings profile with id %s: %v", destrateTiming.TimingsTag, err)
return fmt.Errorf("No Timings profile with id %s: %v", destrateTiming.TimingTag, err)
}
destrateTiming.timing = tm[destrateTiming.TimingsTag]
destrateTiming.timing = tm[destrateTiming.TimingTag]
drm, err := dbr.storDb.GetTpDestinationRates(dbr.tpid, destrateTiming.DestinationRatesTag)
if err != nil || len(drm) == 0 {
return fmt.Errorf("No Timings profile with id %s: %v", destrateTiming.DestinationRatesTag, err)
@@ -240,7 +240,7 @@ func (dbr *DbReader) LoadRatingProfileByTag(tag string) error {
return fmt.Errorf("No Rates profile with id %s: %v", drate.RateTag, err)
}
Logger.Debug(fmt.Sprintf("Rate: %v", rpm))
drate.Rate = rt[drate.RateTag]
drate.rates = rt[drate.RateTag]
if _, exists := activationPeriods[destrateTiming.Tag]; !exists {
activationPeriods[destrateTiming.Tag] = &RatingPlan{}
}

View File

@@ -24,6 +24,7 @@ import (
"fmt"
"github.com/cgrates/cgrates/utils"
"log"
"math"
"os"
"path"
"regexp"
@@ -106,11 +107,24 @@ func NewLoadRate(tag, connectFee, price, ratedUnits, rateIncrements, groupInterv
return
}
func (present *LoadRate) ValidNextGroup(next *LoadRate) error {
if next.GroupIntervalStart <= present.GroupIntervalStart {
return errors.New(fmt.Sprintf("Next rate group interval start must be heigher than the last one: %#v", next))
}
if math.Mod(next.GroupIntervalStart.Seconds(), present.RateIncrement.Seconds()) != 0 {
return errors.New(fmt.Sprintf("GroupIntervalStart of %#v must be a multiple of RateIncrement of %#v", next, present))
}
if present.RoundingMethod != next.RoundingMethod || present.RoundingDecimals != next.RoundingDecimals {
return errors.New(fmt.Sprintf("Rounding stuff must be equal for sam rate tag: %#v, %#v", present, next))
}
return nil
}
type DestinationRate struct {
Tag string
DestinationsTag string
RateTag string
Rate *LoadRate
RateTag string // intermediary used when loading from db
rates []*LoadRate
}
type Timing struct {
@@ -135,43 +149,46 @@ func NewTiming(timingInfo ...string) (rt *Timing) {
type DestinationRateTiming struct {
Tag string
DestinationRatesTag string
DestinationRatesTag string // intermediary used when loading from db
destinationRates []*DestinationRate
Weight float64
TimingsTag string // intermediary used when loading from db
TimingTag string // intermediary used when loading from db
timing *Timing
}
func NewDestinationRateTiming(destinationRatesTag string, timing *Timing, weight string) (rt *DestinationRateTiming) {
func NewDestinationRateTiming(destinationRates []*DestinationRate, timing *Timing, weight string) (drt *DestinationRateTiming) {
w, err := strconv.ParseFloat(weight, 64)
if err != nil {
log.Printf("Error parsing weight unit from: %v", weight)
return
}
rt = &DestinationRateTiming{
DestinationRatesTag: destinationRatesTag,
Weight: w,
timing: timing,
drt = &DestinationRateTiming{
destinationRates: destinationRates,
timing: timing,
Weight: w,
}
return
}
func (rt *DestinationRateTiming) GetRateInterval(dr *DestinationRate) (i *RateInterval) {
func (drt *DestinationRateTiming) GetRateInterval(dr *DestinationRate) (i *RateInterval) {
i = &RateInterval{
Years: rt.timing.Years,
Months: rt.timing.Months,
MonthDays: rt.timing.MonthDays,
WeekDays: rt.timing.WeekDays,
StartTime: rt.timing.StartTime,
Weight: rt.Weight,
ConnectFee: dr.Rate.ConnectFee,
RoundingMethod: dr.Rate.RoundingMethod,
RoundingDecimals: dr.Rate.RoundingDecimals,
Rates: RateGroups{&Rate{
GroupIntervalStart: dr.Rate.GroupIntervalStart,
Value: dr.Rate.Price,
RateIncrement: dr.Rate.RateIncrement,
RateUnit: dr.Rate.RateUnit,
}},
Years: drt.timing.Years,
Months: drt.timing.Months,
MonthDays: drt.timing.MonthDays,
WeekDays: drt.timing.WeekDays,
StartTime: drt.timing.StartTime,
Weight: drt.Weight,
ConnectFee: dr.rates[0].ConnectFee,
RoundingMethod: dr.rates[0].RoundingMethod,
RoundingDecimals: dr.rates[0].RoundingDecimals,
}
for _, rl := range dr.rates {
i.Rates = append(i.Rates, &Rate{
GroupIntervalStart: rl.GroupIntervalStart,
Value: rl.Price,
RateIncrement: rl.RateIncrement,
RateUnit: rl.RateUnit,
})
}
return
}

View File

@@ -40,18 +40,17 @@ type xCachedRatingPlans struct {
/*
Adds one ore more intervals to the internal interval list only if it is not allready in the list.
*/
func (ap *RatingPlan) AddRateInterval(is ...*RateInterval) {
for _, i := range is {
func (ap *RatingPlan) AddRateInterval(ris ...*RateInterval) {
for _, ri := range ris {
found := false
for _, ei := range ap.RateIntervals {
if i.Equal(ei) {
(&ei.Rates).AddRate(i.Rates...)
for _, eri := range ap.RateIntervals {
if ri.Equal(eri) {
found = true
break
}
}
if !found {
ap.RateIntervals = append(ap.RateIntervals, i)
ap.RateIntervals = append(ap.RateIntervals, ri)
}
}
}

View File

@@ -79,8 +79,8 @@ func TestFallbackDirect(t *testing.T) {
func TestFallbackMultiple(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "fall", Destination: "0723045"}
cd.LoadRatingPlans()
if len(cd.RatingPlans) != 1 {
t.Error("Error restoring activation periods: ", len(cd.RatingPlans))
if len(cd.RatingPlans) != 2 {
t.Errorf("Error restoring rating plans: %#v", cd.RatingPlans)
}
}
@@ -161,8 +161,8 @@ func TestApAddRateIntervalGroups(t *testing.T) {
if len(ap.RateIntervals) != 1 {
t.Error("Wronfully appended interval ;)")
}
if len(ap.RateIntervals[0].Rates) != 2 {
t.Error("Group prices not formed: ", ap.RateIntervals[0].Rates)
if len(ap.RateIntervals[0].Rates) != 1 {
t.Errorf("Group prices not formed: %#v", ap.RateIntervals[0].Rates[0])
}
}

View File

@@ -31,20 +31,21 @@ type RatingProfile struct {
}
// Adds an activation period that applyes to current rating profile if not already present.
func (rp *RatingProfile) AddRatingPlanIfNotPresent(destInfo string, aps ...*RatingPlan) {
func (rp *RatingProfile) AddRatingPlanIfNotPresent(destInfo string, plans ...*RatingPlan) {
if rp.DestinationMap == nil {
rp.DestinationMap = make(map[string][]*RatingPlan, 1)
}
for _, ap := range aps {
for _, plan := range plans {
found := false
for _, eap := range rp.DestinationMap[destInfo] {
if ap.Equal(eap) {
for _, existingPlan := range rp.DestinationMap[destInfo] {
if plan.Equal(existingPlan) {
existingPlan.AddRateInterval(plan.RateIntervals...)
found = true
break
}
}
if !found {
rp.DestinationMap[destInfo] = append(rp.DestinationMap[destInfo], ap)
rp.DestinationMap[destInfo] = append(rp.DestinationMap[destInfo], plan)
}
}
}

View File

@@ -143,7 +143,7 @@ type LoadStorage interface {
// loader functions
GetTpDestinations(string, string) ([]*Destination, error)
GetTpTimings(string, string) (map[string]*Timing, error)
GetTpRates(string, string) (map[string]*LoadRate, error)
GetTpRates(string, string) (map[string][]*LoadRate, error)
GetTpDestinationRates(string, string) (map[string][]*DestinationRate, error)
GetTpDestinationRateTimings(string, string) ([]*DestinationRateTiming, error)
GetTpRatingProfiles(string, string) (map[string]*RatingProfile, error)

View File

@@ -363,7 +363,7 @@ func (self *SQLStorage) SetTPDestRateTimings(tpid string, drts map[string][]*Des
qry += ","
}
qry += fmt.Sprintf("('%s','%s','%s','%s',%f)",
tpid, drtId, drt.DestinationRatesTag, drt.TimingsTag, drt.Weight)
tpid, drtId, drt.DestinationRatesTag, drt.TimingTag, drt.Weight)
i++
}
}
@@ -921,8 +921,8 @@ func (self *SQLStorage) GetTpDestinations(tpid, tag string) ([]*Destination, err
return dests, nil
}
func (self *SQLStorage) GetTpRates(tpid, tag string) (map[string]*LoadRate, error) {
rts := make(map[string]*LoadRate)
func (self *SQLStorage) GetTpRates(tpid, tag string) (map[string][]*LoadRate, error) {
rts := make(map[string][]*LoadRate)
q := fmt.Sprintf("SELECT tag, connect_fee, rate, rate_unit, rate_increment, group_interval_start, rounding_method, rounding_decimals, weight FROM %s WHERE tpid='%s' ", utils.TBL_TP_RATES, tpid)
if tag != "" {
q += fmt.Sprintf(" AND tag='%s'", tag)
@@ -951,7 +951,14 @@ func (self *SQLStorage) GetTpRates(tpid, tag string) (map[string]*LoadRate, erro
RoundingDecimals: roundingDecimals,
Weight: weight,
}
rts[tag] = r
// same tag only to create rate groups
existingRates, exists := rts[tag]
if exists {
if err := existingRates[len(existingRates)-1].ValidNextGroup(r); err != nil {
return nil, err
}
}
rts[tag] = append(rts[tag], r)
}
return rts, nil
}
@@ -1027,7 +1034,7 @@ func (self *SQLStorage) GetTpDestinationRateTimings(tpid, tag string) ([]*Destin
Tag: tag,
DestinationRatesTag: destination_rates_tag,
Weight: weight,
TimingsTag: timings_tag,
TimingTag: timings_tag,
}
rts = append(rts, rt)
}

View File

@@ -208,7 +208,7 @@ func (self *TPCSVImporter) importDestRateTimings(fn string) error {
drt := &DestinationRateTiming{Tag: record[0],
DestinationRatesTag: record[1],
Weight: weight,
TimingsTag: record[2],
TimingTag: record[2],
}
if err := self.StorDb.SetTPDestRateTimings(self.TPid, map[string][]*DestinationRateTiming{drt.Tag: []*DestinationRateTiming{drt}}); err != nil {
if self.Verbose {

View File

@@ -287,6 +287,7 @@ func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) error {
if paid {
continue
}
// TODO: Split if some increments were processed by minutes
// debit monetary
for _, b := range usefulMoneyBalances {
if b.Value == 0 {