8000 Reviving Lee's Refresh now (#250) · rmagier1/server-client-python@ec00d79 · GitHub
[go: up one dir, main page]

Skip to content

Commit ec00d79

Browse files
author
Russell Hay
authored
Reviving Lee's Refresh now (tableau#250)
* Update Change Log * Add refresh and jobs endpoints * Fixing pycodestyle failures * Run pycodestyle on samples also * Add sample for refresh and a useful tool to get the id for datasources and workbooks * Actually checking in list.py this time * more pep8 fixes * Addressing Tyler's feedback
1 parent 87acd20 commit ec00d79

19 files changed

+226
-6
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ script:
1515
# Tests
1616
- python setup.py test
1717
# pep8 - disabled for now until we can scrub the files to make sure we pass before turning it on
18-
- pycodestyle tableauserverclient test
18+
- pycodestyle tableauserverclient test samples

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
## 0.5 (11 Sept 2017)
1+
## 0.5.1 (21 Sept 2017
2+
3+
* Fix a critical issue caused by #224 that was the result of lack of test coverage (#226)
4+
5+
## 0.5 (20 Sept 2017)
26

37
* Added revision settings to update site (#187)
48
* Added support for certified data sources (#189)

samples/create_project.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def create_project(server, project_item):
2424
print('We have already created this project: %s' % project_item.name)
2525
sys.exit(1)
2626

27+
2728
def main():
2829
parser = argparse.ArgumentParser(description='Get all of the refresh tasks available on a server')
2930
parser.add_argument('--server', '-s', required=True, help='server address')
@@ -64,5 +65,6 @@ def main():
6465
grand_child_project = TSC.ProjectItem(name='Grand Child Project', parent_id=child_project.id)
6566
grand_child_project = create_project(server, grand_child_project)
6667

68+
6769
if __name__ == '__main__':
6870
main()

samples/download_view_image.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,6 @@ def main():
6464

6565
print("View image saved to {0}".format(args.filepath))
6666

67+
6768
if __name__ == '__main__':
6869
main()

samples/explore_datasource.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,6 @@ def main():
7979
sample_datasource.tags = original_tag_set
8080
server.datasources.update(sample_datasource)
8181

82+
8283
if __name__ == '__main__':
8384
main()

samples/filter_sort_groups.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,5 +87,6 @@ def main():
8787
for group in matching_groups:
8888
print(group.name)
8989

90+
9091
if __name__ == '__main__':
9192
main()

samples/list.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
####
2+
# This script demonstrates how to list all of the workbooks or datasources
3+
#
4+
# To run the script, you must have installed Python 2.7.X or 3.3 and later.
5+
####
6+
7+
import argparse
8+
import getpass
9+
import logging
10+
11+
import tableauserverclient as TSC
12+
13+
14+
def main():
15+
parser = argparse.ArgumentParser(description='Get all of the refresh tasks available on a server')
16+
parser.add_argument('--server', '-s', required=True, help='server address')
17+
parser.add_argument('--username', '-u', required=True, help='username to sign into server')
18+
parser.add_argument('--site', '-S', default=None)
19+
parser.add_argument('-p', default=None)
20+
21+
parser.add_argument('--logging-level', '-l', choices=['debug', 'info', 'error'], default='error',
22+
help='desired logging level (set to error by default)')
23+
24+
parser.add_argument('resource_type', choices=['workbook', 'datasource'])
25+
26+
args = parser.parse_args()
27+
28+
if args.p is None:
29+
password = getpass.getpass("Password: ")
30+
else:
31+
password = args.p
32+
33+
# Set logging level based on user input, or error by default
34+
logging_level = getattr(logging, args.logging_level.upper())
35+
logging.basicConfig(level=logging_level)
36+
37+
# SIGN IN
38+
tableau_auth = TSC.TableauAuth(args.username, password, args.site)
39+
server = TSC.Server(args.server, use_server_version=True)
40+
with server.auth.sign_in(tableau_auth):
41+
endpoint = {
42+
'workbook': server.workbooks,
43+
'datasource': server.datasources
44+
}.get(args.resource_type)
45+
46+
for resource in TSC.Pager(endpoint.get):
47+
print(resource.id, resource.name)
48+
49+
50+
if __name__ == '__main__':
51+
main()

samples/refresh.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
####
2+
# This script demonstrates how to use trigger a refresh on a datasource or workbook
3+
#
4+
# To run the script, you must have installed Python 2.7.X or 3.3 and later.
5+
####
6+
7+
import argparse
8+
import getpass
9+
import logging
10+
11+
import tableauserverclient as TSC
12+
13+
14+
def main():
15+
parser = argparse.ArgumentParser(description='Get all of the refresh tasks available on a server')
16+
parser.add_argument('--server', '-s', required=True, help='server address')
17+
parser.add_argument('--username', '-u', required=True, help='username to sign into server')
18+
parser.add_argument('--site', '-S', default=None)
19+
parser.add_argument('-p', default=None)
20+
21+
parser.add_argument('--logging-level', '-l', choices=['debug', 'info', 'error'], default='error',
22+
help='desired logging level (set to error by default)')
23+
24+
parser.add_argument('resource_type', choices=['workbook', 'datasource'])
25+
parser.add_argument('resource_id')
26+
27+
args = parser.parse_args()
28+
29+
if args.p is None:
30+
password = getpass.getpass("Password: ")
31+
else:
32+
password = args.p
33+
34+
# Set logging level based on user input, or error by default
35+
logging_level = getattr(logging, args.logging_level.upper())
36+
logging.basicConfig(level=logging_level)
37+
38+
# SIGN IN
39+
tableau_auth = TSC.TableauAuth(args.username, password, args.site)
40+
server = TSC.Server(args.server, use_server_version=True)
41+
with server.auth.sign_in(tableau_auth):
42+
endpoint = {
43+
'workbook': server.workbooks,
44+
'datasource': server.datasources
45+
}.get(args.resource_type)
46+
47+
refresh_func = endpoint.refresh
48+
resource = endpoint.get_by_id(args.resource_id)
49+
50+
print(refresh_func(resource))
51+
52+
53+
if __name__ == '__main__':
54+
main()

tableauserverclient/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from .namespace import NEW_NAMESPACE as DEFAULT_NAMESPACE
22
from .models import ConnectionCredentials, ConnectionItem, DatasourceItem,\
3-
GroupItem, PaginationItem, ProjectItem, ScheduleItem, \
3+
GroupItem, JobItem, PaginationItem, ProjectItem, ScheduleItem, \
44
SiteItem, TableauAuth, UserItem, ViewItem, WorkbookItem, UnpopulatedPropertyError, \
55
HourlyInterval, DailyInterval, WeeklyInterval, MonthlyInterval, IntervalItem, TaskItem, \
66
SubscriptionItem

tableauserverclient/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from .exceptions import UnpopulatedPropertyError
55
from .group_item import GroupItem
66
from .interval_item import IntervalItem, DailyInterval, WeeklyInterval, MonthlyInterval, HourlyInterval
7+
from .job_item import JobItem
78
from .pagination_item import PaginationItem
89
from .project_item import ProjectItem
910
from .schedule_item import ScheduleItem
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff lin F438 e change
@@ -0,0 +1,60 @@
1+
import xml.etree.ElementTree as ET
2+
from .target import Target
3+
4+
5+
class JobItem(object):
6+
def __init__(self, id_, job_type, created_at, started_at=None, completed_at=None, finish_code=0):
7+
self._id = id_
8+
self._type = job_type
9+
self._created_at = created_at
10+
self._started_at = started_at
11+
self._completed_at = completed_at
12+
self._finish_code = finish_code
13+
14+
@property
15+
def id(self):
16+
return self._id
17+
18+
@property
19+
def type(self):
20+
return self._type
21+
22+
@property
23+
def created_at(self):
24+
return self._created_at
25+
26+
@property
27+
def started_at(self):
28+
return self._started_at
29+
30+
@property
31+
def completed_at(self):
32+
return self._completed_at
33+
34+
@property
35+
def finish_code(self):
36+
return self._finish_code
37+
38+
def __repr__(self):
39+
return "<Job#{_id} {_type} created_at({_created_at}) started_at({_started_at}) completed_at({_completed_at})" \
40+
" finish_code({_finish_code})>".format(**self.__dict__)
41+
42 10000 +
@classmethod
43+
def from_response(cls, xml, ns):
44+
parsed_response = ET.fromstring(xml)
45+
all_tasks_xml = parsed_response.findall(
46+
'.//t:job', namespaces=ns)
47+
48+
all_tasks = [JobItem._parse_element(x, ns) for x in all_tasks_xml]
49+
50+
return all_tasks
51+
52+
@classmethod
53+
def _parse_element(cls, element, ns):
54+
id_ = element.get('id', None)
55+
type_ = element.get('type', None)
56+
created_at = element.get('createdAt', None)
57+
started_at = element.get('startedAt', None)
58+
completed_at = element.get('completedAt', None)
59+
finish_code = element.get('finishCode', -1)
60+
return cls(id_, type_, created_at, started_at, completed_at, finish_code)

tableauserverclient/server/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from .request_options import ImageRequestOptions, PDFRequestOptions, RequestOptions
33
from .filter import Filter
44
from .sort import Sort
5-
from .. import ConnectionItem, DatasourceItem,\
5+
from .. import ConnectionItem, DatasourceItem, JobItem, \
66
GroupItem, PaginationItem, ProjectItem, ScheduleItem, SiteItem, TableauAuth,\
77
UserItem, ViewItem, WorkbookItem, TaskItem, SubscriptionItem
88
from .endpoint import Auth, Datasources, Endpoint, Groups, Projects, Schedules, \

tableauserverclient/server/endpoint/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from .endpoint import Endpoint
44
from .exceptions import ServerResponseError, MissingRequiredFieldError, ServerInfoEndpointNotFoundError
55
from .groups_endpoint import Groups
6+
from .jobs_endpoint import Jobs
67
from .projects_endpoint import Projects
78
from .schedules_endpoint import Schedules
89
from .server_info_endpoint import ServerInfo

tableauserverclient/server/endpoint/datasources_endpoint.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from .. import RequestFactory, DatasourceItem, PaginationItem, ConnectionItem
66
from ...filesys_helpers import to_filename
77
from ...models.tag_item import TagItem
8+
from ...models.job_item import JobItem
89
import os
910
import logging
1011
import copy
@@ -128,6 +129,13 @@ def update(self, datasource_item):
128129
updated_datasource = copy.copy(datasource_item)
129130
return updated_datasource._parse_common_elements(server_response.content, self.parent_srv.namespace)
130131

132+
def refresh(self, datasource_item):
133+
url = "{0}/{1}/refresh".format(self.baseurl, datasource_item.id)
134+
empty_req = RequestFactory.Empty.empty_req()
135+
server_response = self.post_request(url, empty_req)
136+
new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
137+
return new_job
138+
131139
# Publish datasource
132140
@api(version="2.0")
133141
def publish(self, datasource_item, file_path, mode, connection_credentials=None):

tableauserverclient/server/endpoint/endpoint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
logger = logging.getLogger('tableau.endpoint')
1212

13-
Success_codes = [200, 201, 204]
13+
Success_codes = [200, 201, 202, 204]
1414

1515

1616
class Endpoint(object):
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from .endpoint import Endpoint, api
2+
from .. import JobItem
3+
import logging
4+
5+
logger = logging.getLogger('tableau.endpoint.jobs')
6+
7+
8+
class Jobs(Endpoint):
9+
@property
10+
def baseurl(self):
11+
return "{0}/sites/{1}/jobs".format(self.parent_srv.baseurl, self.parent_srv.site_id)
12+
13+
@api(version='2.6')
14+
def get(self, job_id):
15+
logger.info('Query for information about job ' + job_id)
16+
url = "{0}/{1}".format(self.baseurl, job_id)
17+
server_response = self.get_request(url)
18+
new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
19+
return new_job

tableauserverclient/server/endpoint/workbooks_endpoint.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from .resource_tagger import _ResourceTagger
55
from .. import RequestFactory, WorkbookItem, ConnectionItem, ViewItem, PaginationItem
66
from ...models.tag_item import TagItem
7+
from ...models.job_item import JobItem
78
from ...filesys_helpers import to_filename
89

910
import os
@@ -50,6 +51,14 @@ def get_by_id(self, workbook_id):
5051
server_response = self.get_request(url)
5152
return WorkbookItem.from_response(server_response.content, self.parent_srv.namespace)[0]
5253

54+
@api(version="2.8")
55+
def refresh(self, workbook_id):
56+
url = "{0}/{1}/refresh".format(self.baseurl, workbook_id)
57+
empty_req = RequestFactory.Empty.empty_req()
58+
server_response = self.post_request(url, empty_req)
59+
new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
60+
return new_job
61+
5362
# Delete 1 workbook by id
5463
@api(version="2.0")
5564
def delete(self, workbook_id):

tableauserverclient/server/request_factory.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,9 +377,16 @@ def create_req(self, subscription_item):
377377
return ET.tostring(xml_request)
378378

379379

380+
class EmptyRequest(object):
381+
@_tsrequest_wrapped
382+
def empty_req(xml_request):
383+
pass
384+
385+
380386
class RequestFactory(object):
381387
Auth = AuthRequest()
382388
Datasource = DatasourceRequest()
389+
Empty = EmptyRequest()
383390
Fileupload = FileuploadRequest()
384391
Group = GroupRequest()
385392
Permission = PermissionRequest()

tableauserverclient/server/server.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from .exceptions import NotSignedInError
44
from ..namespace import Namespace
55
from .endpoint import Sites, Views, Users, Groups, Workbooks, Datasources, Projects, Auth, \
6-
Schedules, ServerInfo, Tasks, ServerInfoEndpointNotFoundError, Subscriptions
6+
Schedules, ServerInfo, Tasks, ServerInfoEndpointNotFoundError, Subscriptions, Jobs
77

88
import requests
99

@@ -36,6 +36,7 @@ def __init__(self, server_address, use_server_version=False):
3636
self.users = Users(self)
3737
self.sites = Sites(self)
3838
self.groups = Groups(self)
39+
self.jobs = Jobs(self)
3940
self.workbooks = Workbooks(self)
4041
self.datasources = Datasources(self)
4142
self.projects = Projects(self)

0 commit comments

Comments
 (0)
0