Add new metric type (*distinct) for StatS

This commit is contained in:
TeoV
2019-02-27 15:06:31 +02:00
committed by Dan Christian Bogos
parent 7ae16a20d5
commit 4274edf69e
3 changed files with 221 additions and 18 deletions

View File

@@ -34,15 +34,16 @@ const STATS_NA = -1.0
// cfg serves as general purpose container to pass config options to metric
func NewStatMetric(metricID string, minItems int) (sm StatMetric, err error) {
metrics := map[string]func(int, string) (StatMetric, error){
utils.MetaASR: NewASR,
utils.MetaACD: NewACD,
utils.MetaTCD: NewTCD,
utils.MetaACC: NewACC,
utils.MetaTCC: NewTCC,
utils.MetaPDD: NewPDD,
utils.MetaDDC: NewDCC,
utils.MetaSum: NewStatSum,
utils.MetaAverage: NewStatAverage,
utils.MetaASR: NewASR,
utils.MetaACD: NewACD,
utils.MetaTCD: NewTCD,
utils.MetaACC: NewACC,
utils.MetaTCC: NewTCC,
utils.MetaPDD: NewPDD,
utils.MetaDDC: NewDCC,
utils.MetaSum: NewStatSum,
utils.MetaAverage: NewStatAverage,
utils.MetaDistinct: NewStatDistinct,
}
// split the metricID
// in case of *sum we have *sum#FieldName
@@ -661,6 +662,7 @@ func (ddc *StatDDC) Marshal(ms Marshaler) (marshaled []byte, err error) {
func (ddc *StatDDC) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) {
return ms.Unmarshal(marshaled, ddc)
}
func NewStatSum(minItems int, extraParams string) (StatMetric, error) {
return &StatSum{Events: make(map[string]float64), MinItems: minItems, FieldName: extraParams}, nil
}
@@ -820,3 +822,74 @@ func (avg *StatAverage) Marshal(ms Marshaler) (marshaled []byte, err error) {
func (avg *StatAverage) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) {
return ms.Unmarshal(marshaled, avg)
}
func NewStatDistinct(minItems int, extraParams string) (StatMetric, error) {
return &StatDistinct{Events: make(map[string]struct{}), MinItems: minItems, FieldName: extraParams}, nil
}
type StatDistinct struct {
Numbers float64
Events map[string]struct{} // map[EventTenantID]Cost
MinItems int
FieldName string
val *float64 // cached sum value
}
// getValue returns tcd.val
func (sum *StatDistinct) getValue() float64 {
if sum.val == nil {
if len(sum.Events) == 0 || len(sum.Events) < sum.MinItems {
sum.val = utils.Float64Pointer(STATS_NA)
} else {
sum.val = utils.Float64Pointer(utils.Round(sum.Numbers,
config.CgrConfig().GeneralCfg().RoundingDecimals,
utils.ROUNDING_MIDDLE))
}
}
return *sum.val
}
func (sum *StatDistinct) GetStringValue(fmtOpts string) (valStr string) {
if val := sum.getValue(); val == STATS_NA {
valStr = utils.NOT_AVAILABLE
} else {
valStr = strconv.FormatFloat(sum.getValue(), 'f', -1, 64)
}
return
}
func (sum *StatDistinct) GetValue() (v interface{}) {
return sum.getValue()
}
func (sum *StatDistinct) GetFloat64Value() (v float64) {
return sum.getValue()
}
func (sum *StatDistinct) AddEvent(ev *utils.CGREvent) (err error) {
if has := ev.HasField(sum.FieldName); has {
sum.Numbers += 1
}
sum.Events[ev.ID] = struct{}{}
sum.val = nil
return
}
func (sum *StatDistinct) RemEvent(evID string) (err error) {
_, has := sum.Events[evID]
if !has {
return utils.ErrNotFound
}
delete(sum.Events, evID)
sum.Numbers -= 1
sum.val = nil
return
}
func (sum *StatDistinct) Marshal(ms Marshaler) (marshaled []byte, err error) {
return ms.Marshal(sum)
}
func (sum *StatDistinct) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) {
return ms.Unmarshal(marshaled, sum)
}

View File

