Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
74 changes: 74 additions & 0 deletions 74 samples/refresh_tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
####
# This script demonstrates how to use the Tableau Server Client
# to query extract refresh tasks and run them as needed.
#
# To run the script, you must have installed Python 2.7.X or 3.3 and later.
####

import argparse
import getpass
import logging

import tableauserverclient as TSC


def handle_run(server, args):
task = server.tasks.get_by_id(args.id)
print(server.tasks.run(task))


def handle_list(server, _):
tasks, pagination = server.tasks.get()
for task in tasks:
print("{}".format(task))


def handle_info(server, args):
task = server.tasks.get_by_id(args.id)
print("{}".format(task))


def main():
parser = argparse.ArgumentParser(description='Get all of the refresh tasks available on a server')
parser.add_argument('--server', '-s', required=True, help='server address')
parser.add_argument('--username', '-u', required=True, help='username to sign into server')
parser.add_argument('--site', '-S', default=None)
parser.add_argument('-p', default=None)

parser.add_argument('--logging-level', '-l', choices=['debug', 'info', 'error'], default='error',
help='desired logging level (set to error by default)')

subcommands = parser.add_subparsers()

list_arguments = subcommands.add_parser('list')
list_arguments.set_defaults(func=handle_list)

run_arguments = subcommands.add_parser('run')
run_arguments.add_argument('id', default=None)
run_arguments.set_defaults(func=handle_run)

info_arguments = subcommands.add_parser('info')
info_arguments.add_argument('id', default=None)
info_arguments.set_defaults(func=handle_info)

args = parser.parse_args()

if args.p is None:
password = getpass.getpass("Password: ")
else:
password = args.p

# Set logging level based on user input, or error by default
logging_level = getattr(logging, args.logging_level.upper())
logging.basicConfig(level=logging_level)

# SIGN IN
tableau_auth = TSC.TableauAuth(args.username, password, args.site)
server = TSC.Server(args.server)
server.version = '2.6'
with server.auth.sign_in(tableau_auth):
args.func(server, args)


if __name__ == '__main__':
main()
2 changes: 1 addition & 1 deletion 2 tableauserverclient/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from .models import ConnectionCredentials, ConnectionItem, DatasourceItem,\
GroupItem, PaginationItem, ProjectItem, ScheduleItem, \
SiteItem, TableauAuth, UserItem, ViewItem, WorkbookItem, UnpopulatedPropertyError, \
HourlyInterval, DailyInterval, WeeklyInterval, MonthlyInterval, IntervalItem
HourlyInterval, DailyInterval, WeeklyInterval, MonthlyInterval, IntervalItem, TaskItem
from .server import RequestOptions, ImageRequestOptions, Filter, Sort, Server, ServerResponseError,\
MissingRequiredFieldError, NotSignedInError, Pager

