Adding costDigitsShift formatting

This commit is contained in:
DanB
2014-04-01 16:08:34 +02:00
parent eb333fcfc0
commit cd29a3360b
12 changed files with 69 additions and 44 deletions

View File

@@ -53,7 +53,11 @@ func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.E
if len(exportId) == 0 {
exportId = strconv.FormatInt(time.Now().Unix(), 10)
}
roundDecimals := attr.RoundingDecimals
costShiftDigits := attr.CostShiftDigits
if costShiftDigits != 0 {
costShiftDigits = self.Config.CdreCostShiftDigits
}
roundDecimals := attr.RoundDecimals
if roundDecimals == 0 {
roundDecimals = self.Config.RoundingDecimals
}
@@ -99,7 +103,7 @@ func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.E
return err
}
defer fileOut.Close()
csvWriter := cdre.NewCsvCdrWriter(fileOut, roundDecimals, maskDestId, maskLen, exportedFields)
csvWriter := cdre.NewCsvCdrWriter(fileOut, costShiftDigits, roundDecimals, maskDestId, maskLen, exportedFields)
exportedIds := make([]string, 0)
unexportedIds := make(map[string]string)
for _, cdr := range cdrs {
@@ -132,7 +136,7 @@ func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.E
return err
}
defer fileOut.Close()
fww, _ := cdre.NewFWCdrWriter(self.LogDb, fileOut, exportTemplate, exportId, roundDecimals, maskDestId, maskLen)
fww, _ := cdre.NewFWCdrWriter(self.LogDb, fileOut, exportTemplate, exportId, costShiftDigits, roundDecimals, maskDestId, maskLen)
exportedIds := make([]string, 0)
unexportedIds := make(map[string]string)
for _, cdr := range cdrs {

View File

@@ -26,15 +26,15 @@ import (
)
type CsvCdrWriter struct {
writer *csv.Writer
roundDecimals int // Round floats like Cost using this number of decimals
maskDestId string
maskLen int
exportedFields []*utils.RSRField // The fields exported, order important
writer *csv.Writer
costShiftDigits, roundDecimals int // Round floats like Cost using this number of decimals
maskDestId string
maskLen int
exportedFields []*utils.RSRField // The fields exported, order important
}
func NewCsvCdrWriter(writer io.Writer, roundDecimals int, maskDestId string, maskLen int, exportedFields []*utils.RSRField) *CsvCdrWriter {
return &CsvCdrWriter{csv.NewWriter(writer), roundDecimals, maskDestId, maskLen, exportedFields}
func NewCsvCdrWriter(writer io.Writer, costShiftDigits, roundDecimals int, maskDestId string, maskLen int, exportedFields []*utils.RSRField) *CsvCdrWriter {
return &CsvCdrWriter{csv.NewWriter(writer), costShiftDigits, roundDecimals, maskDestId, maskLen, exportedFields}
}
func (csvwr *CsvCdrWriter) WriteCdr(cdr *utils.StoredCdr) error {
@@ -42,7 +42,7 @@ func (csvwr *CsvCdrWriter) WriteCdr(cdr *utils.StoredCdr) error {
for idx, fld := range csvwr.exportedFields {
var fldVal string
if fld.Id == utils.COST {
fldVal = cdr.FormatCost(csvwr.roundDecimals)
fldVal = cdr.FormatCost(csvwr.costShiftDigits, csvwr.roundDecimals)
} else if fld.Id == utils.DESTINATION {
fldVal = cdr.ExportFieldValue(utils.DESTINATION)
if len(csvwr.maskDestId) != 0 && csvwr.maskLen > 0 && engine.CachedDestHasPrefix(csvwr.maskDestId, fldVal) {

View File

@@ -31,7 +31,7 @@ func TestCsvCdrWriter(t *testing.T) {
writer := &bytes.Buffer{}
cfg, _ := config.NewDefaultCGRConfig()
exportedFields := append(cfg.CdreExportedFields, &utils.RSRField{Id: "extra3"}, &utils.RSRField{Id: "dummy_extra"}, &utils.RSRField{Id: "extra1"})
csvCdrWriter := NewCsvCdrWriter(writer, 4, "", -1, exportedFields)
csvCdrWriter := NewCsvCdrWriter(writer, 0, 4, "", -1, exportedFields)
ratedCdr := &utils.StoredCdr{CgrId: utils.FSCgrId("dsafdsaf"), AccId: "dsafdsaf", CdrHost: "192.168.1.1", ReqType: "rated", Direction: "*out", Tenant: "cgrates.org",
TOR: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(),
Duration: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID,

View File

@@ -47,38 +47,40 @@ const (
META_DURCDRS = "cdrs_duration"
META_COSTCDRS = "cdrs_cost"
META_MASKDESTINATION = "mask_destination"
META_FORMATCOST = "format_cost"
)
var err error
func NewFWCdrWriter(logDb engine.LogStorage, outFile *os.File, exportTpl *config.CgrXmlCdreFwCfg, exportId string,
roundDecimals int, maskDestId string, maskLen int) (*FixedWidthCdrWriter, error) {
costShiftDigits, roundDecimals int, maskDestId string, maskLen int) (*FixedWidthCdrWriter, error) {
return &FixedWidthCdrWriter{
logDb: logDb,
writer: outFile,
exportTemplate: exportTpl,
exportId: exportId,
roundDecimals: roundDecimals,
maskDestId: maskDestId,
maskLen: maskLen,
header: &bytes.Buffer{},
content: &bytes.Buffer{},
trailer: &bytes.Buffer{}}, nil
logDb: logDb,
writer: outFile,
exportTemplate: exportTpl,
exportId: exportId,
costShiftDigits: costShiftDigits,
roundDecimals: roundDecimals,
maskDestId: maskDestId,
maskLen: maskLen,
header: &bytes.Buffer{},
content: &bytes.Buffer{},
trailer: &bytes.Buffer{}}, nil
}
type FixedWidthCdrWriter struct {
logDb engine.LogStorage // Used to extract cost_details if these are requested
writer io.Writer
exportTemplate *config.CgrXmlCdreFwCfg
exportId string // Unique identifier or this export
roundDecimals int
maskDestId string
maskLen int
header, content, trailer *bytes.Buffer
firstCdrATime, lastCdrATime time.Time
numberOfRecords int
totalDuration time.Duration
totalCost float64
logDb engine.LogStorage // Used to extract cost_details if these are requested
writer io.Writer
exportTemplate *config.CgrXmlCdreFwCfg
exportId string // Unique identifier or this export
costShiftDigits, roundDecimals int
maskDestId string
maskLen int
header, content, trailer *bytes.Buffer
firstCdrATime, lastCdrATime time.Time
numberOfRecords int
totalDuration time.Duration
totalCost float64
}
// Return Json marshaled callCost attached to
@@ -116,7 +118,7 @@ func (fwv *FixedWidthCdrWriter) cdrFieldValue(cdr *utils.StoredCdr, cfgHdr, layo
return "", err
}
case utils.COST:
cdrVal = cdr.FormatCost(fwv.roundDecimals)
cdrVal = cdr.FormatCost(fwv.costShiftDigits, fwv.roundDecimals)
case utils.SETUP_TIME:
cdrVal = cdr.SetupTime.Format(layout)
case utils.ANSWER_TIME: // Format time based on layout

View File

@@ -93,6 +93,7 @@ type CGRConfig struct {
CdreCdrFormat string // Format of the exported CDRs. <csv>
CdreMaskDestId string // Id of the destination list to be masked in CDRs
CdreMaskLength int // Number of digits to mask in the destination suffix if destination is in the MaskDestinationdsId
CdreCostShiftDigits int // Shift digits in the cost on export (eg: convert from EUR to cents)
CdreDir string // Path towards exported cdrs directory
CdreExportedFields []*utils.RSRField // List of fields in the exported CDRs
CdreFWXmlTemplate *CgrXmlCdreFwCfg // Use this configuration as export template in case of fixed fields length
@@ -201,6 +202,7 @@ func (self *CGRConfig) setDefaults() error {
self.CdreCdrFormat = "csv"
self.CdreMaskDestId = ""
self.CdreMaskLength = 0
self.CdreCostShiftDigits = 0
self.CdreDir = "/var/log/cgrates/cdr/cdre"
self.CdrcEnabled = false
self.CdrcCdrs = utils.INTERNAL
@@ -498,6 +500,9 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) {
if hasOpt = c.HasOption("cdre", "mask_length"); hasOpt {
cfg.CdreMaskLength, _ = c.GetInt("cdre", "mask_length")
}
if hasOpt = c.HasOption("cdre", "cost_shift_digits"); hasOpt {
cfg.CdreCostShiftDigits, _ = c.GetInt("cdre", "cost_shift_digits")
}
if hasOpt = c.HasOption("cdre", "export_template"); hasOpt { // Load configs for csv normally from template, fixed_width from xml file
exportTemplate, _ := c.GetString("cdre", "export_template")
if cfg.CdreCdrFormat != utils.CDRE_FIXED_WIDTH { // Csv most likely

View File

@@ -84,6 +84,7 @@ func TestDefaults(t *testing.T) {
eCfg.CdreCdrFormat = "csv"
eCfg.CdreMaskDestId = ""
eCfg.CdreMaskLength = 0
eCfg.CdreCostShiftDigits = 0
eCfg.CdreDir = "/var/log/cgrates/cdr/cdre"
eCfg.CdrcEnabled = false
eCfg.CdrcCdrs = utils.INTERNAL
@@ -240,6 +241,7 @@ func TestConfigFromFile(t *testing.T) {
eCfg.CdreCdrFormat = "test"
eCfg.CdreMaskDestId = "test"
eCfg.CdreMaskLength = 99
eCfg.CdreCostShiftDigits = 99
eCfg.CdreExportedFields = []*utils.RSRField{&utils.RSRField{Id: "test"}}
eCfg.CdreDir = "test"
eCfg.CdrcEnabled = true

View File

@@ -51,6 +51,7 @@ mediator = test # Address where to reach the Mediator. Empty for disabling me
cdr_format = test # Exported CDRs format <csv>
mask_destination_id = test # Destination id containing called addresses to be masked on export
mask_length = 99 # Length of the destination suffix to be masked
cost_shift_digits = 99 # Shift the number of cost
export_dir = test # Path where the exported CDRs will be placed
export_template = test # List of fields in the exported CDRs

View File

@@ -54,6 +54,7 @@
# cdr_format = csv # Exported CDRs format <csv>
# mask_destination_id = # Destination id containing called addresses to be masked on export
# mask_length = 0 # Length of the destination suffix to be masked
# cost_shift_digits = 0 # Shift cost on export with the number of digits digits defined here (eg: convert from Eur to cent).
# export_dir = /var/log/cgrates/cdr/cdrexport/csv # Path where the exported CDRs will be placed
# export_template = cgrid,mediation_runid,accid,cdrhost,reqtype,direction,tenant,tor,account,subject,destination,setup_time,answer_time,duration,cost
# Exported fields template <""|fld1,fld2|*xml:instance_name>

View File

@@ -784,7 +784,7 @@ func (self *SQLStorage) GetStoredCdrs(cgrIds, runIds, cdrHosts, cdrSources, reqT
return nil, err
}
if err := json.Unmarshal(extraFields, &extraFieldsMp); err != nil {
return nil, err
return nil, fmt.Errorf("JSON unmarshal error for cgrid: %s, runid: %s, error: %s", cgrid, runid, err.Error())
}
storCdr := &utils.StoredCdr{
CgrId: cgrid, AccId: accid, CdrHost: cdrhost, CdrSource: cdrsrc, ReqType: reqtype, Direction: direction, Tenant: tenant,

View File

@@ -320,7 +320,8 @@ type AttrExpFileCdrs struct {
ExportId string // Optional exportid
ExportFileName string // If provided the output filename will be set to this
ExportTemplate string // Exported fields template <""|fld1,fld2|*xml:instance_name>
RoundingDecimals int // Overwrite configured roundDecimals with this dynamically
CostShiftDigits int // If defined it will shift cost digits before applying rouding (eg: convert from Eur->cents)
RoundDecimals int // Overwrite configured roundDecimals with this dynamically
MaskDestinationId string // Overwrite configured MaskDestId
MaskLength int // Overwrite configured MaskLength
CgrIds []string // If provided, it will filter based on the cgrids present in list

View File

@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package utils
import (
"math"
"net/url"
"strconv"
"time"
@@ -135,8 +136,12 @@ func (storedCdr *StoredCdr) GetExtraFields() map[string]string {
}
// Return cost as string, formated with number of decimals configured
func (storedCdr *StoredCdr) FormatCost(roundDecimals int) string {
return strconv.FormatFloat(storedCdr.Cost, 'f', roundDecimals, 64)
func (storedCdr *StoredCdr) FormatCost(shiftDecimals, roundDecimals int) string {
cost := storedCdr.Cost
if shiftDecimals != 0 {
cost = cost * math.Pow10(shiftDecimals)
}
return strconv.FormatFloat(cost, 'f', roundDecimals, 64)
}
func (storedCdr *StoredCdr) AsStoredCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, setupTimeFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*StoredCdr, error) {

View File

@@ -179,11 +179,15 @@ func TestExportFieldValue(t *testing.T) {
func TestFormatCost(t *testing.T) {
cdr := StoredCdr{Cost: 1.01}
if cdr.FormatCost(4) != "1.0100" {
t.Error("Unexpected format of the cost: ", cdr.FormatCost(4))
if cdr.FormatCost(0, 4) != "1.0100" {
t.Error("Unexpected format of the cost: ", cdr.FormatCost(0, 4))
}
cdr = StoredCdr{Cost: 1.01001}
if cdr.FormatCost(4) != "1.0100" {
t.Error("Unexpected format of the cost: ", cdr.FormatCost(4))
if cdr.FormatCost(0, 4) != "1.0100" {
t.Error("Unexpected format of the cost: ", cdr.FormatCost(0, 4))
}
cdr = StoredCdr{Cost: 1.01001}
if cdr.FormatCost(2, 0) != "101" {
t.Error("Unexpected format of the cost: ", cdr.FormatCost(2, 0))
}
}