Improvements on Suppliers.MaxCost=*event_cost computation

This commit is contained in:
DanB
2018-03-31 19:44:41 +02:00
parent a896d6321b
commit b2e9f5c181
4 changed files with 75 additions and 105 deletions

View File

@@ -284,7 +284,7 @@ func testV1SplSGetLeastCostSuppliersWithMaxCostNotFound(t *testing.T) {
}
var suplsReply engine.SortedSuppliers
if err := splSv1Rpc.Call(utils.SupplierSv1GetSuppliers,
ev, &suplsReply); err.Error() != utils.ErrNotFound.Error() {
ev, &suplsReply); err != nil && err.Error() != utils.ErrNotFound.Error() {
t.Error(err)
}
}

View File

@@ -113,6 +113,52 @@ func Publish(event CgrEvent) {
}
}
// NewCallDescriptorFromCGREvent converts a CGREvent into CallDescriptor
func NewCallDescriptorFromCGREvent(cgrEv *utils.CGREvent,
timezone string) (cd *CallDescriptor, err error) {
cd = &CallDescriptor{Direction: utils.OUT, Tenant: cgrEv.Tenant}
if _, has := cgrEv.Event[utils.Category]; has {
if cd.Category, err = cgrEv.FieldAsString(utils.Category); err != nil {
return nil, err
}
}
if cd.Account, err = cgrEv.FieldAsString(utils.Account); err != nil {
return
}
if cd.Subject, err = cgrEv.FieldAsString(utils.Subject); err != nil {
if err != utils.ErrNotFound {
return
}
cd.Subject = cd.Account
}
if cd.Destination, err = cgrEv.FieldAsString(utils.Destination); err != nil {
return nil, err
}
if cd.TimeStart, err = cgrEv.FieldAsTime(utils.SetupTime,
timezone); err != nil {
return nil, err
}
if _, has := cgrEv.Event[utils.AnswerTime]; has { // AnswerTime takes precendence for TimeStart
if aTime, err := cgrEv.FieldAsTime(utils.AnswerTime,
timezone); err != nil {
return nil, err
} else if !aTime.IsZero() {
cd.TimeStart = aTime
}
}
if usage, err := cgrEv.FieldAsDuration(utils.Usage); err != nil {
return nil, err
} else {
cd.TimeEnd = cd.TimeStart.Add(usage)
}
if _, has := cgrEv.Event[utils.TOR]; has {
if cd.TOR, err = cgrEv.FieldAsString(utils.TOR); err != nil {
return nil, err
}
}
return
}
/*
The input stucture that contains call information.
*/

View File

