Skip to content

Commit

Permalink
[api] Metrics: support real numerical data types
Browse files Browse the repository at this point in the history
Extends `Metrics` data type support to any real number.
  • Loading branch information
yannmh committed Nov 17, 2015
1 parent 240fdf5 commit 9bae564
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 13 deletions.
51 changes: 43 additions & 8 deletions datadog/api/metrics.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import time
from numbers import Number

from datadog.api.base import SearchableAPIResource, SendableAPIResource
from datadog.api.exceptions import ApiError
Expand All @@ -14,17 +15,51 @@ class Metric(SearchableAPIResource, SendableAPIResource):
_METRIC_QUERY_ENDPOINT = '/query'
_METRIC_SUBMIT_ENDPOINT = '/series'

_SUPPORTED_DATA_TYPES = (int, float, long)

@classmethod
def _process_points(cls, points):
"""
Format `points` parameter.
Input:
(value or (timestamp * value)) list or singleton
Returns:
(timestamp * float value) list
"""
now = time.time()
if isinstance(points, cls._SUPPORTED_DATA_TYPES):
points = [(now, points)]
elif isinstance(points, tuple):
points = [points]
points_lst = points if isinstance(points, list) else [points]

def rec_parse(points_lst):
"""
Recursively parse a list of values or a list of timestamp * value to a list of
(timestamp, `float` value)s.
"""
try:
if not points_lst:
return []

point = points_lst.pop()
timestamp = now if isinstance(point, Number) else point[0]
value = float(point) if isinstance(point, Number) else float(point[1])

point = [(timestamp, value)]

return point + rec_parse(points_lst)

except TypeError as e:
raise TypeError(
u"{0}: "
"`points` parameter must use real numerical values.".format(e)
)

except IndexError as e:
raise IndexError(
u"{0}: "
"`points` must be a list of values or a list of timestamp * value".format(e)
)

return points
return rec_parse(points_lst)

@classmethod
def send(cls, metrics=None, **single_metric):
Expand All @@ -34,7 +69,7 @@ def send(cls, metrics=None, **single_metric):
:param metric: the name of the time series
:type metric: string
:param points: list of points to submit
:param points: singleton or list of points to submit
:type points: list
:param host: host name that produced the metric
Expand Down
14 changes: 9 additions & 5 deletions tests/unit/api/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,17 +281,17 @@ def submit_and_assess_metric_payload(self, serie):
# it consists of a [time, value] pair
self.assertEquals(len(metric['points'][0]), 2)
# its value == value we sent
self.assertEquals(metric['points'][0][1], serie[i]['points'])
self.assertEquals(metric['points'][0][1], float(serie[i]['points']))
# it's time not so far from current time
assert now - 1 < metric['points'][0][0] < now + 1

def test_metric_submit_query_switch(self):
"""
Endpoints are different for submission and queries.
"""
Metric.send(points="val")
Metric.send(points=(123, 456))
self.request_called_with('POST', "host/api/v1/series",
data={'series': [{'points': "val", 'host': api._host_name}]})
data={'series': [{'points': [[123, 456.0]], 'host': api._host_name}]})

Metric.query(start="val1", end="val2")
self.request_called_with('GET', "host/api/v1/query",
Expand All @@ -312,9 +312,13 @@ def test_points_submission(self):

def test_data_type_support(self):
"""
`Metric` API supports built-in real numerical data types.
`Metric` API supports `real` numerical data types.
"""
supported_data_types = [1, 1.0, 1L]
from decimal import Decimal
from fractions import Fraction
from builtins import int # subclass of long on Py2

supported_data_types = [1, 1.0, int(1), Decimal(1), Fraction(1, 2)]

for point in supported_data_types:
serie = dict(metric='metric.numerical', points=point)
Expand Down

0 comments on commit 9bae564

Please sign in to comment.