From 381e9ab197d48be86fe8d153ec3245d5ffbc726c Mon Sep 17 00:00:00 2001 From: Richard Theis Date: Fri, 18 Mar 2016 08:54:55 -0500 Subject: [PATCH] Consistent resource.prop for timestamps and booleans (telemetry) This patch set updates all telemetry objects to use consistent resource.prop for timestamps and booleans. In particular, the following changes were made: - Clarify documentation for timestamp and boolean attributes - Use 'is_' prefix and boolean type for boolean attributes - Use '_at' suffix and timestamp type for timestamp attributes Change-Id: I76e98a0f929b8688685a7e9de2d362d8a0a1b555 Partial-Bug: #1544584 --- openstack/telemetry/v2/alarm.py | 20 ++++++----- openstack/telemetry/v2/alarm_change.py | 6 ++-- openstack/telemetry/v2/capability.py | 2 +- openstack/telemetry/v2/resource.py | 15 +++++--- openstack/telemetry/v2/sample.py | 16 +++++---- openstack/telemetry/v2/statistics.py | 21 +++++++----- .../tests/unit/telemetry/v2/test_alarm.py | 18 ++++++---- .../unit/telemetry/v2/test_alarm_change.py | 12 +++++-- .../unit/telemetry/v2/test_capability.py | 12 +++---- .../tests/unit/telemetry/v2/test_resource.py | 16 +++++---- .../tests/unit/telemetry/v2/test_sample.py | 34 +++++++++++++------ .../unit/telemetry/v2/test_statistics.py | 26 +++++++++----- 12 files changed, 130 insertions(+), 68 deletions(-) diff --git a/openstack/telemetry/v2/alarm.py b/openstack/telemetry/v2/alarm.py index 3df1c017..1d182016 100644 --- a/openstack/telemetry/v2/alarm.py +++ b/openstack/telemetry/v2/alarm.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from openstack import format from openstack import resource from openstack.telemetry import telemetry_service from openstack import utils @@ -37,32 +38,35 @@ class Alarm(resource.Resource): combination_rule = resource.prop('combination_rule') #: The description of the alarm description = resource.prop('description') - #: ``True`` if this alarm is enabled - enabled = resource.prop('enabled', type=bool) + #: ``True`` if this alarm is enabled. *Type: bool* + is_enabled = resource.prop('enabled', type=bool) #: The actions to do when alarm state changes to insufficient data insufficient_data_actions = resource.prop('insufficient_data_actions') + #: The actions should be re-triggered on each evaluation cycle. + #: *Type: bool* + is_repeat_actions = resource.prop('repeat_actions', type=bool) #: The name for the alarm name = resource.prop('name') #: The actions to do when alarm state change to ok ok_actions = resource.prop('ok_actions') #: The ID of the project that owns the alarm project_id = resource.prop('project_id') - #: The actions should be re-triggered on each evaluation cycle - repeat_actions = resource.prop('repeat_actions', type=bool) #: The severity of the alarm severity = resource.prop('severity') #: The state off the alarm state = resource.prop('state') - #: The timestamp of the last alarm state change - state_changed_at = resource.prop('state_timestamp') + #: The timestamp of the last alarm state change. + #: *Type: datetime object parsed from ISO 8601 formatted string* + state_changed_at = resource.prop('state_timestamp', type=format.ISO8601) # TODO(briancurtin): undocumented threshold_rule = resource.prop('threshold_rule', type=dict) #: Describe time constraints for the alarm time_constraints = resource.prop('time_constraints') #: Explicit type specifier to select which rule to follow type = resource.prop('type') - #: The timestamp of the last alarm definition update - updated_at = resource.prop('timestamp') + #: The timestamp of the last alarm definition update. + #: *Type: datetime object parsed from ISO 8601 formatted string* + updated_at = resource.prop('timestamp', type=format.ISO8601) #: The ID of the user who created the alarm user_id = resource.prop('user_id') diff --git a/openstack/telemetry/v2/alarm_change.py b/openstack/telemetry/v2/alarm_change.py index 20d57f0a..d959fab0 100644 --- a/openstack/telemetry/v2/alarm_change.py +++ b/openstack/telemetry/v2/alarm_change.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from openstack import format from openstack import resource from openstack.telemetry import telemetry_service @@ -35,8 +36,9 @@ class AlarmChange(resource.Resource): on_behalf_of_id = resource.prop('on_behalf_of') #: The project ID of the initiating identity project_id = resource.prop('project_id') - #: The time/date of the alarm change - triggered_at = resource.prop('timestamp') + #: The time/date of the alarm change. + #: *Type: datetime object parsed from ISO 8601 formatted string* + triggered_at = resource.prop('timestamp', type=format.ISO8601) #: The type of change type = resource.prop('type') #: The user ID of the initiating identity diff --git a/openstack/telemetry/v2/capability.py b/openstack/telemetry/v2/capability.py index 70e706b1..b5001e8e 100644 --- a/openstack/telemetry/v2/capability.py +++ b/openstack/telemetry/v2/capability.py @@ -27,7 +27,7 @@ class Capability(resource.Resource): allow_list = True # Properties - enabled = resource.prop('enabled') + is_enabled = resource.prop('enabled', type=bool) @classmethod def list(cls, session, limit=None, marker=None, path_args=None, diff --git a/openstack/telemetry/v2/resource.py b/openstack/telemetry/v2/resource.py index 31c256dc..fe315660 100644 --- a/openstack/telemetry/v2/resource.py +++ b/openstack/telemetry/v2/resource.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from openstack import format from openstack import resource from openstack.telemetry import telemetry_service @@ -25,10 +26,16 @@ class Resource(resource.Resource): allow_list = True # Properties - #: UTC date & time not later than the first sample known for this resource - first_sample_at = resource.prop('first_sample_timestamp') - #: UTC date & time not earlier than the last sample known for this resource - last_sample_at = resource.prop('last_sample_timestamp') + #: UTC date & time not later than the first sample known + #: for this resource. + #: *Type: datetime object parsed from ISO 8601 formatted string* + first_sample_at = resource.prop('first_sample_timestamp', + type=format.ISO8601) + #: UTC date & time not earlier than the last sample known + #: for this resource. + #: *Type: datetime object parsed from ISO 8601 formatted string* + last_sample_at = resource.prop('last_sample_timestamp', + type=format.ISO8601) #: A list containing a self link and associated meter links links = resource.prop('links') #: Arbitrary metadata associated with the resource diff --git a/openstack/telemetry/v2/sample.py b/openstack/telemetry/v2/sample.py index 9c5c9582..e6f0d114 100644 --- a/openstack/telemetry/v2/sample.py +++ b/openstack/telemetry/v2/sample.py @@ -9,6 +9,8 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. + +from openstack import format from openstack import resource from openstack.telemetry import telemetry_service @@ -24,20 +26,22 @@ class Sample(resource.Resource): allow_list = True # Properties - #: Arbitrary metadata associated with the sample - metadata = resource.prop('metadata', alias='resource_metadata') #: The meter name this sample is for counter_name = resource.prop('meter', alias='counter_name') + #: When the sample has been generated. + #: *Type: datetime object parsed from ISO 8601 formatted string* + generated_at = resource.prop('timestamp', type=format.ISO8601) + #: Arbitrary metadata associated with the sample + metadata = resource.prop('metadata', alias='resource_metadata') #: The ID of the project this sample was taken for project_id = resource.prop('project_id') - #: When the sample has been recorded - recorded_at = resource.prop('recorded_at') + #: When the sample has been recorded. + #: *Type: datetime object parsed from ISO 8601 formatted string* + recorded_at = resource.prop('recorded_at', type=format.ISO8601) #: The ID of the resource this sample was taken for resource_id = resource.prop('resource_id') #: The name of the source that identifies where the sample comes from source = resource.prop('source') - #: When the sample has been generated - generated_at = resource.prop('timestamp') #: The meter type type = resource.prop('type', alias='counter_type') #: The unit of measure diff --git a/openstack/telemetry/v2/statistics.py b/openstack/telemetry/v2/statistics.py index 170a9bf2..4a6d3cd2 100644 --- a/openstack/telemetry/v2/statistics.py +++ b/openstack/telemetry/v2/statistics.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from openstack import format from openstack import resource from openstack.telemetry import telemetry_service @@ -36,10 +37,12 @@ class Statistics(resource.Resource): count = resource.prop('count') #: The difference, in seconds, between the oldest and newest timestamp duration = resource.prop('duration') - #: UTC date and time of the oldest timestamp, or the query end time - duration_end = resource.prop('duration_end') - #: UTC date and time of the earliest timestamp, or the query start time - duration_start = resource.prop('duration_start') + #: UTC date and time of the oldest timestamp, or the query end time. + #: *Type: datetime object parsed from ISO 8601 formatted string* + duration_end_at = resource.prop('duration_end', type=format.ISO8601) + #: UTC date and time of the earliest timestamp, or the query start time. + #: *Type: datetime object parsed from ISO 8601 formatted string* + duration_start_at = resource.prop('duration_start', type=format.ISO8601) #: Dictionary of field names for group, if groupby statistics are requested group_by = resource.prop('groupby') #: The maximum volume seen in the data @@ -48,10 +51,12 @@ class Statistics(resource.Resource): min = resource.prop('min') #: The difference, in seconds, between the period start and end period = resource.prop('period') - #: UTC date and time of the period end - period_end = resource.prop('period_end') - #: UTC date and time of the period start - period_start = resource.prop('period_start') + #: UTC date and time of the period end. + #: *Type: datetime object parsed from ISO 8601 formatted string* + period_end_at = resource.prop('period_end', type=format.ISO8601) + #: UTC date and time of the period start. + #: *Type: datetime object parsed from ISO 8601 formatted string* + period_start_at = resource.prop('period_start', type=format.ISO8601) #: The total of all of the volume values seen in the data sum = resource.prop('sum') #: The unit type of the data set diff --git a/openstack/tests/unit/telemetry/v2/test_alarm.py b/openstack/tests/unit/telemetry/v2/test_alarm.py index 8b58072e..5b81111c 100644 --- a/openstack/tests/unit/telemetry/v2/test_alarm.py +++ b/openstack/tests/unit/telemetry/v2/test_alarm.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +import datetime + import mock import testtools @@ -29,8 +31,8 @@ EXAMPLE = { 'repeat_actions': False, 'severity': 'low', 'state': 'insufficient data', - 'state_timestamp': '8', - 'timestamp': '9', + 'state_timestamp': '2015-03-09T12:15:57.233772', + 'timestamp': '2015-03-09T12:15:57.233772', 'threshold_rule': {'meter_name': 'a', 'evaluation_periods:': '1', 'period': '60', @@ -73,17 +75,21 @@ class TestAlarm(testtools.TestCase): self.assertEqual(IDENTIFIER, sot.alarm_id) self.assertEqual(EXAMPLE['combination_rule'], sot.combination_rule) self.assertEqual(EXAMPLE['description'], sot.description) - self.assertTrue(sot.enabled) + self.assertTrue(sot.is_enabled) self.assertEqual(EXAMPLE['insufficient_data_actions'], sot.insufficient_data_actions) self.assertEqual(EXAMPLE['name'], sot.name) self.assertEqual(EXAMPLE['ok_actions'], sot.ok_actions) self.assertEqual(EXAMPLE['project_id'], sot.project_id) - self.assertFalse(sot.repeat_actions) + self.assertFalse(sot.is_repeat_actions) self.assertEqual(EXAMPLE['severity'], sot.severity) self.assertEqual(EXAMPLE['state'], sot.state) - self.assertEqual(EXAMPLE['state_timestamp'], sot.state_changed_at) - self.assertEqual(EXAMPLE['timestamp'], sot.updated_at) + dt = datetime.datetime(2015, 3, 9, 12, 15, 57, 233772).replace( + tzinfo=None) + self.assertEqual(dt, sot.state_changed_at.replace(tzinfo=None)) + dt = datetime.datetime(2015, 3, 9, 12, 15, 57, 233772).replace( + tzinfo=None) + self.assertEqual(dt, sot.updated_at.replace(tzinfo=None)) self.assertEqual(EXAMPLE['threshold_rule'], sot.threshold_rule) self.assertEqual(EXAMPLE['time_constraints'], sot.time_constraints) self.assertEqual(EXAMPLE['type'], sot.type) diff --git a/openstack/tests/unit/telemetry/v2/test_alarm_change.py b/openstack/tests/unit/telemetry/v2/test_alarm_change.py index 008d0717..6f7a8043 100644 --- a/openstack/tests/unit/telemetry/v2/test_alarm_change.py +++ b/openstack/tests/unit/telemetry/v2/test_alarm_change.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +import datetime + import mock import testtools @@ -22,7 +24,7 @@ EXAMPLE = { 'event_id': IDENTIFIER, 'on_behalf_of': '3', 'project_id': '4', - 'timestamp': '5', + 'timestamp': '2015-03-09T12:15:57.233772', 'type': '6', 'user_id': '7', } @@ -50,7 +52,9 @@ class TestAlarmChange(testtools.TestCase): self.assertEqual(IDENTIFIER, sot.event_id) self.assertEqual(EXAMPLE['on_behalf_of'], sot.on_behalf_of_id) self.assertEqual(EXAMPLE['project_id'], sot.project_id) - self.assertEqual(EXAMPLE['timestamp'], sot.triggered_at) + dt = datetime.datetime(2015, 3, 9, 12, 15, 57, 233772).replace( + tzinfo=None) + self.assertEqual(dt, sot.triggered_at.replace(tzinfo=None)) self.assertEqual(EXAMPLE['type'], sot.type) self.assertEqual(EXAMPLE['user_id'], sot.user_id) @@ -69,6 +73,8 @@ class TestAlarmChange(testtools.TestCase): self.assertEqual(IDENTIFIER, first.event_id) self.assertEqual(EXAMPLE['on_behalf_of'], first.on_behalf_of_id) self.assertEqual(EXAMPLE['project_id'], first.project_id) - self.assertEqual(EXAMPLE['timestamp'], first.triggered_at) + dt = datetime.datetime(2015, 3, 9, 12, 15, 57, 233772).replace( + tzinfo=None) + self.assertEqual(dt, first.triggered_at.replace(tzinfo=None)) self.assertEqual(EXAMPLE['type'], first.type) self.assertEqual(EXAMPLE['user_id'], first.user_id) diff --git a/openstack/tests/unit/telemetry/v2/test_capability.py b/openstack/tests/unit/telemetry/v2/test_capability.py index 7cc0e1c2..a9b35605 100644 --- a/openstack/tests/unit/telemetry/v2/test_capability.py +++ b/openstack/tests/unit/telemetry/v2/test_capability.py @@ -46,7 +46,7 @@ class TestCapability(testtools.TestCase): def test_make_it(self): sot = capability.Capability(EXAMPLE) self.assertEqual(EXAMPLE['id'], sot.id) - self.assertEqual(EXAMPLE['enabled'], sot.enabled) + self.assertEqual(EXAMPLE['enabled'], sot.is_enabled) def test_list(self): sess = mock.Mock() @@ -59,12 +59,12 @@ class TestCapability(testtools.TestCase): caps = sorted(caps, key=lambda cap: cap.id) self.assertEqual(5, len(caps)) self.assertEqual('alarms:history:query:simple', caps[0].id) - self.assertTrue(caps[0].enabled) + self.assertTrue(caps[0].is_enabled) self.assertEqual('alarms:query:simple', caps[1].id) - self.assertTrue(caps[1].enabled) + self.assertTrue(caps[1].is_enabled) self.assertEqual('events:query:simple', caps[2].id) - self.assertTrue(caps[2].enabled) + self.assertTrue(caps[2].is_enabled) self.assertEqual('resources:query:simple', caps[3].id) - self.assertTrue(caps[3].enabled) + self.assertTrue(caps[3].is_enabled) self.assertEqual('statistics:query:complex', caps[4].id) - self.assertFalse(caps[4].enabled) + self.assertFalse(caps[4].is_enabled) diff --git a/openstack/tests/unit/telemetry/v2/test_resource.py b/openstack/tests/unit/telemetry/v2/test_resource.py index 80ecd3d3..522d62ff 100644 --- a/openstack/tests/unit/telemetry/v2/test_resource.py +++ b/openstack/tests/unit/telemetry/v2/test_resource.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +import datetime + import testtools from openstack.telemetry.v2 import resource @@ -19,8 +21,8 @@ LINKS = [{'href': 'first_uri', 'rel': 'label 1', }, {'href': 'other_uri', 'rel': 'label', }, ] EXAMPLE = { 'resource_id': IDENTIFIER, - 'first_sample_timestamp': '1', - 'last_sample_timestamp': '2', + 'first_sample_timestamp': '2015-03-09T12:15:57.233772', + 'last_sample_timestamp': '2015-03-09T12:15:57.233772', 'links': LINKS, 'metadata': {'name_one': '1', 'name_two': '2', }, 'project_id': '123', @@ -46,10 +48,12 @@ class TestResource(testtools.TestCase): def test_make_it(self): sot = resource.Resource(EXAMPLE) self.assertEqual(EXAMPLE['resource_id'], sot.id) - self.assertEqual(EXAMPLE['first_sample_timestamp'], - sot.first_sample_at) - self.assertEqual(EXAMPLE['last_sample_timestamp'], - sot.last_sample_at) + dt = datetime.datetime(2015, 3, 9, 12, 15, 57, 233772).replace( + tzinfo=None) + self.assertEqual(dt, sot.first_sample_at.replace(tzinfo=None)) + dt = datetime.datetime(2015, 3, 9, 12, 15, 57, 233772).replace( + tzinfo=None) + self.assertEqual(dt, sot.last_sample_at.replace(tzinfo=None)) self.assertEqual(EXAMPLE['links'], sot.links) self.assertEqual(EXAMPLE['metadata'], sot.metadata) self.assertEqual(EXAMPLE['project_id'], sot.project_id) diff --git a/openstack/tests/unit/telemetry/v2/test_sample.py b/openstack/tests/unit/telemetry/v2/test_sample.py index fbfdddad..c830e7a8 100644 --- a/openstack/tests/unit/telemetry/v2/test_sample.py +++ b/openstack/tests/unit/telemetry/v2/test_sample.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +import datetime + import mock import testtools @@ -20,10 +22,10 @@ SAMPLE = { 'metadata': {'1': 'one'}, 'counter_name': '2', 'project_id': '3', - 'recorded_at': '4', + 'recorded_at': '2015-03-09T12:15:57.233772', 'resource_id': '5', 'source': '6', - 'timestamp': '7', + 'timestamp': '2015-03-09T12:15:57.233772', 'type': '8', 'unit': '9', 'user_id': '10', @@ -37,11 +39,11 @@ OLD_SAMPLE = { 'counter_volume': '4', 'message_id': '0', 'project_id': '5', - 'recorded_at': '6', + 'recorded_at': '2015-03-09T12:15:57.233772', 'resource_id': '7', 'resource_metadata': '8', 'source': '9', - 'timestamp': '10', + 'timestamp': '2015-03-09T12:15:57.233772', 'user_id': '11', } @@ -66,10 +68,14 @@ class TestSample(testtools.TestCase): self.assertEqual(SAMPLE['metadata'], sot.metadata) self.assertEqual(SAMPLE['counter_name'], sot.counter_name) self.assertEqual(SAMPLE['project_id'], sot.project_id) - self.assertEqual(SAMPLE['recorded_at'], sot.recorded_at) + dt = datetime.datetime(2015, 3, 9, 12, 15, 57, 233772).replace( + tzinfo=None) + self.assertEqual(dt, sot.recorded_at.replace(tzinfo=None)) self.assertEqual(SAMPLE['resource_id'], sot.resource_id) self.assertEqual(SAMPLE['source'], sot.source) - self.assertEqual(SAMPLE['timestamp'], sot.generated_at) + dt = datetime.datetime(2015, 3, 9, 12, 15, 57, 233772).replace( + tzinfo=None) + self.assertEqual(dt, sot.generated_at.replace(tzinfo=None)) self.assertEqual(SAMPLE['type'], sot.type) self.assertEqual(SAMPLE['unit'], sot.unit) self.assertEqual(SAMPLE['user_id'], sot.user_id) @@ -83,11 +89,15 @@ class TestSample(testtools.TestCase): self.assertEqual(OLD_SAMPLE['counter_unit'], sot.unit) self.assertEqual(OLD_SAMPLE['counter_volume'], sot.volume) self.assertEqual(OLD_SAMPLE['project_id'], sot.project_id) - self.assertEqual(OLD_SAMPLE['recorded_at'], sot.recorded_at) + dt = datetime.datetime(2015, 3, 9, 12, 15, 57, 233772).replace( + tzinfo=None) + self.assertEqual(dt, sot.recorded_at.replace(tzinfo=None)) self.assertEqual(OLD_SAMPLE['resource_id'], sot.resource_id) self.assertEqual(OLD_SAMPLE['resource_metadata'], sot.metadata) self.assertEqual(OLD_SAMPLE['source'], sot.source) - self.assertEqual(OLD_SAMPLE['timestamp'], sot.generated_at) + dt = datetime.datetime(2015, 3, 9, 12, 15, 57, 233772).replace( + tzinfo=None) + self.assertEqual(dt, sot.generated_at.replace(tzinfo=None)) self.assertEqual(OLD_SAMPLE['user_id'], sot.user_id) def test_list(self): @@ -103,10 +113,14 @@ class TestSample(testtools.TestCase): self.assertEqual(SAMPLE['metadata'], first.metadata) self.assertEqual(SAMPLE['counter_name'], first.counter_name) self.assertEqual(SAMPLE['project_id'], first.project_id) - self.assertEqual(SAMPLE['recorded_at'], first.recorded_at) + dt = datetime.datetime(2015, 3, 9, 12, 15, 57, 233772).replace( + tzinfo=None) + self.assertEqual(dt, first.recorded_at.replace(tzinfo=None)) self.assertEqual(SAMPLE['resource_id'], first.resource_id) self.assertEqual(SAMPLE['source'], first.source) - self.assertEqual(SAMPLE['timestamp'], first.generated_at) + dt = datetime.datetime(2015, 3, 9, 12, 15, 57, 233772).replace( + tzinfo=None) + self.assertEqual(dt, first.generated_at.replace(tzinfo=None)) self.assertEqual(SAMPLE['type'], first.type) self.assertEqual(SAMPLE['unit'], first.unit) self.assertEqual(SAMPLE['user_id'], first.user_id) diff --git a/openstack/tests/unit/telemetry/v2/test_statistics.py b/openstack/tests/unit/telemetry/v2/test_statistics.py index f869ff6b..2f868347 100644 --- a/openstack/tests/unit/telemetry/v2/test_statistics.py +++ b/openstack/tests/unit/telemetry/v2/test_statistics.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +import datetime + import mock import testtools @@ -20,14 +22,14 @@ EXAMPLE = { 'avg': '2', 'count': '3', 'duration': '4', - 'duration_end': '5', - 'duration_start': '6', + 'duration_end': '2015-03-09T12:45:00.000000', + 'duration_start': '2015-03-09T12:15:00.000000', 'groupby': '7', 'max': '8', 'min': '9', 'period': '10', - 'period_end': '11', - 'period_start': '12', + 'period_end': '2015-03-09T12:45:00.000000', + 'period_start': '2015-03-09T12:15:00.000000', 'sum': '13', 'unit': '14', } @@ -54,14 +56,22 @@ class TestStatistics(testtools.TestCase): self.assertEqual(EXAMPLE['avg'], sot.avg) self.assertEqual(EXAMPLE['count'], sot.count) self.assertEqual(EXAMPLE['duration'], sot.duration) - self.assertEqual(EXAMPLE['duration_end'], sot.duration_end) - self.assertEqual(EXAMPLE['duration_start'], sot.duration_start) + dt = datetime.datetime(2015, 3, 9, 12, 45, 00, 000000).replace( + tzinfo=None) + self.assertEqual(dt, sot.duration_end_at.replace(tzinfo=None)) + dt = datetime.datetime(2015, 3, 9, 12, 15, 00, 000000).replace( + tzinfo=None) + self.assertEqual(dt, sot.duration_start_at.replace(tzinfo=None)) self.assertEqual(EXAMPLE['groupby'], sot.group_by) self.assertEqual(EXAMPLE['max'], sot.max) self.assertEqual(EXAMPLE['min'], sot.min) self.assertEqual(EXAMPLE['period'], sot.period) - self.assertEqual(EXAMPLE['period_end'], sot.period_end) - self.assertEqual(EXAMPLE['period_start'], sot.period_start) + dt = datetime.datetime(2015, 3, 9, 12, 45, 00, 000000).replace( + tzinfo=None) + self.assertEqual(dt, sot.period_end_at.replace(tzinfo=None)) + dt = datetime.datetime(2015, 3, 9, 12, 15, 00, 000000).replace( + tzinfo=None) + self.assertEqual(dt, sot.period_start_at.replace(tzinfo=None)) self.assertEqual(EXAMPLE['sum'], sot.sum) self.assertEqual(EXAMPLE['unit'], sot.unit)