8000 Development by rshide · Pull Request #1 · rshide/server-client-python · GitHub
[go: up one dir, main page]

Skip to content

Development #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Aug 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ python:
- "3.5"
- "3.6"
- "3.7"
- "3.8"
# command to install dependencies
install:
- "pip install -e ."
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Tableau Server Client (Python)
[![Tableau Supported](https://img.shields.io/badge/Support%20Level-Tableau%20Supported-53bd92.svg)](https://www.tableau.com/support-levels-it-and-developer-tools)

[![Tableau Supported](https://img.shields.io/badge/Support%20Level-Tableau%20Supported-53bd92.svg)](https://www.tableau.com/support-levels-it-and-developer-tools) [![Build Status](https://travis-ci.org/tableau/server-client-python.svg?branch=master)](https://travis-ci.org/tableau/server-client-python)

Use the Tableau Server Client (TSC) library to increase your productivity as you interact with the Tableau Server REST API. With the TSC library you can do almost everything that you can do with the REST API, including:

* Publish workbooks and data sources.
* Create users and groups.
* Query projects, sites, and more.

This repository contains Python source code and sample files.
This repository contains Python source code and sample files. Python versions 3.5 and up are supported.

For more information on installing and using TSC, see the documentation:

<https://tableau.github.io/server-client-python/docs/>
27 changes: 16 additions & 11 deletions contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ a feature do not require the CLA.

## Issues and Feature Requests

To submit an issue/bug report, or to request a feature, please submit a [github issue](https://github.com/tableau/server-client-python/issues) to the repo.
To submit an issue/bug report, or to request a feature, please submit a [GitHub issue](https://github.com/tableau/server-client-python/issues) to the repo.

If you are submitting a bug report, please provide as much information as you can, including clear and concise repro steps, attaching any necessary
files to assist in the repro. **Be sure to scrub the files of any potentially sensitive information. Issues are public.**
Expand Down Expand Up @@ -48,19 +48,24 @@ anyone can add to an issue:
## Fixes, Implementations, and Documentation

For all other things, please submit a PR that includes the fix, documentation, or new code that you are trying to contribute. More information on
creating a PR can be found in the [Development Guide](https://tableau.github.io/server-client-python/docs/dev-guide)
creating a PR can be found in the [Development Guide](https://tableau.github.io/server-client-python/docs/dev-guide).

If the feature is complex or has multiple solutions that could be equally appropriate approaches, it would be helpful to file an issue to discuss the
design trade-offs of each solution before implementing, to allow us to collectively arrive at the best solution, which most likely exists in the middle
somewhere.


## Getting Started
> pip install versioneer
> python setup.py build
> python setup.py test
>

### before committing
Our CI runs include a python lint run, so you should run this locally and fix complaints before committing as this will fail your checkin
> pycodestyle tableauserverclient test samples

```shell
pip install versioneer
python setup.py build
python setup.py test
```

### Before Committing

Our CI runs include a Python lint run, so you should run this locally and fix complaints before committing as this will fail your checkin.

```shell
pycodestyle tableauserverclient test samples
```
2 changes: 1 addition & 1 deletion samples/add_default_permission.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@


def main():
parser = argparse.ArgumentParser(description='Add workbook default permission for a given project')
parser = argparse.ArgumentParser(description='Add workbook default permissions for a given project.')
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, help='Site to sign into - default site if not provided')
Expand Down
4 changes: 2 additions & 2 deletions samples/create_group.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
####
# This script demonstrates how to create groups using the Tableau
# This script demonstrates how to create a group using the Tableau
# Server Client.
#
# To run the script, you must have installed Python 3.5 or later.
Expand All @@ -17,7 +17,7 @@

def main():

parser = argparse.ArgumentParser(description='Creates sample schedules for each type of frequency.')
parser = argparse.ArgumentParser(description='Creates a sample user group.')
pa 628C rser.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('--logging-level', '-l', choices=['debug', 'info', 'error'], default='error',
Expand Down
2 changes: 1 addition & 1 deletion samples/create_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def create_project(server, project_item):


def main():
parser = argparse.ArgumentParser(description='Get all of the refresh tasks available on a server')
parser = argparse.ArgumentParser(description='Create new projects.')
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)
Expand Down
2 changes: 1 addition & 1 deletion samples/download_view_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

def main():

parser = argparse.ArgumentParser(description='Query View Image From Server')
parser = argparse.ArgumentParser(description='Download image of a specified view.')
parser.add_argument('--server', '-s', required=True, help='server address')
parser.add_argument('--site-id', '-si', required=False,
help='content url for site the view is on')
Expand Down
9 changes: 8 additions & 1 deletion samples/export.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
####
# This script demonstrates how to export a view using the Tableau
# Server Client.
#
# To run the script, you must have installed Python 3.5 or later.
####

import argparse
import getpass
import logging
Expand All @@ -6,7 +13,7 @@


def main():
parser = argparse.ArgumentParser(description='Export a view as an image, pdf, or csv')
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)
Expand Down
7 changes: 5 additions & 2 deletions samples/export_wb.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
#
####
# 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.
#
# To run the script, you must have installed Python 3.5 or later.
####


import argparse
import getpass
Expand Down Expand Up @@ -48,7 +51,7 @@ def cleanup(tempdir):


def main():
parser = argparse.ArgumentParser(description='Export to PDF all of the views in a workbook')
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')
Expand Down
4 changes: 2 additions & 2 deletions samples/filter_sort_groups.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
####
# This script demonstrates how to filter groups using the Tableau
# This script demonstrates how to filter and sort groups using the Tableau
# Server Client.
#
# To run the script, you must have installed Python 3.5 or later.
Expand All @@ -24,7 +24,7 @@ def create_example_group(group_name='Example Group', server=None):


def main():
parser = argparse.ArgumentParser(description='Filter on groups')
parser = argparse.ArgumentParser(description='Filter and sort groups.')
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('--logging-level', '-l', choices=['debug', 'info', 'error'], default='error',
Expand Down
3 changes: 1 addition & 2 deletions samples/filter_sort_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# This script demonstrates how to use the Tableau Server Client
# to filter and sort on the name of the projects present on site.
#
#
# To run the script, you must have installed Python 3.5 or later.
####

Expand All @@ -26,7 +25,7 @@ def create_example_project(name='Example Project', content_permissions='LockedTo


def main():
parser = argparse.ArgumentParser(description='Get all of the refresh tasks available on a server')
parser = argparse.ArgumentParser(description='Filter and sort projects.')
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)
Expand Down
2 changes: 1 addition & 1 deletion samples/kill_all_jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@


def main():
parser = argparse.ArgumentParser(description='Cancel all of the running background jobs')
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')
Expand Down
2 changes: 1 addition & 1 deletion samples/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@


def main():
parser = argparse.ArgumentParser(description='List out the names and LUIDs for different resource types')
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="", help='site to log into, do not specify for default site')
parser.add_argument('--token-name', '-n', required=True, help='username to signin under')
Expand Down
2 changes: 1 addition & 1 deletion samples/pagination_sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

def main():

parser = argparse.ArgumentParser(description='Return a list of all of the workbooks on your server')
parser = argparse.ArgumentParser(description='Demonstrate pagination on the list of workbooks on the 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('--logging-level', '-l', choices=['debug', 'info', 'error'], default='error',
Expand Down
2 changes: 1 addition & 1 deletion samples/query_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@


def main():
parser = argparse.ArgumentParser(description='Query permissions of a given resource')
parser = argparse.ArgumentParser(description='Query permissions of a given resource.')
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, help='Site to sign into - default site if not provided')
Expand Down
2 changes: 1 addition & 1 deletion samples/refresh.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@


def main():
parser = argparse.ArgumentParser(description='Get all of the refresh tasks available on a server')
parser = argparse.ArgumentParser(description='Trigger a refresh task on a workbook or datasource.')
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)
Expand Down
10 changes: 9 additions & 1 deletion samples/set_refresh_schedule.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
####
# This script demonstrates how to set the refresh schedule for
# a workbook or datasource.
#
# To run the script, you must have installed Python 3.5 or later.
####


import argparse
import getpass
import logging
Expand All @@ -6,7 +14,7 @@


def usage(args):
parser = argparse.ArgumentParser(description='Explore workbook functions supported by the Server API.')
parser = argparse.ArgumentParser(description='Set refresh schedule for a workbook or datasource.')
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('--logging-level', '-l', choices=['debug', 'info', 'error'], default='error',
Expand Down
12 changes: 10 additions & 2 deletions tableauserverclient/models/job_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@


class JobItem(object):
def __init__(self, id_, job_type, progress, created_at, started_at=None, completed_at=None, finish_code=0):
def __init__(self, id_, job_type, progress, created_at, started_at=None,
completed_at=None, finish_code=0, notes=None):
self._id = id_
self._type = job_type
self._progress = progress
self._created_at = created_at
self._started_at = started_at
self._completed_at = completed_at
self._finish_code = finish_code
self._notes = notes or []

@property
def id(self):
Expand Down Expand Up @@ -40,6 +42,10 @@ def completed_at(self):
def finish_code(self):
return self._finish_code

@property
def notes(self):
return self._notes

def __repr__(self):
return "<Job#{_id} {_type} created_at({_created_at}) started_at({_started_at}) completed_at({_completed_at})" \
" progress ({_progress}) finish_code({_finish_code})>".format(**self.__dict__)
Expand All @@ -63,7 +69,9 @@ def _parse_element(cls, element, ns):
started_at = parse_datetime(element.get('startedAt', None))
completed_at = parse_datetime(element.get('completedAt', None))
finish_code = element.get('finishCode', -1)
return cls(id_, type_, progress, created_at, started_at, completed_at, finish_code)
notes = [note.text for note in
element.findall('.//t:notes', namespaces=ns)] or None
return cls(id_, type_, progress, created_at, started_at, completed_at, finish_code, notes)


class BackgroundJobItem(object):
Expand Down
14 changes: 14 additions & 0 deletions test/assets/job_get_by_id.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version='1.0' encoding='UTF-8'?>
<tsResponse xmlns="http://tableau.com/api" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://tableau.com/api http://tableau.com/api/ts-api-3.1.xsd">
<job id="2eef4225-aa0c-41c4-8662-a76d89ed7336" mode="job-mode" type="extractRefreshJob"
progress="100" createdAt="2020-05-13T20:23:45Z" updatedAt="2020-05-13T20:25:18Z"
completedAt="2020-05-13T20:25:18Z" finishCode="0">
<extractRefreshJob>
<notes>Job detail notes</notes>
</extractRefreshJob>
<statusNotes>
<statusNote type="CountOfUsersAddedToGroup" value="5" text="Description of how many users were added to the group during the import." />More detail
</statusNotes>
</job>
</tsResponse>
20 changes: 17 additions & 3 deletions test/test_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import requests_mock
import tableauserverclient as TSC
from tableauserverclient.datetime_helpers import utc
from ._utils import read_xml_asset

TEST_ASSET_DIR = os.path.join(os.path.dirname(__file__), 'assets')

GET_XML = os.path.join(TEST_ASSET_DIR, 'job_get.xml')
GET_XML = 'job_get.xml'
GET_BY_ID_XML = 'job_get_by_id.xml'


class JobTests(unittest.TestCase):
Expand All @@ -22,8 +24,7 @@ def setUp(self):
self.baseurl = self.server.jobs.baseurl

def test_get(self):
with open(GET_XML, 'rb') as f:
response_xml = f.read().decode('utf-8')
response_xml = read_xml_asset(GET_XML)
with requests_mock.mock() as m:
m.get(self.baseurl, text=response_xml)
all_jobs, pagination_item = self.server.jobs.get()
Expand All @@ -41,6 +42,19 @@ def test_get(self):
self.assertEqual(started_at, job.started_at)
self.assertEqual(ended_at, job.ended_at)

def test_get_by_id(self):
response_xml = read_xml_asset(GET_BY_ID_XML)
job_id = '2eef4225-aa0c-41c4-8662-a76d89ed7336'
with requests_mock.mock() as m:
m.get('{0}/{1}'.format(self.baseurl, job_id), text=response_xml)
job = self.server.jobs.get_by_id(job_id)

created_at = datetime(2020, 5, 13, 20, 23, 45, tzinfo=utc)
updated_at = datetime(2020, 5, 13, 20, 25, 18, tzinfo=utc)
ended_at = datetime(2020, 5, 13, 20, 25, 18, tzinfo=utc)
self.assertEqual(job_id, job.id)
self.assertListEqual(job.notes, ['Job detail notes'])

def test_get_before_signin(self):
self.server._auth_token = None
self.assertRaises(TSC.NotSignedInError, self.server.jobs.get)
Expand Down
2 changes: 1 addition & 1 deletion test/test_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def test_get_materializeviews_tasks(self):
self.assertEqual(parse_datetime('2019-12-09T22:30:00Z'), task.schedule_item.next_run_at)
self.assertEqual(parse_datetime('2019-12-09T20:45:04Z'), task.last_run_at)

def test_delete(self):
def test_delete_data_acceleration(self):
with requests_mock.mock() as m:
m.delete('{}/{}/{}'.format(
self.server.tasks.baseurl, TaskItem.Type.DataAcceleration,
Expand Down
5 changes: 3 additions & 2 deletions test/test_workbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#%%
import unittest
import os
import re
import requests_mock
import tableauserverclient as TSC
import xml.etree.ElementTree as ET
Expand Down Expand Up @@ -461,8 +462,8 @@ def test_publish_with_hidden_view(self):
hidden_views=['GDP per capita'])

request_body = m._adapter.request_history[0]._request.body
self.assertIn(
b'<views><view hidden="true" name="GDP per capita" /></views>', request_body)
self.assertTrue(re.search(rb'<views><view.*?hidden=\"true\".*?\/><\/views>', request_body))
self.assertTrue(re.search(rb'<views><view.*?name=\"GDP per capita\".*?\/><\/views>', request_body))

def test_publish_async(self):
self.server.version = '3.0'
Expand Down
0