diff --git a/opencensus/metrics/export/__init__.py b/opencensus/metrics/export/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/opencensus/metrics/export/point.py b/opencensus/metrics/export/point.py
new file mode 100644
index 000000000..16aaa52a8
--- /dev/null
+++ b/opencensus/metrics/export/point.py
@@ -0,0 +1,38 @@
+# Copyright 2018, OpenCensus Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class Point(object):
+ """A timestamped measurement of a TimeSeries.
+
+ :type value: Value
+ :param value: the Value of the Point.
+
+ :type timestamp: time
+ :param timestamp: the Timestamp when the Point was recorded.
+ """
+
+ def __init__(self, value, timestamp):
+ self._value = value
+ self._timestamp = timestamp
+
+ @property
+ def value(self):
+ """Returns the Value"""
+ return self._value
+
+ @property
+ def timestamp(self):
+ """Returns the Timestamp when this Point was recorded."""
+ return self._timestamp
diff --git a/opencensus/metrics/export/summary.py b/opencensus/metrics/export/summary.py
new file mode 100644
index 000000000..8ed511148
--- /dev/null
+++ b/opencensus/metrics/export/summary.py
@@ -0,0 +1,144 @@
+# Copyright 2018, OpenCensus Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class Summary(object):
+ """Implementation of the Summary as a summary of observations.
+
+ :type count: long
+ :param count: the count of the population values.
+
+ :type sum_data: float
+ :param sum_data: the sum of the population values.
+
+ :type snapshot: Snapshot
+ :param snapshot: the values calculated over a sliding time window.
+ """
+
+ def __init__(self, count, sum_data, snapshot):
+ check_count_and_sum(count, sum_data)
+ self._count = count
+ self._sum_data = sum_data
+
+ if snapshot is None:
+ raise ValueError('snapshot must not be none')
+
+ self._snapshot = snapshot
+
+ @property
+ def count(self):
+ """Returns the count of the population values"""
+ return self._count
+
+ @property
+ def sum_data(self):
+ """Returns the sum of the population values."""
+ return self._sum_data
+
+ @property
+ def snapshot(self):
+ """Returns the values calculated over a sliding time window."""
+ return self._snapshot
+
+
+class Snapshot(object):
+ """Represents the summary observation of the recorded events over a
+ sliding time window.
+
+ :type count: long
+ :param count: the number of values in the snapshot.
+
+ :type sum_data: float
+ :param sum_data: the sum of values in the snapshot.
+
+ :type value_at_percentiles: ValueAtPercentile
+ :param value_at_percentiles: a list of values at different percentiles
+ of the distribution calculated from the current snapshot. The percentiles
+ must be strictly increasing.
+ """
+
+ def __init__(self, count, sum_data, value_at_percentiles=None):
+ check_count_and_sum(count, sum_data)
+ self._count = count
+ self._sum_data = sum_data
+
+ if value_at_percentiles is None:
+ value_at_percentiles = []
+
+ if not isinstance(value_at_percentiles, list):
+ raise ValueError('value_at_percentiles must be an '
+ 'instance of list')
+
+ self._value_at_percentiles = value_at_percentiles
+
+ @property
+ def count(self):
+ """Returns the number of values in the snapshot"""
+ return self._count
+
+ @property
+ def sum_data(self):
+ """Returns the sum of values in the snapshot."""
+ return self._sum_data
+
+ @property
+ def value_at_percentiles(self):
+ """Returns a list of values at different percentiles
+ of the distribution calculated from the current snapshot.
+ """
+ return self._value_at_percentiles
+
+
+class ValueAtPercentile(object):
+ """Represents the value at a given percentile of a distribution.
+
+ :type percentile: float
+ :param percentile: the percentile in the ValueAtPercentile.
+
+ :type value: float
+ :param value: the value in the ValueAtPercentile.
+ """
+
+ def __init__(self, percentile, value):
+
+ if not 0 < percentile <= 100.0:
+ raise ValueError("percentile must be in the interval (0.0, 100.0]")
+
+ self._percentile = percentile
+
+ if value < 0:
+ raise ValueError('value must be non-negative')
+
+ self._value = value
+
+ @property
+ def percentile(self):
+ """Returns the percentile in the ValueAtPercentile"""
+ return self._percentile
+
+ @property
+ def value(self):
+ """Returns the value in the ValueAtPercentile"""
+ return self._value
+
+
+def check_count_and_sum(count, sum_data):
+ if not (count is None or count >= 0):
+ raise ValueError('count must be non-negative')
+
+ if not (sum_data is None or sum_data >= 0):
+ raise ValueError('sum_data must be non-negative')
+
+ if count == 0 and sum_data != 0:
+ raise ValueError('sum_data must be 0 if count is 0')
diff --git a/opencensus/metrics/export/value.py b/opencensus/metrics/export/value.py
new file mode 100644
index 000000000..995abb8f6
--- /dev/null
+++ b/opencensus/metrics/export/value.py
@@ -0,0 +1,90 @@
+# Copyright 2018, OpenCensus Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class Value(object):
+ """The actual point value for a Point.
+ Currently there are four types of Value:
+
+ - double
+
- long
+
- Summary
+
- Distribution (TODO(mayurkale): add Distribution class)
+
+ Each Point contains exactly one of the four Value types.
+ """
+ def __init__(self, value):
+ self._value = value
+
+ @staticmethod
+ def double_value(value):
+ """Returns a double Value
+
+ :type value: float
+ :param value: value in double
+ """
+ return ValueDouble(value)
+
+ @staticmethod
+ def long_value(value):
+ """Returns a long Value
+
+ :type value: long
+ :param value: value in long
+ """
+ return ValueLong(value)
+
+ @staticmethod
+ def summary_value(value):
+ """Returns a summary Value
+
+ :type value: Summary
+ :param value: value in Summary
+ """
+ return ValueSummary(value)
+
+ @property
+ def value(self):
+ """Returns the value."""
+ return self._value
+
+
+class ValueDouble(Value):
+ """A 64-bit double-precision floating-point number.
+
+ :type value: float
+ :param value: the value in float.
+ """
+ def __init__(self, value):
+ super(ValueDouble, self).__init__(value)
+
+
+class ValueLong(Value):
+ """A 64-bit integer.
+
+ :type value: long
+ :param value: the value in long.
+ """
+ def __init__(self, value):
+ super(ValueLong, self).__init__(value)
+
+
+class ValueSummary(Value):
+ """Represents a snapshot values calculated over an arbitrary time window.
+
+ :type value: summary
+ :param value: the value in summary.
+ """
+ def __init__(self, value):
+ super(ValueSummary, self).__init__(value)
diff --git a/tests/unit/metrics/export/__init__.py b/tests/unit/metrics/export/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/unit/metrics/export/test_point.py b/tests/unit/metrics/export/test_point.py
new file mode 100644
index 000000000..db11eb3fc
--- /dev/null
+++ b/tests/unit/metrics/export/test_point.py
@@ -0,0 +1,64 @@
+# Copyright 2018, OpenCensus Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+from opencensus.metrics.export import point as point_module
+from opencensus.metrics.export import summary as summary_module
+from opencensus.metrics.export import value as value_module
+
+
+class TestPoint(unittest.TestCase):
+
+ def setUp(self):
+ self.double_value = value_module.Value.double_value(55.5)
+ self.long_value = value_module.Value.long_value(9876543210)
+ self.timestamp = '2018-10-06T17:57:57.936475Z'
+
+ value_at_percentile = [summary_module.ValueAtPercentile(99.5, 10.2)]
+ snapshot = summary_module.Snapshot(10, 87.07, value_at_percentile)
+ self.summary = summary_module.Summary(10, 6.6, snapshot)
+ self.summary_value = value_module.Value.summary_value(self.summary)
+
+ def test_point_with_double_value(self):
+ point = point_module.Point(self.double_value, self.timestamp)
+
+ self.assertIsNotNone(point)
+ self.assertEqual(point.timestamp, self.timestamp)
+
+ self.assertIsInstance(point.value, value_module.ValueDouble)
+ self.assertIsNotNone(point.value)
+ self.assertEqual(point.value, self.double_value)
+ self.assertEqual(point.value.value, 55.5)
+
+ def test_point_with_long_value(self):
+ point = point_module.Point(self.long_value, self.timestamp)
+
+ self.assertIsNotNone(point)
+ self.assertEqual(point.timestamp, self.timestamp)
+
+ self.assertIsInstance(point.value, value_module.ValueLong)
+ self.assertIsNotNone(point.value)
+ self.assertEqual(point.value, self.long_value)
+ self.assertEqual(point.value.value, 9876543210)
+
+ def test_point_with_summary_value(self):
+ point = point_module.Point(self.summary_value, self.timestamp)
+
+ self.assertIsNotNone(point)
+ self.assertEqual(point.timestamp, self.timestamp)
+
+ self.assertIsInstance(point.value, value_module.ValueSummary)
+ self.assertIsNotNone(point.value)
+ self.assertEqual(point.value, self.summary_value)
+ self.assertEqual(point.value.value, self.summary)
diff --git a/tests/unit/metrics/export/test_summary.py b/tests/unit/metrics/export/test_summary.py
new file mode 100644
index 000000000..960d726a3
--- /dev/null
+++ b/tests/unit/metrics/export/test_summary.py
@@ -0,0 +1,121 @@
+# Copyright 2018, OpenCensus Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+from opencensus.metrics.export import summary as summary_module
+
+
+class TestSummary(unittest.TestCase):
+
+ def setUp(self):
+ value_at_percentile = [summary_module.ValueAtPercentile(99.5, 10.2)]
+ self.snapshot = summary_module.Snapshot(10, 87.07, value_at_percentile)
+
+ def test_constructor(self):
+ summary = summary_module.Summary(10, 6.6, self.snapshot)
+
+ self.assertIsNotNone(summary)
+ self.assertEquals(summary.count, 10)
+ self.assertEquals(summary.sum_data, 6.6)
+ self.assertIsNotNone(summary.snapshot)
+ self.assertIsInstance(summary.snapshot, summary_module.Snapshot)
+
+ def test_constructor_with_negative_count(self):
+ with self.assertRaisesRegexp(ValueError, 'count must be non-negative'):
+ summary_module.Summary(-10, 87.07, self.snapshot)
+
+ def test_constructor_with_negative_sum_data(self):
+ with self.assertRaisesRegexp(ValueError, 'sum_data must be non-negative'):
+ summary_module.Summary(10, -87.07, self.snapshot)
+
+ def test_constructor_with_zero_count_and_sum_data(self):
+ with self.assertRaisesRegexp(ValueError, 'sum_data must be 0 if count is 0'):
+ summary_module.Summary(0, 87.07, self.snapshot)
+
+ def test_constructor_with_none_snapshot(self):
+ with self.assertRaisesRegexp(ValueError, 'snapshot must not be none'):
+ summary_module.Summary(10, 87.07, None)
+
+
+class TestSnapshot(unittest.TestCase):
+
+ def setUp(self):
+ self.value_at_percentile = [summary_module.ValueAtPercentile(99.5, 10.2)]
+
+ # Invalid value_at_percentile
+ self.value_at_percentile1 = summary_module.ValueAtPercentile(99.5, 10.2)
+
+ def test_constructor(self):
+ snapshot = summary_module.Snapshot(10, 87.07, self.value_at_percentile)
+
+ self.assertIsNotNone(snapshot)
+ self.assertEquals(snapshot.count, 10)
+ self.assertEquals(snapshot.sum_data, 87.07)
+ self.assertIsNotNone(snapshot.value_at_percentiles)
+ self.assertEquals(len(snapshot.value_at_percentiles), 1)
+ self.assertEquals(snapshot.value_at_percentiles[0].percentile, 99.5)
+ self.assertEquals(snapshot.value_at_percentiles[0].value, 10.2)
+
+ def test_constructor_invalid_value_at_percentile(self):
+ with self.assertRaises(ValueError):
+ summary_module.Snapshot(10, 87.07, self.value_at_percentile1)
+
+ def test_constructor_empty_value_at_percentile(self):
+ snapshot = summary_module.Snapshot(10, 87.07)
+
+ self.assertIsNotNone(snapshot)
+ self.assertIsNotNone(snapshot.value_at_percentiles)
+ self.assertEquals(len(snapshot.value_at_percentiles), 0)
+
+ def test_constructor_with_negative_count(self):
+ with self.assertRaisesRegexp(ValueError, 'count must be non-negative'):
+ summary_module.Snapshot(-10, 87.07, self.value_at_percentile)
+
+ def test_constructor_with_negative_sum_data(self):
+ with self.assertRaisesRegexp(ValueError, 'sum_data must be non-negative'):
+ summary_module.Snapshot(10, -87.07, self.value_at_percentile)
+
+ def test_constructor_with_zero_count(self):
+ with self.assertRaisesRegexp(ValueError, 'sum_data must be 0 if count is 0'):
+ summary_module.Snapshot(0, 87.07, self.value_at_percentile)
+
+ def test_constructor_with_zero_count_and_sum_data(self):
+ summary_module.Snapshot(0, 0, self.value_at_percentile)
+
+ def test_constructor_with_none_count_sum(self):
+ snapshot = summary_module.Snapshot(None, None, self.value_at_percentile)
+
+ self.assertIsNotNone(snapshot)
+ self.assertIsNone(snapshot.count)
+ self.assertIsNone(snapshot.sum_data)
+ self.assertIsNotNone(snapshot.value_at_percentiles)
+ self.assertEquals(len(snapshot.value_at_percentiles), 1)
+
+
+class TestValueAtPercentile(unittest.TestCase):
+
+ def test_constructor(self):
+ value_at_percentile = summary_module.ValueAtPercentile(99.5, 10.2)
+
+ self.assertIsNotNone(value_at_percentile)
+ self.assertEquals(value_at_percentile.value, 10.2)
+ self.assertEquals(value_at_percentile.percentile, 99.5)
+
+ def test_constructor_invalid_percentile(self):
+ with self.assertRaises(ValueError):
+ summary_module.ValueAtPercentile(100.1, 10.2)
+
+ def test_constructor_invalid_value(self):
+ with self.assertRaisesRegexp(ValueError, 'value must be non-negative'):
+ summary_module.ValueAtPercentile(99.5, -10.2)
diff --git a/tests/unit/metrics/export/test_value.py b/tests/unit/metrics/export/test_value.py
new file mode 100644
index 000000000..d6bfb5fae
--- /dev/null
+++ b/tests/unit/metrics/export/test_value.py
@@ -0,0 +1,45 @@
+# Copyright 2018, OpenCensus Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+from opencensus.metrics.export import summary as summary_module
+from opencensus.metrics.export import value as value_module
+
+
+class TestValue(unittest.TestCase):
+
+ def test_create_double_value(self):
+ double_value = value_module.Value.double_value(-34.56)
+
+ self.assertIsNotNone(double_value)
+ self.assertIsInstance(double_value, value_module.ValueDouble)
+ self.assertEqual(double_value.value, -34.56)
+
+ def test_create_long_value(self):
+ long_value = value_module.Value.long_value(123456789)
+
+ self.assertIsNotNone(long_value)
+ self.assertIsInstance(long_value, value_module.ValueLong)
+ self.assertEqual(long_value.value, 123456789)
+
+ def test_create_summary_value(self):
+ value_at_percentile = [summary_module.ValueAtPercentile(99.5, 10.2)]
+ snapshot = summary_module.Snapshot(10, 87.07, value_at_percentile)
+ summary = summary_module.Summary(10, 6.6, snapshot)
+
+ summary_value = value_module.Value.summary_value(summary)
+
+ self.assertIsNotNone(summary_value)
+ self.assertIsInstance(summary_value, value_module.ValueSummary)
+ self.assertEqual(summary_value.value, summary)