mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Add new metric type (*distinct) for StatS
This commit is contained in:
committed by
Dan Christian Bogos
parent
7ae16a20d5
commit
4274edf69e
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user