Adding detailed CDR export filters both in storage and APIs

This commit is contained in:
DanB
2014-03-16 16:21:18 +01:00
parent f6d16cecc5
commit ee31976401
6 changed files with 98 additions and 18 deletions

View File

@@ -21,6 +21,7 @@ package apier
import (
"fmt"
"github.com/cgrates/cgrates/cdrexporter"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/utils"
"os"
"path"
@@ -35,6 +36,12 @@ func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.E
if !utils.IsSliceMember(utils.CdreCdrFormats, cdrFormat) {
return fmt.Errorf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, "CdrFormat")
}
exportedFields := self.Config.CdreExportedFields
if len(attr.ExportedFields) != 0 {
if exportedFields, err = config.ParseRSRFields(attr.ExportedFields); err != nil {
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
}
}
if len(attr.TimeStart) != 0 {
if tStart, err = utils.ParseTimeDetectLayout(attr.TimeStart); err != nil {
return err
@@ -45,7 +52,8 @@ func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.E
return err
}
}
cdrs, err := self.CdrDb.GetStoredCdrs(tStart, tEnd, attr.SkipErrors, attr.SkipRated)
cdrs, err := self.CdrDb.GetStoredCdrs(attr.MediationRunId, attr.CdrHost, attr.CdrSource, attr.ReqType, attr.Direction,
attr.Tenant, attr.Tor, attr.Account, attr.Subject, attr.DestinationPrefix, tStart, tEnd, attr.SkipErrors, attr.SkipRated)
if err != nil {
return err
}
@@ -58,7 +66,7 @@ func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.E
} else {
defer fileOut.Close()
}
csvWriter := cdrexporter.NewCsvCdrWriter(fileOut, self.Config.RoundingDecimals, self.Config.CdreExportedFields)
csvWriter := cdrexporter.NewCsvCdrWriter(fileOut, self.Config.RoundingDecimals, exportedFields)
for _, cdr := range cdrs {
if err := csvWriter.Write(cdr); err != nil {
os.Remove(fileName)

View File

@@ -102,7 +102,7 @@ type CdrStorage interface {
Storage
SetCdr(utils.RawCDR) error
SetRatedCdr(*utils.StoredCdr, string) error
GetStoredCdrs(time.Time, time.Time, bool, bool) ([]*utils.StoredCdr, error)
GetStoredCdrs(string, string, string, string, string, string, string, string, string, string, time.Time, time.Time, bool, bool) ([]*utils.StoredCdr, error)
RemStoredCdrs([]string) error
}

View File

@@ -569,10 +569,71 @@ func (self *SQLStorage) SetRatedCdr(storedCdr *utils.StoredCdr, extraInfo string
// Return a slice of CDRs from storDb using optional filters.a
// ignoreErr - do not consider cdrs with rating errors
// ignoreRated - do not consider cdrs which were already rated, including here the ones with errors
func (self *SQLStorage) GetStoredCdrs(timeStart, timeEnd time.Time, ignoreErr, ignoreRated bool) ([]*utils.StoredCdr, error) {
func (self *SQLStorage) GetStoredCdrs(runId, cdrHost, cdrSource, reqType, direction, tenant, tor, account, subject, destPrefix string,
timeStart, timeEnd time.Time, ignoreErr, ignoreRated bool) ([]*utils.StoredCdr, error) {
var cdrs []*utils.StoredCdr
q := fmt.Sprintf("SELECT %s.cgrid,accid,cdrhost,cdrsource,reqtype,direction,tenant,tor,account,%s.subject,destination,setup_time,answer_time,duration,extra_fields,runid,cost FROM %s LEFT JOIN %s ON %s.cgrid=%s.cgrid LEFT JOIN %s ON %s.cgrid=%s.cgrid", utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_EXTRA, utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_EXTRA, utils.TBL_RATED_CDRS, utils.TBL_CDRS_PRIMARY, utils.TBL_RATED_CDRS)
fltr := ""
if len(runId) != 0 {
if len(fltr) != 0 {
fltr += " AND "
}
fltr += fmt.Sprintf(" runid='%s'", runId)
}
if len(cdrHost) != 0 {
if len(fltr) != 0 {
fltr += " AND "
}
fltr += fmt.Sprintf(" cdrhost='%s'", cdrHost)
}
if len(cdrSource) != 0 {
if len(fltr) != 0 {
fltr += " AND "
}
fltr += fmt.Sprintf(" cdrsource='%s'", cdrSource)
}
if len(reqType) != 0 {
if len(fltr) != 0 {
fltr += " AND "
}
fltr += fmt.Sprintf(" reqtype='%s'", reqType)
}
if len(direction) != 0 {
if len(fltr) != 0 {
fltr += " AND "
}
fltr += fmt.Sprintf(" direction='%s'", direction)
}
if len(tenant) != 0 {
if len(fltr) != 0 {
fltr += " AND "
}
fltr += fmt.Sprintf(" tenant='%s'", tenant)
}
if len(tor) != 0 {
if len(fltr) != 0 {
fltr += " AND "
}
fltr += fmt.Sprintf(" tor='%s'", tor)
}
if len(account) != 0 {
if len(fltr) != 0 {
fltr += " AND "
}
fltr += fmt.Sprintf(" account='%s'", account)
}
if len(subject) != 0 {
if len(fltr) != 0 {
fltr += " AND "
}
fltr += fmt.Sprintf(" %s.subject='%s'", utils.TBL_CDRS_PRIMARY, subject)
}
if len(destPrefix) != 0 {
if len(fltr) != 0 {
fltr += " AND "
}
fltr += fmt.Sprintf(" destination LIKE '%s%'", destPrefix)
}
if !timeStart.IsZero() {
if len(fltr) != 0 {
fltr += " AND "

View File

@@ -161,7 +161,7 @@ func (self *Mediator) RateCdr(dbcdr utils.RawCDR) error {
}
func (self *Mediator) RateCdrs(timeStart, timeEnd time.Time, rerateErrors, rerateRated bool) error {
cdrs, err := self.cdrDb.GetStoredCdrs(timeStart, timeEnd, !rerateErrors, !rerateRated)
cdrs, err := self.cdrDb.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", timeStart, timeEnd, !rerateErrors, !rerateRated)
if err != nil {
return err
}

View File

@@ -150,12 +150,12 @@ func TestPostCdrs(t *testing.T) {
}
}
time.Sleep(10 * time.Millisecond) // Give time for CDRs to reach database
if storedCdrs, err := cdrStor.GetStoredCdrs(time.Time{}, time.Time{}, false, false); err != nil {
if storedCdrs, err := cdrStor.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, false, false); err != nil {
t.Error(err)
} else if len(storedCdrs) != 2 { // Make sure CDRs made it into StorDb
t.Error(fmt.Sprintf("Unexpected number of CDRs stored: %d", len(storedCdrs)))
}
if nonErrorCdrs, err := cdrStor.GetStoredCdrs(time.Time{}, time.Time{}, true, false); err != nil {
if nonErrorCdrs, err := cdrStor.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, true, false); err != nil {
t.Error(err)
} else if len(nonErrorCdrs) != 0 { // Just two of them should be without errors
t.Error(fmt.Sprintf("Unexpected number of CDRs stored: %d", len(nonErrorCdrs)))
@@ -178,12 +178,12 @@ func TestInjectCdrs(t *testing.T) {
t.Error(err)
}
}
if storedCdrs, err := cdrStor.GetStoredCdrs(time.Time{}, time.Time{}, false, false); err != nil {
if storedCdrs, err := cdrStor.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, false, false); err != nil {
t.Error(err)
} else if len(storedCdrs) != 4 { // Make sure CDRs made it into StorDb
t.Error(fmt.Sprintf("Unexpected number of CDRs stored: %d", len(storedCdrs)))
}
if nonRatedCdrs, err := cdrStor.GetStoredCdrs(time.Time{}, time.Time{}, true, true); err != nil {
if nonRatedCdrs, err := cdrStor.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, true, true); err != nil {
t.Error(err)
} else if len(nonRatedCdrs) != 2 { // Just two of them should be non-rated
t.Error(fmt.Sprintf("Unexpected number of CDRs non-rated: %d", len(nonRatedCdrs)))
@@ -215,12 +215,12 @@ func TestRateCdrs(t *testing.T) {
} else if reply != utils.OK {
t.Errorf("Unexpected reply: %s", reply)
}
if nonRatedCdrs, err := cdrStor.GetStoredCdrs(time.Time{}, time.Time{}, true, true); err != nil {
if nonRatedCdrs, err := cdrStor.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, true, true); err != nil {
t.Error(err)
} else if len(nonRatedCdrs) != 0 { // Just two of them should be non-rated
t.Error(fmt.Sprintf("Unexpected number of CDRs non-rated: %d", len(nonRatedCdrs)))
}
if errRatedCdrs, err := cdrStor.GetStoredCdrs(time.Time{}, time.Time{}, false, true); err != nil {
if errRatedCdrs, err := cdrStor.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, false, true); err != nil {
t.Error(err)
} else if len(errRatedCdrs) != 2 { // The first 2 with errors should be still there before rerating
t.Error(fmt.Sprintf("Unexpected number of CDRs with errors: %d", len(errRatedCdrs)))
@@ -230,7 +230,7 @@ func TestRateCdrs(t *testing.T) {
} else if reply != utils.OK {
t.Errorf("Unexpected reply: %s", reply)
}
if errRatedCdrs, err := cdrStor.GetStoredCdrs(time.Time{}, time.Time{}, false, true); err != nil {
if errRatedCdrs, err := cdrStor.GetStoredCdrs("", "", "", "", "", "", "", "", "", "", time.Time{}, time.Time{}, false, true); err != nil {
t.Error(err)
} else if len(errRatedCdrs) != 1 { // One CDR with errors should be fixed now by rerating
t.Error(fmt.Sprintf("Unexpected number of CDRs with errors: %d", len(errRatedCdrs)))

View File

@@ -309,12 +309,23 @@ type CachedItemAge struct {
}
type AttrExpFileCdrs struct {
CdrFormat string // Cdr output file format <utils.CdreCdrFormats>
TimeStart string // If provided, will represent the starting of the CDRs interval (>=)
TimeEnd string // If provided, will represent the end of the CDRs interval (<)
SkipErrors bool // Do not export errored CDRs
SkipRated bool // Do not export rated CDRs
RemoveFromDb bool // If true the CDRs will be also deleted after export
CdrFormat string // Cdr output file format <utils.CdreCdrFormats>
ExportedFields string // Optional comma separated list of fields ot be exported in a CDR
MediationRunId string // If provided, it will filter on mediation runid
CdrHost string // If provided, it will filter cdrhost
CdrSource string // If provided, it will filter cdrsource
ReqType string // If provided, it will fiter reqtype
Direction string // If provided, it will fiter direction
Tenant string // If provided, it will filter tenant
Tor string // If provided, it will filter tor
Account string // If provided, it will filter account
Subject string // If provided, it will filter the rating subject
DestinationPrefix string // If provided, it will filter on destination prefix
TimeStart string // If provided, it will represent the starting of the CDRs interval (>=)
TimeEnd string // If provided, it will represent the end of the CDRs interval (<)
SkipErrors bool // Do not export errored CDRs
SkipRated bool // Do not export rated CDRs
RemoveFromDb bool // If true the CDRs will be also deleted after export
}
type ExportedFileCdrs struct {