@@ -1152,6 +1152,112 @@ func TestStatAverageGetStringValue(t *testing.T) {
}
}
func TestStatDistinctGetFloat64Value(t *testing.T) {
statDistinct, _ := NewStatDistinct(2, "Usage")
ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1",
Event: map[string]interface{}{
"Cost": "20",
"AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC),
"Usage": time.Duration(10 * time.Second),
utils.PDD: time.Duration(5 * time.Second),
utils.Destination: "1002"}}
statDistinct.AddEvent(ev)
if v := statDistinct.GetFloat64Value(); v != -1.0 {
t.Errorf("wrong statDistinct value: %v", v)
}
ev2 := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_2"}
statDistinct.AddEvent(ev2)
if v := statDistinct.GetFloat64Value(); v != 1.0 {
t.Errorf("wrong statDistinct value: %v", v)
}
ev4 := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_4",
Event: map[string]interface{}{
"Cost": "20",
"Usage": time.Duration(1 * time.Minute),
"AnswerTime": time.Date(2015, 7, 14, 14, 25, 0, 0, time.UTC),
utils.PDD: time.Duration(10 * time.Second),
utils.Destination: "1001",
},
}
ev5 := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_5",
Event: map[string]interface{}{
"Cost": "20",
"Usage": time.Duration(1*time.Minute + 30*time.Second),
"AnswerTime": time.Date(2015, 7, 14, 14, 25, 0, 0, time.UTC),
utils.Destination: "1003",
},
}
statDistinct.AddEvent(ev4)
if strVal := statDistinct.GetFloat64Value(); strVal != 2 {
t.Errorf("wrong statDistinct value: %v", strVal)
}
statDistinct.AddEvent(ev5)
if strVal := statDistinct.GetFloat64Value(); strVal != 3 {
t.Errorf("wrong statDistinct value: %v", strVal)
}
statDistinct.RemEvent(ev2.ID)
if strVal := statDistinct.GetFloat64Value(); strVal != 2 {
t.Errorf("wrong statDistinct value: %v", strVal)
}
statDistinct.RemEvent(ev4.ID)
if strVal := statDistinct.GetFloat64Value(); strVal != 1 {
t.Errorf("wrong statDistinct value: %v", strVal)
}
statDistinct.RemEvent(ev.ID)
if strVal := statDistinct.GetFloat64Value(); strVal != -1 {
t.Errorf("wrong statDistinct value: %v", strVal)
}
statDistinct.RemEvent(ev5.ID)
if strVal := statDistinct.GetFloat64Value(); strVal != -1 {
t.Errorf("wrong statDistinct value: %v", strVal)
}
}
func TestStatDistinctGetStringValue(t *testing.T) {
statDistinct, _ := NewStatDistinct(2, "Cost")
ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1",
Event: map[string]interface{}{
"Cost": "20",
"AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC),
utils.Destination: "1002"}}
if strVal := statDistinct.GetStringValue(""); strVal != utils.NOT_AVAILABLE {
t.Errorf("wrong statDistinct value: %s", strVal)
}
statDistinct.AddEvent(ev)
if strVal := statDistinct.GetStringValue(""); strVal != utils.NOT_AVAILABLE {
t.Errorf("wrong statDistinct value: %s", strVal)
}
ev2 := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_2",
Event: map[string]interface{}{
"Cost": "20",
"AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC),
utils.Destination: "1002"}}
ev3 := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_3",
Event: map[string]interface{}{
"Cost": "20",
"AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC),
utils.Destination: "1001"}}
statDistinct.AddEvent(ev2)
statDistinct.AddEvent(ev3)
if strVal := statDistinct.GetStringValue(""); strVal != "3" {
t.Errorf("wrong statDistinct value: %s", strVal)
}
statDistinct.RemEvent(ev.ID)
if strVal := statDistinct.GetStringValue(""); strVal != "2" {
t.Errorf("wrong statDistinct value: %s", strVal)
}
statDistinct.RemEvent(ev2.ID)
if strVal := statDistinct.GetStringValue(""); strVal != utils.NOT_AVAILABLE {
t.Errorf("wrong statDistinct value: %s", strVal)
}
statDistinct.RemEvent(ev3.ID)
if strVal := statDistinct.GetStringValue(""); strVal != utils.NOT_AVAILABLE {
t.Errorf("wrong statDistinct value: %s", strVal)
}
}
var jMarshaler JSONMarshaler
func TestASRMarshal(t *testing.T) {
@@ -1341,3 +1447,26 @@ func TestStatAverageMarshal(t *testing.T) {
t.Errorf("Expected: %s , recived: %s", utils.ToJSON(statAvg), utils.ToJSON(nstatAvg))
}
}
func TestStatDistrictMarshal(t *testing.T) {
statDistinct, _ := NewStatDistinct(2, "Usage")
ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1",
Event: map[string]interface{}{
"Cost": "20",
"AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC),
"Usage": time.Duration(10 * time.Second),
utils.PDD: time.Duration(5 * time.Second),
utils.Destination: "1002"}}
statDistinct.AddEvent(ev)
var nStatDistinct StatDistinct
expected := []byte(`{"Numbers":1,"Events":{"EVENT_1":{}},"MinItems":2,"FieldName":"Usage"}`)
if b, err := statDistinct.Marshal(&jMarshaler); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(expected, b) {
t.Errorf("Expected: %s , recived: %s", string(expected), string(b))
} else if err := nStatDistinct.LoadMarshaled(&jMarshaler, b); err != nil {
t.Error(err)
} else if reflect.DeepEqual(statDistinct, nStatDistinct) {
t.Errorf("Expected: %s , recived: %s", utils.ToJSON(statDistinct), utils.ToJSON(nStatDistinct))
}
}

View File

@@ -559,15 +559,16 @@ const (
// MetaMetrics
const (
MetaASR = "*asr"
MetaACD = "*acd"
MetaTCD = "*tcd"
MetaACC = "*acc"
MetaTCC = "*tcc"
MetaPDD = "*pdd"
MetaDDC = "*ddc"
MetaSum = "*sum"
MetaAverage = "*average"
MetaASR = "*asr"
MetaACD = "*acd"
MetaTCD = "*tcd"
MetaACC = "*acc"
MetaTCC = "*tcc"
MetaPDD = "*pdd"
MetaDDC = "*ddc"
MetaSum = "*sum"
MetaAverage = "*average"
MetaDistinct = "*distinct"
)
// Services