Skip to content

Commit

Permalink
Merge pull request #316 from RussTheAerialist/master
Browse files Browse the repository at this point in the history
Merge in v0.7 from development.
  • Loading branch information
Russell Hay committed Jul 11, 2018
2 parents 5e88a41 + 02e0111 commit b7b544e
Show file tree
Hide file tree
Showing 41 changed files with 885 additions and 83 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,20 @@
## 0.7 (2 Jul 2018)

* Added cancel job (#299)
* Added Get background jobs (#298)
* Added Multi-credential support (#276)
* Added Update Groups (#279)
* Adding project_id to view (#285)
* Added ability to rename workbook using `update workbook` (#284)
* Added Sample for exporting full pdf using pdf page combining (#267)
* Added Sample for exporting data, images, and single view pdfs (#263)
* Added view filters to the populate request options (#260)
* Add Async publishing for workbook and datasource endpoints (#311)
* Fixed ability to update datasource server connection port (#283)
* Fixed next project handling (#267)
* Cleanup debugging output to strip out non-xml response
* Improved refresh sample for readability (#288)

## 0.6.1 (26 Jan 2018)

* Fixed #257 where refreshing extracts does not work due to a missing "self"
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Expand Up @@ -15,6 +15,7 @@ The following people have contributed to this project to make it possible, and w
* [William Lang](https://github.com/williamlang)
* [Jim Morris](https://github.com/jimbodriven)
* [BingoDinkus](https://github.com/BingoDinkus)
* [Sergey Sotnichenko](https://github.com/sotnich)

## Core Team

Expand Down
46 changes: 46 additions & 0 deletions docs/docs/api-ref.md
Expand Up @@ -849,6 +849,52 @@ Error | Description
<br>
<br>

#### groups.update

```py
groups.update(group_item, default_site_role=UserItem.Roles.Unlicensed)
```

Updates the group on the site.
If domain_name = 'local' then update only the name of the group.
If not - update group from the Active Directory with domain_name.

REST API: [Update Group](http://onlinehelp.tableau.com/current/api/rest_api/en-us/help.htm#REST/rest_api_ref.htm#Update_Group%3FTocPath%3DAPI%2520Reference%7C_____95){:target="_blank"}


**Parameters**

Name | Description
:--- | :---
`group_item` | the group_item specifies the group to update.
`default_site_role` | if group updates from Active Directory then this is the default role for the new users.


**Exceptions**

Error | Description
:--- | :---
`Group item missing ID` | Raises an exception if a valid `group_item.id` is not provided.


**Example**

```py
# Update a group

# import tableauserverclient as TSC
# tableau_auth = TSC.TableauAuth('USERNAME', 'PASSWORD')
# server = TSC.Server('http://SERVERURL')

with server.auth.sign_in(tableau_auth):
all_groups, pagination_item = server.groups.get()

for group in all_groups:
server.groups.update(group)
```
<br>
<br>

#### groups.get

```py
Expand Down
2 changes: 1 addition & 1 deletion samples/download_view_image.py
Expand Up @@ -43,7 +43,7 @@ def main():
tableau_auth = TSC.TableauAuth(args.username, password, site_id=site_id)
server = TSC.Server(args.server)
# The new endpoint was introduced in Version 2.5
server.version = 2.5
server.version = "2.5"

with server.auth.sign_in(tableau_auth):
# Step 2: Query for the view that we want an image of
Expand Down
74 changes: 74 additions & 0 deletions samples/export.py
@@ -0,0 +1,74 @@
import argparse
import getpass
import logging

import tableauserverclient as TSC


def main():
parser = argparse.ArgumentParser(description='Export a view as an image, pdf, or csv')
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)')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--pdf', dest='type', action='store_const', const=('populate_pdf', 'PDFRequestOptions', 'pdf',
'pdf'))
group.add_argument('--png', dest='type', action='store_const', const=('populate_image', 'ImageRequestOptions',
'image', 'png'))
group.add_argument('--csv', dest='type', action='store_const', const=('populate_csv', 'CSVRequestOptions', 'csv',
'csv'))

parser.add_argument('--file', '-f', help='filename to store the exported data')
parser.add_argument('--filter', '-vf', metavar='COLUMN:VALUE',
help='View filter to apply to the view')
parser.add_argument('resource_id', help='LUID for the view')

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)

tableau_auth = TSC.TableauAuth(args.username, password, args.site)
server = TSC.Server(args.server, use_server_version=True)
with server.auth.sign_in(tableau_auth):
views = filter(lambda x: x.id == args.resource_id,
TSC.Pager(server.views.get))
view = views.pop()

# We have a number of different types and functions for each different export type.
# We encode that information above in the const=(...) parameter to the add_argument function to make
# the code automatically adapt for the type of export the user is doing.
# We unroll that information into methods we can call, or objects we can create by using getattr()
(populate_func_name, option_factory_name, member_name, extension) = args.type
populate = getattr(server.views, populate_func_name)
option_factory = getattr(TSC, option_factory_name)

if args.filter:
options = option_factory().vf(*args.filter.split(':'))
else:
options = None
if args.file:
filename = args.file
else:
filename = 'out.{}'.format(extension)

populate(view, options)
with file(filename, 'wb') as f:
if member_name == 'csv':
f.writelines(getattr(view, member_name))
else:
f.write(getattr(view, member_name))


if __name__ == '__main__':
main()
92 changes: 92 additions & 0 deletions samples/export_wb.py
@@ -0,0 +1,92 @@
#
# This sample uses the PyPDF2 library for combining pdfs together to get the full pdf for all the views in a
# workbook.
#
# You will need to do `pip install PyPDF2` to use this sample.
#

import argparse
import getpass
import logging
import tempfile
import shutil
import functools
import os.path

import tableauserverclient as TSC
try:
import PyPDF2
except ImportError:
print('Please `pip install PyPDF2` to use this sample')
import sys
sys.exit(1)


def get_views_for_workbook(server, workbook_id): # -> Iterable of views
workbook = server.workbooks.get_by_id(workbook_id)
server.workbooks.populate_views(workbook)
return workbook.views


def download_pdf(server, tempdir, view): # -> Filename to downloaded pdf
logging.info("Exporting {}".format(view.id))
destination_filename = os.path.join(tempdir, view.id)
server.views.populate_pdf(view)
with file(destination_filename, 'wb') as f:
f.write(view.pdf)

return destination_filename


def combine_into(dest_pdf, filename): # -> None
dest_pdf.append(filename)
return dest_pdf


def cleanup(tempdir):
shutil.rmtree(tempdir)


def main():
parser = argparse.ArgumentParser(description='Export to PDF all of the views in a workbook')
parser.add_argument('--server', '-s', required=True, help='server address')
parser.add_argument('--site', '-S', default=None, help='Site to log into, do not specify for default site')
parser.add_argument('--username', '-u', required=True, help='username to sign into server')
parser.add_argument('--password', '-p', default=None, help='password for the user')

parser.add_argument('--logging-level', '-l', choices=['debug', 'info', 'error'], default='error',
help='desired logging level (set to error by default)')
parser.add_argument('--file', '-f', default='out.pdf', help='filename to store the exported data')
parser.add_argument('resource_id', help='LUID for the workbook')

args = parser.parse_args()

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

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

tempdir = tempfile.mkdtemp('tsc')
logging.debug("Saving to tempdir: %s", tempdir)

tableau_auth = TSC.TableauAuth(args.username, password, args.site)
server = TSC.Server(args.server, use_server_version=True)
try:
with server.auth.sign_in(tableau_auth):
get_list = functools.partial(get_views_for_workbook, server)
download = functools.partial(download_pdf, server, tempdir)

downloaded = (download(x) for x in get_list(args.resource_id))
output = reduce(combine_into, downloaded, PyPDF2.PdfFileMerger())
with file(args.file, 'wb') as f:
output.write(f)
finally:
cleanup(tempdir)


if __name__ == '__main__':
main()
47 changes: 47 additions & 0 deletions samples/kill_all_jobs.py
@@ -0,0 +1,47 @@
####
# This script demonstrates how to kill all of the running jobs
#
# 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 main():
parser = argparse.ArgumentParser(description='Cancel all of the running background jobs')
parser.add_argument('--server', '-s', required=True, help='server address')
parser.add_argument('--site', '-S', default=None, help='site to log into, do not specify for default site')
parser.add_argument('--username', '-u', required=True, help='username to sign into server')
parser.add_argument('--password', '-p', default=None, help='password for the user')

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

args = parser.parse_args()

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

# 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, use_server_version=True)
with server.auth.sign_in(tableau_auth):
req = TSC.RequestOptions()

req.filter.add(TSC.Filter("progress", TSC.RequestOptions.Operator.LessThanOrEqual, 0))
for job in TSC.Pager(server.jobs, request_opts=req):
print(server.jobs.cancel(job.id), job.id, job.status, job.type)


if __name__ == '__main__':
main()
17 changes: 10 additions & 7 deletions samples/list.py
Expand Up @@ -12,23 +12,23 @@


def main():
parser = argparse.ArgumentParser(description='Get all of the refresh tasks available on a server')
parser = argparse.ArgumentParser(description='List out the names and LUIDs for different resource types')
parser.add_argument('--server', '-s', required=True, help='server address')
parser.add_argument('--site', '-S', default=None, help='site to log into, do not specify for default site')
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('--password', '-p', default=None, help='password for the user')

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

parser.add_argument('resource_type', choices=['workbook', 'datasource'])
parser.add_argument('resource_type', choices=['workbook', 'datasource', 'project', 'view', 'job'])

args = parser.parse_args()

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

# Set logging level based on user input, or error by default
logging_level = getattr(logging, args.logging_level.upper())
Expand All @@ -40,7 +40,10 @@ def main():
with server.auth.sign_in(tableau_auth):
endpoint = {
'workbook': server.workbooks,
'datasource': server.datasources
'datasource': server.datasources,
'view': server.views,
'job': server.jobs,
'project': server.projects,
}.get(args.resource_type)

for resource in TSC.Pager(endpoint.get):
Expand Down
20 changes: 18 additions & 2 deletions samples/publish_workbook.py
Expand Up @@ -19,6 +19,7 @@
import logging

import tableauserverclient as TSC
from tableauserverclient import ConnectionCredentials, ConnectionItem


def main():
Expand Down Expand Up @@ -51,14 +52,29 @@ def main():
all_projects, pagination_item = server.projects.get()
default_project = next((project for project in all_projects if project.is_default()), None)

connection1 = ConnectionItem()
connection1.server_address = "mssql.test.com"
connection1.connection_credentials = ConnectionCredentials("test", "password", True)

connection2 = ConnectionItem()
connection2.server_address = "postgres.test.com"
connection2.server_port = "5432"
connection2.connection_credentials = ConnectionCredentials("test", "password", True)

all_connections = list()
all_connections.append(connection1)
all_connections.append(connection2)

# Step 3: If default project is found, form a new workbook item and publish.
if default_project is not None:
new_workbook = TSC.WorkbookItem(default_project.id)
if args.as_job:
new_job = server.workbooks.publish(new_workbook, args.filepath, overwrite_true, as_job=args.as_job)
new_job = server.workbooks.publish(new_workbook, args.filepath, overwrite_true,
connections=all_connections, as_job=args.as_job)
print("Workbook published. JOB ID: {0}".format(new_job.id))
else:
new_workbook = server.workbooks.publish(new_workbook, args.filepath, overwrite_true, as_job=args.as_job)
new_workbook = server.workbooks.publish(new_workbook, args.filepath, overwrite_true,
connections=all_connections, as_job=args.as_job)
print("Workbook published. ID: {0}".format(new_workbook.id))
else:
error = "The default project could not be found."
Expand Down

0 comments on commit b7b544e

Please sign in to comment.