8000 Initial implementation to address #102 and provide datetime objects · ajbosco/server-client-python@cc8d544 · GitHub
[go: up one dir, main page]

Skip to content

Commit cc8d544

Browse files
author
Russell Hay
committed
Initial implementation to address tableau#102 and provide datetime objects
1 parent 75a3623 commit cc8d544

File tree

6 files changed

+65
-10
lines changed

6 files changed

+65
-10
lines changed

tableauserverclient/datetime_helpers.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import datetime
22

3-
43
# This code below is from the python documentation for tzinfo: https://docs.python.org/2.3/lib/datetime-tzinfo.html
54
ZERO = datetime.timedelta(0)
65
HOUR = datetime.timedelta(hours=1)

tableauserverclient/models/datasource_item.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import xml.etree.ElementTree as ET
22
from .exceptions import UnpopulatedPropertyError
3-
from .property_decorators import property_not_nullable
3+
from .property_decorators import property_not_nullable, property_is_datetime
44
from .tag_item import TagItem
55
from .. import NAMESPACE
66
from ..datetime_helpers import parse_datetime
@@ -35,6 +35,11 @@ def content_url(self):
3535
def created_at(self):
3636
return self._created_at
3737

38+
@created_at.setter
39+
@property_is_datetime
40+
def created_at(self, value):
41+
self._created_at = value
42+
3843
@property
3944
def id(self):
4045
return self._id

tableauserverclient/models/schedule_item.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from datetime import datetime
33

44
from .interval_item import IntervalItem, HourlyInterval, DailyInterval, WeeklyInterval, MonthlyInterval
5-
from .property_decorators import property_is_enum, property_not_nullable, property_is_int
5+
from .property_decorators import property_is_enum, property_not_nullable, property_is_int, property_is_datetime
66
from .. import NAMESPACE
77
from ..datetime_helpers import parse_datetime
88

@@ -37,6 +37,11 @@ def __init__(self, name, priority, schedule_type, execution_order, interval_item
3737
def created_at(self):
3838
return self._created_at
3939

40+
@created_at.setter
41+
@property_is_datetime
42+
def created_at(self, value):
43+
self._created_at = value
44+
4045
@property
4146
def end_schedule_at(self):
4247
return self._end_schedule_at
@@ -99,6 +104,11 @@ def state(self, value):
99104
def updated_at(self):
100105
return self._updated_at
101106

107+
@updated_at.setter
108+
@property_is_datetime
109+
def updated_at(self, value):
110+
self._updated_at = value
111+
102112
def _parse_common_tags(self, schedule_xml):
103113
if not isinstance(schedule_xml, ET.Element):
104114
schedule_xml = ET.fromstring(schedule_xml).find('.//t:schedule', namespaces=NAMESPACE)

tableauserverclient/models/workbook_item.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import xml.etree.ElementTree as ET
22
from .exceptions import UnpopulatedPropertyError
3-
from .property_decorators import property_not_nullable, property_is_boolean
3+
from .property_decorators import property_not_nullable, property_is_boolean, property_is_datetime
44
from .tag_item import TagItem
55
from .view_item import ViewItem
66
from .. import NAMESPACE
@@ -41,6 +41,11 @@ def content_url(self):
4141
def created_at(self):
4242
return self._created_at
4343

44+
@created_at.setter
45+
@property_is_datetime
46+
def created_at(self, value):
47+
self._created_at = value
48+
4449
@property
4550
def id(self):
4651
return self._id
@@ -82,6 +87,11 @@ def size(self):
8287
def updated_at(self):
8388
return self._updated_at
8489

90+
@updated_at.setter
91+
@property_is_datetime
92+
def updated_at(self, value):
93+
self._updated_at = value
94+
8595
@property
8696
def views(self):
8797
if self._views is None:

test/test_datasource_model.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,34 @@ def test_invalid_project_id(self):
99
datasource = TSC.DatasourceItem("10")
1010
with self.assertRaises(ValueError):
1111
datasource.project_id = None
12+
13+
def test_datetime_conversion(self):
14+
datasource = TSC.DatasourceItem("10")
15+
datasource.created_at = "2016-08-18T19:25:36Z"
16+
actual = datasource.created_at
17+
self.assertIsInstance(actual, datetime.datetime)
18+
self.assertEquals(actual.year, 2016)
19+
self.assertEquals(actual.month, 8)
20+
self.assertEquals(actual.day, 18)
21+
self.assertEquals(actual.hour, 19)
22+
self.assertEquals(actual.minute, 25)
23+
self.assertEquals(actual.second, 36)
24+
25+
def test_datetime_conversion_allows_datetime_passthrough(self):
26+
datasource = TSC.DatasourceItem("10")
27+
now = datetime.datetime.utcnow()
28+
datasource.created_at = now
29+
self.assertEquals(datasource.created_at, now)
30+
31+
def test_datetime_conversion_is_timezone_aware(self):
32+
datasource = TSC.DatasourceItem("10")
33+
datasource.created_at = "2016-08-18T19:25:36Z"
34+
actual = datasource.created_at
35+
self.assertEquals(actual.utcoffset().seconds, 0)
36+
37+
def test_datetime_conversion_rejects_things_that_cannot_be_converted(self):
38+
datasource = TSC.DatasourceItem("10")
39+
with self.assertRaises(ValueError):
40+
datasource.created_at = object()
41+
with self.assertRaises(ValueError):
42+
datasource.created_at = "This is so not a datetime"

test/test_requests.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ def test_make_get_request(self):
2828
auth_token='j80k54ll2lfMZ0tv97mlPvvSCRyD0DOM',
2929
content_type='text/xml')
3030

31-
self.assertEquals(resp.request.query, 'pagenumber=13&pagesize=13')
32-
self.assertEquals(resp.request.headers['x-tableau-auth'], 'j80k54ll2lfMZ0tv97mlPvvSCRyD0DOM')
33-
self.assertEquals(resp.request.headers['content-type'], 'text/xml')
31+
self.assertEqual(resp.request.query, 'pagenumber=13&pagesize=13')
32+
self.assertEqual(resp.request.headers['x-tableau-auth'], 'j80k54ll2lfMZ0tv97mlPvvSCRyD0DOM')
33+
self.assertEqual(resp.request.headers['content-type'], 'text/xml')
3434

3535
def test_make_post_request(self):
3636
with requests_mock.mock() as m:
@@ -42,6 +42,6 @@ def test_make_post_request(self):
4242
request_object=None,
4343
auth_token='j80k54ll2lfMZ0tv97mlPvvSCRyD0DOM',
4444
content_type='multipart/mixed')
45-
self.assertEquals(resp.request.headers['x-tableau-auth'], 'j80k54ll2lfMZ0tv97mlPvvSCRyD0DOM')
46-
self.assertEquals(resp.request.headers['content-type'], 'multipart/mixed')
47-
self.assertEquals(resp.request.body, b'1337')
45+
self.assertEqual(resp.request.headers['x-tableau-auth'], 'j80k54ll2lfMZ0tv97mlPvvSCRyD0DOM')
46+
self.assertEqual(resp.request.headers['content-type'], 'multipart/mixed')
47+
self.assertEqual(resp.request.body, b'1337')

0 commit comments

Comments
 (0)
0