@@ -180,11 +180,8 @@ func (spS *SupplierService) costForEvent(ev *utils.CGREvent,
if sTime, err = ev.FieldAsTime(utils.SetupTime, spS.timezone); err != nil {
return
}
usage := time.Duration(time.Minute)
if _, has := ev.Event[utils.Usage]; has {
if usage, err = ev.FieldAsDuration(utils.Usage); err != nil {
return
}
if usage, err = ev.FieldAsDuration(utils.Usage); err != nil {
return
}
for _, anctID := range acntIDs {
cd := &CallDescriptor{
@@ -265,6 +262,9 @@ func (spS *SupplierService) resourceUsage(resIDs []string) (tUsage float64, err
// supliersForEvent will return the list of valid supplier IDs
// for event based on filters and sorting algorithms
func (spS *SupplierService) sortedSuppliersForEvent(args *ArgsGetSuppliers) (sortedSuppls *SortedSuppliers, err error) {
if _, has := args.CGREvent.Event[utils.Usage]; !has {
args.CGREvent.Event[utils.Usage] = time.Duration(time.Minute) // make sure we have default set for Usage
}
var suppPrfls SupplierProfiles
if suppPrfls, err = spS.matchingSupplierProfilesForEvent(&args.CGREvent); err != nil {
return
@@ -284,11 +284,12 @@ func (spS *SupplierService) sortedSuppliersForEvent(args *ArgsGetSuppliers) (sor
}
spls = append(spls, s)
}
extraOpts, err := args.asOptsGetSuppliers(spls)
extraOpts, err := args.asOptsGetSuppliers(spls) // convert suppliers arguments into internal options used to limit data
if err != nil {
return nil, err
}
sortedSuppliers, err := spS.sorter.SortSuppliers(splPrfl.ID, splPrfl.Sorting, spls, &args.CGREvent, extraOpts)
sortedSuppliers, err := spS.sorter.SortSuppliers(splPrfl.ID, splPrfl.Sorting,
spls, &args.CGREvent, extraOpts)
if err != nil {
return nil, err
}
@@ -307,29 +308,33 @@ func (spS *SupplierService) sortedSuppliersForEvent(args *ArgsGetSuppliers) (sor
type ArgsGetSuppliers struct {
IgnoreErrors bool
MaxCost string
MaxCost string // toDo: try with interface{} here
utils.CGREvent
utils.Paginator
}
func (args *ArgsGetSuppliers) asOptsGetSuppliers(supp []*Supplier) (out *optsGetSuppliers, err error) {
out = new(optsGetSuppliers)
if args.MaxCost != "" {
if args.MaxCost == utils.MetaEventCost {
val, err := out.costForEvent(&args.CGREvent, supp)
if err != nil {
return nil, err
}
out.maxCost = val
func (args *ArgsGetSuppliers) asOptsGetSuppliers(supp []*Supplier) (opts *optsGetSuppliers, err error) {
opts = &optsGetSuppliers{ignoreErrors: args.IgnoreErrors}
if args.MaxCost == utils.MetaEventCost { // dynamic cost needs to be calculated from event
if err = args.CGREvent.CheckMandatoryFields([]string{utils.Account,
utils.Destination, utils.SetupTime, utils.Usage}); err != nil {
return
}
cd, err := NewCallDescriptorFromCGREvent(&args.CGREvent, config.CgrConfig().DefaultTimezone)
if err != nil {
return nil, err
}
if cc, err := cd.GetCost(); err != nil {
return nil, err
} else {
val, err := strconv.ParseFloat(args.MaxCost, 64)
if err != nil {
return nil, err
}
out.maxCost = val
opts.maxCost = cc.Cost
}
} else if args.MaxCost != "" {
if opts.maxCost, err = strconv.ParseFloat(args.MaxCost,
64); err != nil {
return nil, err
}
}
out.ignoreErrors = args.IgnoreErrors
return
}
@@ -338,86 +343,6 @@ type optsGetSuppliers struct {
maxCost float64
}
func (args *optsGetSuppliers) costForEvent(ev *utils.CGREvent, suppls []*Supplier) (val float64, err error) {
if err = ev.CheckMandatoryFields([]string{utils.Account,
utils.Destination, utils.SetupTime}); err != nil {
return
}
var acnt, subj, dst, tenant string
if acnt, err = ev.FieldAsString(utils.Account); err != nil {
return
}
if subj, err = ev.FieldAsString(utils.Account); err != nil {
if err != utils.ErrNotFound {
return
}
subj = acnt
}
if dst, err = ev.FieldAsString(utils.Destination); err != nil {
return
}
if tenant, err = ev.FieldAsString(utils.Tenant); err != nil {
if err != utils.ErrNotFound {
return
}
tenant = config.CgrConfig().DefaultTenant
}
var sTime time.Time
if sTime, err = ev.FieldAsTime(utils.SetupTime, config.CgrConfig().DefaultTimezone); err != nil {
return
}
usage := time.Duration(time.Minute)
if _, has := ev.Event[utils.Usage]; has {
if usage, err = ev.FieldAsDuration(utils.Usage); err != nil {
return
}
}
// not sure here how to do or from where get ratingPlan for cost
maxCost := -1.0
for _, s := range suppls {
for _, rp := range s.RatingPlanIDs { // loop through RatingPlans until we find one without errors
rPrfl := &RatingProfile{
Id: utils.ConcatenatedKey(utils.OUT,
tenant, utils.MetaSuppliers, subj),
RatingPlanActivations: RatingPlanActivations{
&RatingPlanActivation{
ActivationTime: sTime,
RatingPlanId: rp,
},
},
}
// force cache set so it can be picked by calldescriptor for cost calculation
Cache.Set(utils.CacheRatingProfiles, rPrfl.Id, rPrfl, nil,
true, utils.NonTransactional)
cd := &CallDescriptor{
Direction: utils.OUT,
Category: utils.MetaSuppliers,
Tenant: tenant,
Subject: subj,
Account: acnt,
Destination: dst,
TimeStart: sTime,
TimeEnd: sTime.Add(usage),
DurationIndex: usage,
}
cc, err := cd.GetCost()
Cache.Remove(utils.CacheRatingProfiles, rPrfl.Id,
true, utils.NonTransactional) // Remove here so we don't overload memory
if err != nil {
if err != utils.ErrNotFound {
return 0.0, err
}
continue
}
ec := NewEventCostFromCallCost(cc, "", "")
if ec.GetCost() > maxCost {
maxCost = ec.GetCost()
}
}
}
return maxCost, nil
}
// V1GetSuppliersForEvent returns the list of valid supplier IDs
func (spS *SupplierService) V1GetSuppliers(args *ArgsGetSuppliers, reply *SortedSuppliers) (err error) {
if missing := utils.MissingStructFields(&args.CGREvent, []string{"Tenant", "ID"}); len(missing) != 0 {

View File

@@ -1406,7 +1406,6 @@ func (smg *SMGeneric) BiRPCv1AuthorizeEvent(clnt rpcclient.RpcClientConnection,
return utils.NewErrNotConnected(utils.SupplierS)
}
cgrEv := args.CGREvent.Clone()
cgrEv.Event[utils.Usage] = time.Duration(time.Minute)
if acd, has := cgrEv.Event[utils.ACD]; has {
cgrEv.Event[utils.Usage] = acd
}