From 1a1403ace1c628ce638d8f5ee8d49edf76dfefee Mon Sep 17 00:00:00 2001 From: DanB Date: Mon, 29 Jul 2013 12:40:23 +0200 Subject: [PATCH] Few more methods on TPCSVImporter, API TPRates modifications to include GroupInterval --- apier/tprates.go | 8 ++- .../mysql/create_tariffplan_tables.sql | 3 +- docs/api_tprates.rst | 46 +++++++------ engine/action.go | 2 +- engine/storage_interface.go | 2 +- engine/storage_map.go | 2 +- engine/storage_mongo.go | 2 +- engine/storage_redis.go | 2 +- engine/storage_sql.go | 31 ++++++--- engine/tpimporter_csv.go | 64 +++++++++++++++++-- utils/apitpdata.go | 1 + 11 files changed, 120 insertions(+), 43 deletions(-) diff --git a/apier/tprates.go b/apier/tprates.go index 56b9c8595..41790aa6b 100644 --- a/apier/tprates.go +++ b/apier/tprates.go @@ -24,6 +24,7 @@ import ( "errors" "fmt" "github.com/cgrates/cgrates/utils" + "github.com/cgrates/cgrates/engine" ) // Creates a new rate within a tariff plan @@ -36,7 +37,12 @@ func (self *Apier) SetTPRate(attrs utils.TPRate, reply *string) error { } else if exists { return errors.New(utils.ERR_DUPLICATE) } - if err := self.StorDb.SetTPRate(&attrs); err != nil { + rts := make([]*engine.Rate, len(attrs.RateSlots)) + for idx,rtSlot := range attrs.RateSlots { + rts[idx] = &engine.Rate{attrs.RateId, rtSlot.ConnectFee, rtSlot.Rate, float64(rtSlot.RatedUnits), + float64(rtSlot.RateIncrements), float64(rtSlot.GroupInterval), rtSlot.RoundingMethod, rtSlot.RoundingDecimals, rtSlot.Weight} + } + if err := self.StorDb.SetTPRates( attrs.TPid, map[string][]*engine.Rate{ attrs.RateId: rts } ); err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } *reply = "OK" diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index ae8fd575a..bead88034 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -43,11 +43,12 @@ CREATE TABLE `tp_rates` ( `rate` decimal(5,4) NOT NULL, `rated_units` int(11) NOT NULL, `rate_increments` int(11) NOT NULL, + `group_interval` int(11) NOT NULL, `rounding_method` varchar(255) NOT NULL, `rounding_decimals` tinyint(4) NOT NULL, `weight` decimal(5,2) NOT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `tpid_tag_rate_weight` (`tpid`,`tag`,`weight`), + UNIQUE KEY `unique_tprate` (`tpid`,`tag`,`group_interval`), KEY `tpid` (`tpid`), KEY `tpid_tag` (`tpid`,`tag`) ); diff --git a/docs/api_tprates.rst b/docs/api_tprates.rst index ab957e9e1..98155f34e 100644 --- a/docs/api_tprates.rst +++ b/docs/api_tprates.rst @@ -15,13 +15,14 @@ Creates a new rate within a tariff plan. } type RateSlot struct { - ConnectFee float64 // ConnectFee applied once the call is answered - Rate float64 // Rate applied - RatedUnits int // Number of billing units this rate applies to - RateIncrements int // This rate will apply in increments of duration - RoundingMethod string // Use this method to round the cost - RoundingDecimals int // Round the cost number of decimals - Weight float64 // Rate's priority when dealing with grouped rates + ConnectFee float64 // ConnectFee applied once the call is answered + Rate float64 // Rate applied + RatedUnits int // Number of billing units this rate applies to + RateIncrements int // This rate will apply in increments of duration + GroupInterval int // Group position + RoundingMethod string // Use this method to round the cost + RoundingDecimals int // Round the cost number of decimals + Weight float64 // Rate's priority when dealing with grouped rates } Mandatory parameters: ``[]string{"TPid", "RateId", "ConnectFee", "RateSlots"}`` @@ -39,9 +40,10 @@ Creates a new rate within a tariff plan. { "ConnectFee": 0.2, "Rate": 2, - "RateIncrements": 1, + "RateIncrements": 60, "RatedUnits": 1, - "RoundingDecimals": 2, + "RoundingDecimals": 2, + "GroupInterval": 0, "RoundingMethod": "*up", "Weight": 10.0 }, @@ -50,7 +52,8 @@ Creates a new rate within a tariff plan. "Rate": 2.1, "RateIncrements": 1, "RatedUnits": 1, - "RoundingDecimals": 2, + "RoundingDecimals": 2, + "GroupInterval": 60, "RoundingMethod": "*up", "Weight": 20.0 } @@ -131,13 +134,14 @@ Queries specific rate on tariff plan. } type RateSlot struct { - ConnectFee float64 // ConnectFee applied once the call is answered - Rate float64 // Rate applied - RatedUnits int // Number of billing units this rate applies to - RateIncrements int // This rate will apply in increments of duration - RoundingMethod string // Use this method to round the cost - RoundingDecimals int // Round the cost number of decimals - Weight float64 // Rate's priority when dealing with grouped rates + ConnectFee float64 // ConnectFee applied once the call is answered + Rate float64 // Rate applied + RatedUnits int // Number of billing units this rate applies to + RateIncrements int // This rate will apply in increments of duration + GroupInterval int // Group position + RoundingMethod string // Use this method to round the cost + RoundingDecimals int // Round the cost number of decimals + Weight float64 // Rate's priority when dealing with grouped rates } *JSON sample*: @@ -152,9 +156,10 @@ Queries specific rate on tariff plan. { "ConnectFee": 0.2, "Rate": 2, - "RateIncrements": 1, + "RateIncrements": 60, "RatedUnits": 1, - "RoundingDecimals": 2, + "RoundingDecimals": 2, + "GroupInterval": 0, "RoundingMethod": "*up", "Weight": 10 }, @@ -163,7 +168,8 @@ Queries specific rate on tariff plan. "Rate": 2.1, "RateIncrements": 1, "RatedUnits": 1, - "RoundingDecimals": 2, + "RoundingDecimals": 2, + "GroupInterval": 60, "RoundingMethod": "*up", "Weight": 20 } diff --git a/engine/action.go b/engine/action.go index 7dd7c3f80..227277e56 100644 --- a/engine/action.go +++ b/engine/action.go @@ -116,7 +116,7 @@ func topupResetAction(ub *UserBalance, a *Action) (err error) { if a.BalanceId == MINUTES { ub.MinuteBuckets = make([]*MinuteBucket, 0) } else { - ub.BalanceMap[a.BalanceId+a.Direction] = BalanceChain{&Balance{Value: 0}} + ub.BalanceMap[a.BalanceId+a.Direction] = BalanceChain{&Balance{Value: 0}} // ToDo: can ub be empty here? } genericMakeNegative(a) genericDebit(ub, a) diff --git a/engine/storage_interface.go b/engine/storage_interface.go index cd9b4f963..d3f8374c0 100644 --- a/engine/storage_interface.go +++ b/engine/storage_interface.go @@ -68,7 +68,7 @@ type DataStorage interface { GetTPDestination(string, string) (*Destination, error) GetTPDestinationIds(string) ([]string, error) ExistsTPRate(string, string) (bool, error) - SetTPRate(*utils.TPRate) error + SetTPRates(string, map[string][]*Rate) error GetTPRate(string, string) (*utils.TPRate, error) GetTPRateIds(string) ([]string, error) ExistsTPDestinationRate(string, string) (bool, error) diff --git a/engine/storage_map.go b/engine/storage_map.go index f6aaeb4a4..78cdf78e6 100644 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -113,7 +113,7 @@ func (ms *MapStorage) ExistsTPRate(tpid, rtId string) (bool, error) { return false, errors.New(utils.ERR_NOT_IMPLEMENTED) } -func (ms *MapStorage) SetTPRate(rt *utils.TPRate) error { +func (ms *MapStorage) SetTPRates(tpid string, rts map[string][]*Rate) error { return errors.New(utils.ERR_NOT_IMPLEMENTED) } diff --git a/engine/storage_mongo.go b/engine/storage_mongo.go index 2ecff5575..c6bf13c8b 100644 --- a/engine/storage_mongo.go +++ b/engine/storage_mongo.go @@ -188,7 +188,7 @@ func (ms *MongoStorage) ExistsTPRate(tpid, rtId string) (bool, error) { return false, errors.New(utils.ERR_NOT_IMPLEMENTED) } -func (ms *MongoStorage) SetTPRate(rt *utils.TPRate) error { +func (ms *MongoStorage) SetTPRates(tpid string, rts map[string][]*Rate) error { return errors.New(utils.ERR_NOT_IMPLEMENTED) } diff --git a/engine/storage_redis.go b/engine/storage_redis.go index cc539f419..9725764a5 100644 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -143,7 +143,7 @@ func (rs *RedisStorage) ExistsTPRate(tpid, rtId string) (bool, error) { return false, errors.New(utils.ERR_NOT_IMPLEMENTED) } -func (rs *RedisStorage) SetTPRate(rt *utils.TPRate) error { +func (rs *RedisStorage) SetTPRates(tpid string, rts map[string][]*Rate) error { return errors.New(utils.ERR_NOT_IMPLEMENTED) } diff --git a/engine/storage_sql.go b/engine/storage_sql.go index 3242b7c35..20c7458f4 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -210,19 +210,29 @@ func (self *SQLStorage) ExistsTPRate(tpid, rtId string) (bool, error) { return exists, nil } -func (self *SQLStorage) SetTPRate(rt *utils.TPRate) error { - for _, rtSlot := range rt.RateSlots { - if _, err := self.Db.Exec(fmt.Sprintf("INSERT INTO %s (tpid, tag, connect_fee, rate, rated_units, rate_increments, rounding_method, rounding_decimals, weight) VALUES ('%s', '%s', %f, %f, %d, %d,'%s', %d, %f)", - utils.TBL_TP_RATES, rt.TPid, rt.RateId, rtSlot.ConnectFee, rtSlot.Rate, rtSlot.RatedUnits, rtSlot.RateIncrements, - rtSlot.RoundingMethod, rtSlot.RoundingDecimals, rtSlot.Weight)); err != nil { - return err +func (self *SQLStorage) SetTPRates(tpid string, rts map[string][]*Rate) error { + if len(rts) == 0 { + return nil //Nothing to set + } + qry := fmt.Sprintf("INSERT INTO %s (tpid, tag, connect_fee, rate, rated_units, rate_increments, group_interval, rounding_method, rounding_decimals, weight) VALUES ", utils.TBL_TP_RATES) + for rtId, rtRows := range rts { + for idx, rt := range rtRows { + if idx != 0 { //Consecutive values after the first will be prefixed with "," as separator + qry += "," + } + qry += fmt.Sprintf("('%s', '%s', %f, %f, %d, %d,%d,'%s', %d, %f)", + tpid, rtId, rt.ConnectFee, rt.Price, int(rt.PricedUnits), int(rt.RateIncrements), int(rt.GroupInterval), + rt.RoundingMethod, rt.RoundingDecimals, rt.Weight) } } + if _, err := self.Db.Exec(qry); err != nil { + return err + } return nil } func (self *SQLStorage) GetTPRate(tpid, rtId string) (*utils.TPRate, error) { - rows, err := self.Db.Query(fmt.Sprintf("SELECT connect_fee, rate, rated_units, rate_increments, rounding_method, rounding_decimals, weight FROM %s WHERE tpid='%s' AND tag='%s'", utils.TBL_TP_RATES, tpid, rtId)) + rows, err := self.Db.Query(fmt.Sprintf("SELECT connect_fee, rate, rated_units, rate_increments, group_interval, rounding_method, rounding_decimals, weight FROM %s WHERE tpid='%s' AND tag='%s'", utils.TBL_TP_RATES, tpid, rtId)) if err != nil { return nil, err } @@ -232,13 +242,14 @@ func (self *SQLStorage) GetTPRate(tpid, rtId string) (*utils.TPRate, error) { for rows.Next() { i++ //Keep here a reference so we know we got at least one prefix var connectFee, rate, weight float64 - var ratedUnits, rateIncrements, roundingDecimals int + var ratedUnits, rateIncrements, roundingDecimals, groupInterval int var roundingMethod string - err = rows.Scan(&connectFee, &rate, &ratedUnits, &rateIncrements, &roundingMethod, &roundingDecimals, &weight) + err = rows.Scan(&connectFee, &rate, &ratedUnits, &rateIncrements, &groupInterval, &roundingMethod, &roundingDecimals, &weight) if err != nil { return nil, err } - rt.RateSlots = append(rt.RateSlots, utils.RateSlot{connectFee, rate, ratedUnits, rateIncrements, roundingMethod, roundingDecimals, weight}) + rt.RateSlots = append(rt.RateSlots, utils.RateSlot{connectFee, rate, ratedUnits, rateIncrements, groupInterval, + roundingMethod, roundingDecimals, weight}) } if i == 0 { return nil, nil diff --git a/engine/tpimporter_csv.go b/engine/tpimporter_csv.go index 877f4467b..b268ba347 100644 --- a/engine/tpimporter_csv.go +++ b/engine/tpimporter_csv.go @@ -35,6 +35,8 @@ type TPCSVImporter struct { Verbose bool // If true will print a detailed information instead of silently discarding it } +// Maps csv file to handler which should process it. Defined like this since tests on 1.0.3 were failing on Travis. +// Change it to func(string) error as soon as Travis updates. var fileHandlers = map[string]func(*TPCSVImporter,string) error{ utils.TIMINGS_CSV: (*TPCSVImporter).importTimings, utils.DESTINATIONS_CSV: (*TPCSVImporter).importDestinations, @@ -49,10 +51,6 @@ var fileHandlers = map[string]func(*TPCSVImporter,string) error{ } func (self *TPCSVImporter) Run() error { - - // Maps csv file to handler which should process it - - files, _ := ioutil.ReadDir(self.DirPath) for _, f := range files { fHandler,hasName := fileHandlers[f.Name()] @@ -66,6 +64,9 @@ func (self *TPCSVImporter) Run() error { // Handler importing timings from file, saved row by row to storDb func (self *TPCSVImporter) importTimings(fn string) error { + if self.Verbose { + log.Printf("Processing file: <%s> ", fn) + } fParser, err := NewTPCSVFileParser( self.DirPath, fn ) if err!=nil { return err @@ -90,11 +91,62 @@ func (self *TPCSVImporter) importTimings(fn string) error { return nil } -func (self *TPCSVImporter) importDestinations(fPath string) error { +func (self *TPCSVImporter) importDestinations(fn string) error { + if self.Verbose { + log.Printf("Processing file: <%s> ", fn) + } + fParser, err := NewTPCSVFileParser( self.DirPath, fn ) + if err!=nil { + return err + } + lineNr := 0 + for { + lineNr++ + record, err := fParser.ParseNextLine() + if err == io.EOF { // Reached end of file + break + } else if err != nil { + if self.Verbose { + log.Printf("Ignoring line %d, warning: <%s> ", lineNr, err.Error()) + } + continue + } + dst := &Destination{record[0], []string{record[1]}} + if err := self.StorDb.SetTPDestination(self.TPid, dst); err != nil { + log.Printf("Ignoring line %d, storDb operational error: <%s> ", lineNr, err.Error()) + } + } return nil } -func (self *TPCSVImporter) importRates(fPath string) error { +func (self *TPCSVImporter) importRates(fn string) error { + if self.Verbose { + log.Printf("Processing file: <%s> ", fn) + } + fParser, err := NewTPCSVFileParser( self.DirPath, fn ) + if err!=nil { + return err + } + lineNr := 0 + for { + lineNr++ + record, err := fParser.ParseNextLine() + if err == io.EOF { // Reached end of file + break + } else if err != nil { + if self.Verbose { + log.Printf("Ignoring line %d, warning: <%s> ", lineNr, err.Error()) + } + continue + } + rt, err := NewRate(record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7], record[8]) + if err != nil { + return err + } + if err := self.StorDb.SetTPRates( self.TPid, map[string][]*Rate{ record[0]: []*Rate{rt} } ); err != nil { + log.Printf("Ignoring line %d, storDb operational error: <%s> ", lineNr, err.Error()) + } + } return nil } diff --git a/utils/apitpdata.go b/utils/apitpdata.go index c389db1cf..89d396b33 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -31,6 +31,7 @@ type RateSlot struct { Rate float64 // Rate applied RatedUnits int // Number of billing units this rate applies to RateIncrements int // This rate will apply in increments of duration + GroupInterval int // Group position RoundingMethod string // Use this method to round the cost RoundingDecimals int // Round the cost number of decimals Weight float64 // Rate's priority when dealing with grouped rates