Expand Down
1 change: 1 addition & 0 deletions 1 tableauserverclient/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from .server_info_item import ServerInfoItem
from .site_item import SiteItem
from .tableau_auth import TableauAuth
from .task_item import TaskItem
from .user_item import UserItem
from .view_item import ViewItem
from .workbook_item import WorkbookItem
21 changes: 14 additions & 7 deletions 21 tableauserverclient/models/schedule_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ def __init__(self, name, priority, schedule_type, execution_order, interval_item
self.priority = priority
self.schedule_type = schedule_type

def __repr__(self):
return "<Schedule#{_id} \"{_name}\" {interval_item}>".format(**self.__dict__)

@property
def created_at(self):
return self._created_at
Expand Down Expand Up @@ -106,7 +109,7 @@ def _parse_common_tags(self, schedule_xml):
(_, name, _, _, updated_at, _, next_run_at, end_schedule_at, execution_order,
priority, interval_item) = self._parse_element(schedule_xml)

self._set_values(id=None,
self._set_values(id_=None,
name=name,
state=None,
created_at=None,
Expand All @@ -120,10 +123,10 @@ def _parse_common_tags(self, schedule_xml):

return self

def _set_values(self, id, name, state, created_at, updated_at, schedule_type,
def _set_values(self, id_, name, state, created_at, updated_at, schedule_type,
next_run_at, end_schedule_at, execution_order, priority, interval_item):
if id is not None:
self._id = id
if id_ is not None:
self._id = id_
if name:
self._name = name
if state:
Expand All @@ -147,16 +150,20 @@ def _set_values(self, id, name, state, created_at, updated_at, schedule_type,

@classmethod
def from_response(cls, resp):
all_schedule_items = []
parsed_response = ET.fromstring(resp)
return cls.from_element(parsed_response)

@classmethod
def from_element(cls, parsed_response):
all_schedule_items = []
all_schedule_xml = parsed_response.findall('.//t:schedule', namespaces=NAMESPACE)
for schedule_xml in all_schedule_xml:
(id, name, state, created_at, updated_at, schedule_type, next_run_at,
(id_, name, state, created_at, updated_at, schedule_type, next_run_at,
end_schedule_at, execution_order, priority, interval_item) = cls._parse_element(schedule_xml)

schedule_item = cls(name, priority, schedule_type, execution_order, interval_item)

schedule_item._set_values(id=id,
schedule_item._set_values(id_=id_,
name=None,
state=state,
created_at=created_at,
Expand Down
38 changes: 38 additions & 0 deletions 38 tableauserverclient/models/task_item.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import xml.etree.ElementTree as ET
from .. import NAMESPACE
from .schedule_item import ScheduleItem


class TaskItem(object):
def __init__(self, id_, task_type, priority, consecutive_failed_count=0, schedule_id=None):
self.id = id_
self.task_type = task_type
self.priority = priority
self.consecutive_failed_count = consecutive_failed_count
self.schedule_id = schedule_id

def __repr__(self):
return "<Task#{id} {task_type} pri({priority}) failed({consecutive_failed_count}) schedule_id({" \
"schedule_id})>".format(**self.__dict__)

@classmethod
def from_response(cls, xml):
parsed_response = ET.fromstring(xml)
all_tasks_xml = parsed_response.findall(
'.//t:task/t:extractRefresh', namespaces=NAMESPACE)

all_tasks = (TaskItem._parse_element(x) for x in all_tasks_xml)

return list(all_tasks)

@classmethod
def _parse_element(cls, element):
schedule = None
schedule_element = element.find('.//t:schedule', namespaces=NAMESPACE)
if schedule_element is not None:
schedule = schedule_element.get('id', None)
task_type = element.get('type', None)
priority = int(element.get('priority', -1))
consecutive_failed_count = int(element.get('consecutiveFailedCount', 0))
id_ = element.get('id', None)
return cls(id_, task_type, priority, consecutive_failed_count, schedule)
2 changes: 1 addition & 1 deletion 2 tableauserverclient/server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from .sort import Sort
from .. import ConnectionItem, DatasourceItem,\
GroupItem, PaginationItem, ProjectItem, ScheduleItem, SiteItem, TableauAuth,\
UserItem, ViewItem, WorkbookItem, NAMESPACE
UserItem, ViewItem, WorkbookItem, TaskItem, NAMESPACE
from .endpoint import Auth, Datasources, Endpoint, Groups, Projects, Schedules, \
Sites, Users, Views, Workbooks, ServerResponseError, MissingRequiredFieldError
from .server import Server
Expand Down
1 change: 1 addition & 0 deletions 1 tableauserverclient/server/endpoint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from .schedules_endpoint import Schedules
from .server_info_endpoint import ServerInfo
from .sites_endpoint import Sites
from .tasks_endpoint import Tasks
from .users_endpoint import Users
from .views_endpoint import Views
from .workbooks_endpoint import Workbooks
42 changes: 42 additions & 0 deletions 42 tableauserverclient/server/endpoint/tasks_endpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from .endpoint import Endpoint
from .exceptions import MissingRequiredFieldError
from .. import TaskItem, PaginationItem, RequestFactory
import logging

logger = logging.getLogger('tableau.endpoint.tasks')


class Tasks(Endpoint):
@property
def baseurl(self):
return "{0}/sites/{1}/tasks/extractRefreshes".format(self.parent_srv.baseurl,
self.parent_srv.site_id)

def get(self, req_options=None):
logger.info('Querying all tasks for the site')
url = self.baseurl
server_response = self.get_request(url, req_options)

pagination_item = PaginationItem.from_response(server_response.content)
all_extract_tasks = TaskItem.from_response(server_response.content)
return all_extract_tasks, pagination_item

def get_by_id(self, task_id):
if not task_id:
error = "No Task ID provided"
raise ValueError(error)
logger.info("Querying a single task by id ({})".format(task_id))
url = "{}/{}".format(self.baseurl, task_id)
server_response = self.get_request(url)
return TaskItem.from_response(server_response.content)[0]

def run(self, task_item):
if not task_item.id:
error = "User item missing ID."
raise MissingRequiredFieldError(error)

url = "{0}/{1}/runNow".format(self.baseurl, task_item.id)
print(url)
run_req = RequestFactory.Task.run_req(task_item)
server_response = self.post_request(url, run_req)
return server_response.content
17 changes: 17 additions & 0 deletions 17 tableauserverclient/server/request_factory.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from ..datetime_helpers import format_datetime
import xml.etree.ElementTree as ET
from functools import wraps

from requests.packages.urllib3.fields import RequestField
from requests.packages.urllib3.filepost import encode_multipart_formdata
Expand All @@ -16,6 +17,14 @@ def _add_multipart(parts):
return xml_request, content_type


def _tsrequest_wrapped(func):
def wrapper(self, *args, **kwargs):
xml_request = ET.Element('tsRequest')
func(xml_request, *args, **kwargs)
return ET.tostring(xml_request)
return wrapper


class AuthRequest(object):
def signin_req(self, auth_item):
xml_request = ET.Element('tsRequest')
Expand Down Expand Up @@ -331,6 +340,13 @@ def update_req(self, connection_item):
return ET.tostring(xml_request)


class TaskRequest(object):
@_tsrequest_wrapped
def run_req(xml_request, task_item):
# Send an empty tsRequest
pass


class RequestFactory(object):
Auth = AuthRequest()
Datasource = DatasourceRequest()
Expand All @@ -341,6 +357,7 @@ class RequestFactory(object):
Schedule = ScheduleRequest()
Site = SiteRequest()
Tag = TagRequest()
Task = TaskRequest()
User = UserRequest()
Workbook = WorkbookRequest()
WorkbookConnection = WorkbookConnection()
20 changes: 20 additions & 0 deletions 20 tableauserverclient/server/request_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,23 @@ def apply_query_params(self, url):
params.append('resolution={0}'.format(self.imageresolution))

return "{0}?{1}".format(url, '&'.join(params))


class ImageRequestOptions(RequestOptionsBase):
# if 'high' isn't specified, the REST API endpoint returns an image with standard resolution
class Resolution:
High = 'high'

def __init__(self, imageresolution=None):
self.imageresolution = imageresolution

def image_resolution(self, imageresolution):
self.imageresolution = imageresolution
return self

def apply_query_params(self, url):
params = []
if self.image_resolution:
params.append('resolution={0}'.format(self.imageresolution))

return "{0}?{1}".format(url, '&'.join(params))
3 changes: 2 additions & 1 deletion 3 tableauserverclient/server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from .exceptions import NotSignedInError
from .endpoint import Sites, Views, Users, Groups, Workbooks, Datasources, Projects, Auth, \
Schedules, ServerInfo, ServerInfoEndpointNotFoundError
Schedules, ServerInfo, Tasks, ServerInfoEndpointNotFoundError

import requests

Expand Down Expand Up @@ -40,6 +40,7 @@ def __init__(self, server_address):
self.projects = Projects(self)
self.schedules = Schedules(self)
self.server_info = ServerInfo(self)
self.tasks = Tasks(self)

def add_http_options(self, options_dict):
self._http_options.update(options_dict)
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.