Giter Club home page Giter Club logo

tableau_tools's People

Contributors

bryanthowell-tableau avatar daniilbalabanov33n avatar hudsonmc16 avatar ilyaderendyaev avatar jachicao avatar jd-r avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tableau_tools's Issues

tableau_parameters.py error whe Parameter name includes the text "Parameter"

Hi,

The TableauParameters init() code chokes on parameters with the text "Parameter" in the name.

The code assumes they have the format: [Parameter #] and tries to cast the second "word" in the parameter name to an int.

This fails if you have a parameter called (for example) "Country Name Parameter" (the xml name attribute is [Country Name Parameter]).

One method to fix the error might be to use a regular expression matcher rather than string.find().
e.g.:

...

import re
...

        else:
            self.log(u'Parameter XML passed in, finding essential characteristics')
            self.ds_xml = datasource_xml
            params_xml = self.ds_xml.findall(u'./column')
            numberedParameterRegex = re.compile('\[Parameter (\d+)\]')
            for column in params_xml:
                alias = column.get(u'caption')
                internal_name = column.get(u'name')  # type: unicode
                # Parameters are all given internal name [Parameter #], unless they are copies where they
                # end with (copy) h/t Jeff James for discovering
                regexMatch = numberedParameterRegex.match(internal_name)
                if regexMatch and regexMatch.group(1):
                    param_num = int(regexMatch.group(1))
                    # Move up the highest_param_num counter for when you add new ones
                    if param_num > self._highest_param_num:
                        self._highest_param_num = param_num
...

Regards,

Andrew

using the package with python 3.4

Is it compatible with python 3? I'm trying to use the TableauRestApiConnection to signin to the server. I get an error -

Traceback (most recent call last):
File "program.py", line 32, in
from tableau_tools.tableau_rest_api import *
File "C:\Python34\lib\site-packages\tableau_tools__init__.py", line 2, in
from tableau_base import TableauBase
File "C:\Python34\Lib\site-packages\tableau_tools\tableau_base.py", line 3, in

from logger import Logger
File "C:\Python34\Lib\site-packages\tableau_tools\logger.py", line 11
print u"Error: File '{}' cannot be opened to write for logging".format(filen
ame)
^
SyntaxError: invalid syntax

Project is uninstallable from source with pipenv

When installing via pipenv, tableau_tools gets installed from pypi because it is listed in the requirements.txt. Additionally, when trying to install package with pipenv install -e ., the following error is produced

Installing -e ....
Obtaining file:///path_here/tableau_tools
    Complete output from command python setup.py egg_info:
    running egg_info
    creating tableau_tools.egg-info
    writing requirements to tableau_tools.egg-info/requires.txt
    writing tableau_tools.egg-info/PKG-INFO
    writing top-level names to tableau_tools.egg-info/top_level.txt
    writing dependency_links to tableau_tools.egg-info/dependency_links.txt
    writing manifest file 'tableau_tools.egg-info/SOURCES.txt'
    error: package directory 'tableau_tools' does not exist
    
    ----------------------------------------

Error:  An error occurred while installing -e .!
Command "python setup.py egg_info" failed with error code 1 in /path_here/tableau_tools/

This is likely caused by a bug in -e .. Report this to its maintainers.

The same issues occurs in Python 3.

API 2.8: 400 Client Error Bad Request for url

tableau_tools Version = 4.5.3
Python Version = 2.7.10
OS Version = macOS High Sierra 10.13.3
Tableau Server Version = 10.5

Hi,

I'm getting an error refreshing an extract using the API 2.8 call for update_datasource_now. I get the following:
HTTPError: 400 Client Error: Bad Request for url: https://server/api/2.8/sites/{site_id}/datasources/{ds_id}/refresh

I am able to manually refresh data sources directly on Tableau Server. Do I need specific permissions to do the same via code? Currently, my site role is Publisher.

Thanks

Connection Payload is not formatted correctly

In tableau_rest_api_connection, starting on line 121 you have:

        if new_server_address is not None:
            c.set(u'serverAddress="{}" ', new_server_address)
        if new_server_port is not None:
            c.set(u'serverPort="{}" ', new_server_port)
        if new_connection_username is not None:
            c.set(u'userName="{}" ', new_connection_username)
        if new_connection_username is not None:
            c.set(u'password="{}"', new_connection_password)

You need to remove '="{}"' from each unicode string. It misformats the connection xml payload and causes an error. Look at the function above for an example of how you are correctly using it.

Change it to this:

        if new_server_address is not None:
            c.set(u'serverAddress', new_server_address)
        if new_server_port is not None:
            c.set(u'serverPort', new_server_port)
        if new_connection_username is not None:
            c.set(u'userName', new_connection_username)
        if new_connection_username is not None:
            c.set(u'password', new_connection_password)

Querying workbooks for username only queries first 99 users

We've lately upgraded Tableau Server to:
Tableau Server Version: 10.3.2 (10300.17.0728.2252) 64-bit Windows

We've started seeing issues with querying list of workbooks by username - only first 99 users are taken in the querying pool, if the user is latter on the user list, the app will throw NoMatchFoundException, since it cannot find the user it's querying the workbooks for.

We're guessing this might be some kind of pagination issue - is there anything on API level that can be set to resolve this? Or is it some kind of server-side settings we need to change?

Tabcmd export user impersonation

I believe the Tabcmd.create_export command's User Impersonation does not work on Python 3.4+. Tracing the error led me to tableau_http.py which calls request.add_data(). The add_data function was removed from urllib.

Unable to upload files larger than 20mb

Publish call fails for extracts larger than 20mb.

The error occurs when tableau_rest_api_connection.py runs append_to_file_upload, specifically the publish += content. The error is TypeError: must be str, not bytes.

I dug in and can change the single_upload_limit to 64, but that doesn't solve the problem for extracts larger than this.

I've seen similar issues reported for workbooks, but I guess they both use the same functions.

AWS Athena needs both "dbname" and "schema" fields in TableauConnection

Tableau connection currently assumes "schema" to be an alias for "dbname", whereas Athena uses both these fields in the xml, and in fact expects them to be different.

This is problematic when replicating publishing connections, as doing a conn.schema = u'whatever' actually modifies the "dbname" field in the xml, rather than the "schema" field, as would be proper.

In TableauDatasource generate_relation_section is never called, so new TDS is never valid

I'm using the latest version of the tableau_tools library to try and create new data sources from scratch.

I've been successful in creating a new file and adding a connection to my database, but the file is not valid without adding at least one relation to the TDS file. I saw in the code that you have a function set_first_table() which sets up the main relation, but while the main_table_relation object is set in the TableauDatasource object, it is never actually added to the data source xml.

tableau_parameter.py failing when getting datasource characteristics

I have a Parameters data source which has copied parameters. Some of them have been copied more than once. In that case, Tableau appends '(copy 2)' or '(copy #)' depending on how many times it's been copied. The if internal_name.find condition should account for that possibility. Thank you!

Starting at line 43:

# Parameters are all given internal name [Parameter #], unless they are copies where they
# end with (copy) h/t Jeff James for discovering
                if internal_name.find(u'(copy)') == -1:

query_datasource with a name AND a project_id returns the wrong datasource

If you call query_datasource with a name AND a project id, the method returns the first datasource in that project, no matter what the name.

This appears to be due to the method getting all datasources, then doing a findall by name, ignoring that result and doing a findall by project id on the full list and returning the first datasource!

PS: If your server has a lot of datasources, this method takes a very long time to return due to the REST call returning all datasources first. Can you update it to use name:eq: filter and then filter the result by project id (if required)?

Thanks,

Andrew

Extract filters not included with TWBX file save

Hi,

I've been experiencing one particular issue for a couple of days now, and I'm hoping you might be able to help. I have a TWBX file saved locally (by nature of TWBX it includes an extract) for which I am attempting to make a copy with certain extract filters added. However despite seeing the successful addition of these filters within python (datasource extract_filters are present in TableauDatasource object), I am finding that upon saving the TWBX file - the new extract filters are not being included in the saved file. Can you please advise on resolution to this issue?

relevant excerpt from my script:

`
##myTWBXfileVariable is filepath to TWBX file
tempWorkBook = myTWBXfileVariable
tempTWBXfile = TableauFile(filename=tempWorkBook, logger_obj=None, create_new=False, ds_version=u'10')
tempDoc = tempTWBXfile.tableau_document
tempDSes = tempDoc.datasources

##product is a variable containing string of desired filter value
for tempDS in tempDSes:
tempDS.add_dimension_extract_filter(column_name=u"Product_Name", values=[product, ],
custom_value_list=False)
tempDS.add_continuous_extract_filter(column_name="DeliveryDate", min_value=startDate,
max_value=None, date=True)
newFile = tempTWBXfile.save_new_file(new_filename_no_extension=product)
`

I appreciate any insight that you might be able to provide.

Thanks!

TableauDocument.save_file(filename) Not allowing saving

I am testing out the functionality of the tableau_tools.tableau_documents package, looking into creating tableau datasources in code. Unfortunately, before I can even start creating datasources, I want to make sure I can create and save a new file, which I currently cannot do.

My function:

def test_tableau_tools_library():
    LOGGER = Logger("log.log")
    new_tableau_file = TableauFile("test.tds", logger_obj=LOGGER, create_new=True, ds_version=u'10')
    print(new_tableau_file.file_type)
    new_tableau_document = new_tableau_file.tableau_document
    print(new_tableau_document.document_type)
    new_tableau_document.save_file(u'test')

While attempting to run this function, I receive the following error:

Traceback (most recent call last):
  File "tableau_datasource_creator/tableau_datasource_creator.py", line 17, in <module>
    test_tableau_tools_library()
  File "tableau_datasource_creator/tableau_datasource_creator.py", line 14, in test_tableau_tools_library
    new_tableau_document.save_file(u'test', save_to_directory="testing")
  File "C:\Users\COBY~1.BEN\Desktop\env\lib\site-packages\tableau_tools\tableau_documents\tableau_datasource.py", line 315, in save_file
    lh.write(self.get_datasource_xml())
  File "C:\Users\COBY~1.BEN\Desktop\env\lib\codecs.py", line 718, in write
    return self.writer.write(data)
  File "C:\Users\COBY~1.BEN\Desktop\env\lib\codecs.py", line 376, in write
    data, consumed = self.encode(object, self.errors)
TypeError: utf_8_encode() argument 1 must be str, not bytes

Change parameter in published workbook?

Hello Bryan,
is it possible to edit the XML structure of a published workbook, for example by published_workbook_object?

I need to find a way to edit parameters in an wb after publishing.

Best regards,
Michael

Error Trying to Change Workbook Owner Using update_workbook Function

I am able to change owner on Tableau Server directly, but when running update_workbook(workbook_name_or_luid, workbook_project_name_or_luid, new_owner_luid) I get the error below. I get a similar error when trying to use the query_users function. From the error, it appears to want to query users, so that might be where it's failing.

EDIT: I also notice it's using tableau_rest_api_connection_25.pyc, but I connected using the 2.8 API.
EDIT2: I tried the same code with someone who has Site Administrator access and it seemed to work for them. I have Project Lead access (or elevated Publisher, don't know the exact terminology). I'd still be curious knowing how to equivalently change object owners via code as I can already do it manually on Tableau Server.

<ipython-input-9-da6428a90963> in <module>()
     14         workbook_name_or_luid=f['tableau_workbook_name'],
     15         workbook_project_name_or_luid=proj_luid,
---> 16         new_owner_luid=f['tableau_owner_luid']
     17     )
     18 #     sleep(.5)

/Users/user/virtualenvs/ishbooks/lib/python2.7/site-packages/tableau_tools/tableau_rest_api/tableau_rest_api_connection.pyc in update_workbook(self, workbook_name_or_luid, workbook_project_name_or_luid, new_project_luid, new_owner_luid, show_tabs)
   1871         else:
   1872             workbook_luid = self.query_workbook_luid(workbook_name_or_luid, workbook_project_name_or_luid,
-> 1873                                                      self.username)
   1874         tsr = etree.Element(u"tsRequest")
   1875         w = etree.Element(u"workbook")

/Users/user/virtualenvs/ishbooks/lib/python2.7/site-packages/tableau_tools/tableau_rest_api/tableau_rest_api_connection.pyc in query_workbook_luid(self, wb_name, proj_name_or_luid, username_or_luid)
    916         if username_or_luid is None:
    917             username_or_luid = self.user_luid
--> 918         workbooks = self.query_workbooks(username_or_luid)
    919         workbooks_with_name = workbooks.findall(u'.//t:workbook[@name="{}"]'.format(wb_name), self.ns_map)
    920         if len(workbooks_with_name) == 0:

/Users/user/virtualenvs/ishbooks/lib/python2.7/site-packages/tableau_tools/tableau_rest_api/tableau_rest_api_connection_25.pyc in query_workbooks(self, username_or_luid, project_name_or_luid, all_fields, created_at_filter, updated_at_filter, owner_name_filter, tags_filter, sorts, fields)
    315             user_luid = username_or_luid
    316         else:
--> 317             user_luid = self.query_user_luid(username_or_luid)
    318 
    319         filter_checks = {u'updatedAt': updated_at_filter, u'createdAt': created_at_filter, u'tags': tags_filter,

/Users/user/virtualenvs/ishbooks/lib/python2.7/site-packages/tableau_tools/tableau_rest_api/tableau_rest_api_connection_25.pyc in query_user_luid(self, username)
    400         else:
    401             user_luid = self.query_single_element_luid_from_endpoint_with_filter(u"user", username,
--> 402                                                                                  optimize_with_field=True)
    403             self.username_luid_cache[username] = user_luid
    404         self.end_log_block()

/Users/user/virtualenvs/ishbooks/lib/python2.7/site-packages/tableau_tools/tableau_rest_api/tableau_rest_api_connection_25.pyc in query_single_element_luid_from_endpoint_with_filter(self, element_name, name, optimize_with_field)
    242         self.start_log_block()
    243         if optimize_with_field is True:
--> 244             elements = self.query_resource(u"{}s?filter=name:eq:{}&fields=id".format(element_name, name))
    245         else:
    246             elements = self.query_resource(u"{}s?filter=name:eq:{}".format(element_name, name))

/Users/user/virtualenvs/ishbooks/lib/python2.7/site-packages/tableau_tools/tableau_rest_api/tableau_rest_api_connection_25.pyc in query_resource(self, url_ending, server_level, filters, sorts, additional_url_ending, fields)
    180         self._request_obj.url = api_call
    181         self._request_obj.http_verb = u'get'
--> 182         self._request_obj.request_from_api()
    183         xml = self._request_obj.get_response()  # return Element rather than ElementTree
    184         self._request_obj.url = None

/Users/user/virtualenvs/ishbooks/lib/python2.7/site-packages/tableau_tools/tableau_rest_api/rest_xml_request.pyc in request_from_api(self, page_number)
    259     def request_from_api(self, page_number=1):
    260         try:
--> 261             self.__make_request(page_number)
    262         except:
    263             raise

/Users/user/virtualenvs/ishbooks/lib/python2.7/site-packages/tableau_tools/tableau_rest_api/rest_xml_request.pyc in __make_request(self, page_number)
    196         # Error detection
    197         except requests.exceptions.HTTPError as e:
--> 198             self._handle_http_error(response, e)
    199 
    200     def _handle_http_error(self, response, e):

/Users/user/virtualenvs/ishbooks/lib/python2.7/site-packages/tableau_tools/tableau_rest_api/rest_xml_request.pyc in _handle_http_error(self, response, e)
    238             if status_code == 409:
    239                 self.log(u'HTTP 409 error, most likely an already exists')
--> 240             raise RecoverableHTTPException(status_code, error_code, detail_luid)
    241         else:
    242             raise e

RecoverableHTTPException: ```

pip install missing requirements

It appears this package doesn't work without psycopg2

from tableau_tools import Tabcmd
Traceback (most recent call last):
File "", line 1, in
ImportError: cannot import name 'Tabcmd'
from tableau_tools import tabcmd
Traceback (most recent call last):
File "", line 1, in
File "/env/lib/python3.6/site-packages/tableau_tools/tabcmd.py", line 6, in
from tableau_tools.tableau_repository import *
File "
/env/lib/python3.6/site-packages/tableau_tools/tableau_repository.py", line 5, in
import psycopg2
ModuleNotFoundError: No module named 'psycopg2'

Workbook - Published separately

Hi
I am a beginner tableau developer. I have a question how to publish a workbook with separately data source using Tableau REST API. I'm using source control so the tbwx and tde files are on the disk. Do you have a step-by-step example of how to do this?

Best regards,
Michael

Keep password embedded in connection after replicating workbooks

Hi!

After replicating the workbooks (replicate_workbooks_with_published_dses), the embedded password for the connection to pgSQL in the workbooks is lost. I need to manually go to Workbook > Data Sources > Edit connection, switch the "Password" option to "Embedded password in connection" and paste the password again.

How could I prevent that?

Tabcmd is not defined

Hi,
I'm getting below error:
Tabcmd(tabcmd_dir, server, username, password, site=site_content_url, tabcmd_config_location=tabcmd_config_location)
NameError: name 'Tabcmd' is not defined.

Attached my python script.
testtabcmd.txt

self.__publish = True remains after send_publish_request or send_append_request

If you publish content, any subsequent (non-publish) update fails.

The send_publish_request and send_append_request methods call set_publish_content(content, boundary_string) and that sets__publish = True
However, in their "cleanup" code, when they call self._request_obj.set_publish_content(None, None) ,
sets__publish remains True.

see:
https://github.com/bryantbhowell/tableau_tools/blob/f64a04a8b5d79807bd12a03459508b377462c92b/tableau_rest_api/tableau_rest_api_connection.py#L407

Subsequent calls to update have the wrong ContentType set.
see: https://github.com/bryantbhowell/tableau_tools/blob/f64a04a8b5d79807bd12a03459508b377462c92b/tableau_rest_api/rest_xml_request.py#L138

Thanks,

Andrew

UnicodeDecodeError

I'm getting the well known from previous versions - UnicodeDecodeError. This happens exactly the same as was in: https://github.com/bryantbhowell/tableau_rest_api/issues/9

all my files are UTF-8 encoded, I'm using the latest version of Tableau Tools.

Traceback (most recent call last): File "D:\Apache\Apache_2.2_32\Apache22\htdocs\CoMeT\python\getWorkbookList.py" , line 30, in <module> tab_srv.signin() File "C:\Python27\lib\tableau_tools\tableau_rest_api\tableau_rest_api_connecti on.py", line 215, in signin api.request_from_api(0) File "C:\Python27\lib\tableau_tools\tableau_rest_api\rest_xml_request.py", lin e 195, in request_from_api self.__make_request(page_number) File "C:\Python27\lib\tableau_tools\tableau_rest_api\rest_xml_request.py", lin e 140, in __make_request unicode_raw_response = parser.unescape(initial_response) File "C:\Python27\lib\HTMLParser.py", line 475, in unescape return re.sub(r"&(#?[xX]?(?:[0-9a-fA-F]+|\w{1,8}));", replaceEntities, s) File "C:\Python27\lib\re.py", line 155, in sub return _compile(pattern, flags).sub(repl, string, count) UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 674: ordina l not in range(128)

Any ideas?

EDIT: Adding log file:

---------- signin started at 2017-07-03 03:41:34 ---------- 2017-07-03 03:41:34 : Trying version 10.0 2017-07-03 03:41:34 : Logging in via: https://tab.cert.sabre.com/api/2.3/auth/signin 2017-07-03 03:41:34 : Login payload is <tsRequest><credentials name="username" password="password" ><site /></credentials></tsRequest> 2017-07-03 03:41:34 : HTTP verb is post 2017-07-03 03:41:34 : Making REST request to Tableau Server using post 2017-07-03 03:41:34 : Request URI: https://tab.cert.sabre.com/api/2.3/auth/signin 2017-07-03 03:41:34 : Request XML: <tsRequest><credentials name="username" password="password" ><site /></credentials></tsRequest> 2017-07-03 03:41:35 : Content type from headers: text/html;charset=UTF-8

AuthenticationException on Tabcmd

Hi.
It appears that I am not able to connected to Tableau Online when using Tabcmd(). Have attached the screenshot of the error message. Running the same configuration directly on tabcmd.exe

Script details:
`
from tableau_tools import *
from tableau_tools.tabcmd import *
from tableau_tools.tableau_rest_api import *
from tableau_tools.tableau_documents import *

logger = Logger(u'abc.log')

tabcmd_dir = u"C:\Program Files\Tableau\Tableau Server\10.5\extras\Command Line Utility\"
tabcmd_config_location = u'C:\Users\DataSpark Analytics\AppData\Local\Tableau\Tabcmd\'
server = u'https://online.tableau.com'
site_content_url = u'abc'
username = u'{[email protected]}'
password = u'{abc}'

tabcmd = Tabcmd(tabcmd_dir, server, username, password, site=site_content_url, tabcmd_config_location=tabcmd_config_location)
tabcmd.enable_logging(logger)
`
screen shot 2018-01-17 at 1 56 44 pm

Error when calling publish_workbook on a file greater than 20MB

I have recently started to use tableau_tools to upload dynamically created Tableau workbooks to Tableau server. I can get a code snippet to work when uploading workbooks smaller than 20MB, but I receive an error when I try to upload larger notebooks. I think the problem may be coming from the way in which this package handles the sending of chunks of data to the Tableau API, but I'm not sure.

Below is the error that I receive in my code snippet:

TypeError                                 Traceback (most recent call last)
<ipython-input-315-8c1a4ee06859> in <module>()
      1 t.publish_workbook(
      2     LOCAL_FILE_NAME, SERVER_WORKBOOK_NAME, project, check_published_ds=False,
----> 3     connection_username='xxxxxxxx', connection_password='xxxxxxxx')

~/personal_python_libraries/tableau_tools/tableau_rest_api/tableau_rest_api_connection.py in publish_workbook(self, workbook_filename, workbook_name, project_obj, overwrite, connection_username, connection_password, save_credentials, show_tabs, check_published_ds, oauth_flag)
   2124                                    {"overwrite": overwrite}, connection_username, connection_password,
   2125                                    save_credentials, show_tabs=show_tabs, check_published_ds=check_published_ds,
-> 2126                                    oauth_flag=oauth_flag)
   2127         workbook = xml.findall('.//t:workbook', self.ns_map)
   2128         return workbook[0].get('id')

~/personal_python_libraries/tableau_tools/tableau_rest_api/tableau_rest_api_connection.py in publish_content(self, content_type, content_filename, content_name, project_luid, url_params, connection_username, connection_password, save_credentials, show_tabs, check_published_ds, oauth_flag)
   2272                         for piece in self.read_file_in_chunks(content_file):
   2273                             self.log("Appending chunk to upload session {}".format(upload_session_id))
-> 2274                             self.append_to_file_upload(upload_session_id, piece, final_filename)
   2275 
   2276                         # Finalize the publish

~/personal_python_libraries/tableau_tools/tableau_rest_api/tableau_rest_api_connection.py in append_to_file_upload(self, upload_session_id, content, filename)
   2325         publish_request += 'Content-Type: application/octet-stream\r\n\r\n'
   2326 
-> 2327         publish_request += content
   2328 
   2329         publish_request += "\r\n--{}--".format(boundary_string)

TypeError: Can't convert 'bytes' object to str implicitly

I am using Python 3.5.2 and the most recent version of tableau_tools. Please let me know if I can provide more information.

Adding more configuration options to a Datasource file

The datasource class is missing a couple features I need for creating a tds file from scratch:

  1. Ability to add columns to folders
  2. Add option of setting 'hidden' and 'aggregation' attributes for each column

Both additions would be incredibly useful to have so I can fully automate the editing/creation of datasources!

None Element Error When Creating Data Source from Scratch

tableau_tools Version = 4.6.0
Python Version = 2.7.10
OS Version = macOS High Sierra 10.13.3
Tableau Server Version = 10.5

I followed the docs to create a data source from scratch, but keep running into errors. When trying to create one with a Custom SQL Query, I get an Element error. Below is my code:

new_file = TableauFile(filename='test.tds',create_new=True,ds_version='10.5')
new_doc = new_file.tableau_document

new_doc.add_new_connection(ds_type='postgres'
                           ,server='localhost'
                           ,db_or_schema_name='db_name'
                           ,authentication='username-password'
                          )

ds = new_doc.datasources[0]
conn = ds.connections[0]

conn.port = '5432'
conn.username = 'postgres'

ds.set_first_custom_sql('select * from schema.table limit 5','my_query',conn.connection_name)

new_doc.save_file(u'New TDS')
TypeError                                 Traceback (most recent call last)
<ipython-input-33-c2c2382e0ae5> in <module>()
----> 1 new_doc.save_file(u'New TDS')

/Users/tableaukun/virtualenvs/venv/lib/python2.7/site-packages/tableau_tools/tableau_documents/tableau_datasource.py in save_file(self, filename_no_extension, save_to_directory)
    386             lh.write(u"<?xml version='1.0' encoding='utf-8' ?>\n\n")
    387             # Write the datasource XML itself
--> 388             lh.write(self.get_datasource_xml())
    389             lh.close()
    390 

/Users/tableaukun/virtualenvs/venv/lib/python2.7/site-packages/tableau_tools/tableau_documents/tableau_datasource.py in get_datasource_xml(self)
    319             new_rel_xml = self.generate_relation_section()
    320             connection_root = new_xml.find(u'.//connection', self.ns_map)
--> 321             connection_root.append(new_rel_xml)
    322             cas = self.generate_aliases_column_section()
    323             # If there is no existing aliases tag, gotta add one. Unlikely but safety first

TypeError: must be Element, not None

There seems to be an issue with generate_relation_section(). I manually ran the code for the function and received the expected value:

import xml.etree.ElementTree as ET

rel_xml_obj = ET.Element(u"relation")
# There's only a single main relation with only one table

if len(new_doc.join_relations) == 0:
    for item in new_doc.main_table_relation.items():
        rel_xml_obj.set(item[0], item[1])
    if new_doc.main_table_relation.text is not None:
        rel_xml_obj.text = new_doc.main_table_relation.text

tableau_datasource.py - extra null check needed before calling self.read_existing_relations()?

I recently used the tableau_tools library to do automated translations of field labels and ran into this issue.

When running the translate_columns operation, I would encounter an error. The error would occur at the self.read_existing_relations() call. In this operation, the object self.relation_xml_obj is used. However, within the operation there is no check to see if this object actually exists. Apparently, after reading my workbook, this object would have value None.

Proposed solution: add a check to see if self.read_existing_relations() is None before running the self.read_existing_relations() operation or perform this check within the operation.

Tableau_Tools API connection defaults to version 2.8. Recommended update to tableau_rest_api_connection.py

tableau_rest_api_connection.py
Line 17 update to:
def init(self, server, username, password, site_content_url="", version=""):

after line 24 insert another line to set the api_version (right now it defaults to "2.8" no matter what):
TableauBase.set_tableau_server_version(self, version)

This now lets you determine which API to use when making call:
TableauRestApiConnection(server, username, password, site_content_url=site, version=ver)

tableau_document.py - translate_captions - getroot() calls unnecessary?

I recently used the tableau_tools library to do automated translations of field labels and ran into this issue.

When running the translate_captions operation from the tableau_document.py file, the getroot() call would throw an error. I am new to Python but if I understood it correctly, this call is not needed here since it would return the root of the XML branch but we want to select columns in this operation, which are nested children.

After removing the getroot() calls, the operation seems to work correctly.

For reference, my new version of this operation:

def translate_captions(self):
        self.start_log_block()
        for column in self.columns_list:
            if column.get('caption') is None:
                trans = self.__find_translation(column.get('name'))
            else:
                # Try to match caption first, if not move to name
                self.log('Existing caption')
                trans = self.__find_translation(column.get('caption'))
                if trans is None:
                    trans = self.__find_translation(column.get('name'))
            if trans is not None:
                column.set('caption', trans)
        self.end_log_block()

NameError: name 'wbp_name_or_luid' is not defined in query_workbook()

If you call query_workbook() with a p_name_or_luid set to a luid, then you get the python error:
NameError: name 'wbp_name_or_luid' is not defined

This is caused by a typo in tableau_rest_api_connection.py, line 838:
wb_in_proj = workbooks.findall('.//t:workbook[@name="{}"]/:project[@id="{}"]/..'.format(wbp_name_or_luid), self.ns_map)

see: https://github.com/bryantbhowell/tableau_tools/blob/f64a04a8b5d79807bd12a03459508b377462c92b/tableau_rest_api/tableau_rest_api_connection.py#L838

Creating a Datasource from Scratch - Type Error for ds_string

For the new version 4.6.3 (and 4.6.2), there now appears to be a Type error on line 390 in the tableau_datasource.py file:
if type(ds_string, u'bytes'): throws an error: TypeError: type() takes 1 or 3 arguments
This appears to be a new addition. Will ds_string ever be a byte?

TableauSDK and tableau_rest_api

Hi,

Previous project tableau_rest_api did not require the TableauSDK dependency, which is not on PyPI and can be a pain to install inside docker images.

Is there a way of using tableautools.tableau_rest_api without requiring this extra dependency?

Thanks,

Unable to Translate Username to Email

Tableau On-premise server to Tableau Online migration requires username translation to email ID
Code given in replicate_site_structure_sample.py is not working. How do we translate the replace the username to email?

Unable to publish large workbook - 400 Bad Request

Tableau_tools version: 4.4.1.
OS: MacOs High Sierra 10.13.4
Python: 2.7.10

I'm using an example in this repo. Original file uses TableauRestApiConnection25 which results in the same error as when it is modified to using TableauRestApiConnection28.

Publishing a small <10MB workbook works just fine. Uploading a 100MB+ workbook results in an error like so:

$ python copy-extract-large.py
  Error: File 'temp_wb.twbx' cannot be opened to upload
  Traceback (most recent call last):
    File "copy-extract-large.py", line 31, in <module>
      d.publish_workbook(u'{}.twbx'.format(downloaded_filename), wb_name_on_server, proj, save_credentials=False, overwrite=True)
    File "/Users/svaranasi/WorkDir/py27venv/scheduler/lib/python2.7/site-packages/tableau_tools/tableau_rest_api/tableau_rest_api_connection.py", line 1946, in publish_workbook
      check_published_ds=check_published_ds)
    File "/Users/svaranasi/WorkDir/py27venv/scheduler/lib/python2.7/site-packages/tableau_tools/tableau_rest_api/tableau_rest_api_connection.py", line 2077, in publish_content
      return self.send_publish_request(url, publish_request, boundary_string)
    File "/Users/svaranasi/WorkDir/py27venv/scheduler/lib/python2.7/site-packages/tableau_tools/tableau_rest_api/tableau_rest_api_connection.py", line 367, in send_publish_request
      api.request_from_api(0)
    File "/Users/svaranasi/WorkDir/py27venv/scheduler/lib/python2.7/site-packages/tableau_tools/tableau_rest_api/rest_xml_request.py", line 285, in request_from_api
      self.__make_request(page_number)
    File "/Users/svaranasi/WorkDir/py27venv/scheduler/lib/python2.7/site-packages/tableau_tools/tableau_rest_api/rest_xml_request.py", line 200, in __make_request
      response.raise_for_status()
    File "/Users/svaranasi/WorkDir/py27venv/scheduler/lib/python2.7/site-packages/requests/models.py", line 935, in raise_for_status
      raise HTTPError(http_error_msg, response=self)
  requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://<snipped>/api/2.8/sites/8fui90b6-61e7-40a4-8b27-3a1971caef0e/workbooks?uploadSessionId=228:3A71C112761E4547BG872YZ562FP5D54-2:0&workbookType=twbx&overwrite=true

The error Error: File 'temp_wb.twbx' cannot be opened to upload is strange because I can surely see the script creating this file and deleting it as part of the copy.

I've also checked Tableau's REST API docs for Publish Workbook and the URL and params seems to be to spec.

The logs show the part files uploading successfully (PUT operations). It is the final "send_publish_request" that is failing.

The log file has this:

2018-04-10 15:07:32 : Received a 400 error, here was response:
2018-04-10 15:07:32 : <?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-2.8.xsd"><error code="400011"><summary>Bad Request</summary><detail>There was a problem publishing the file '29187:9B137D4FF84945D4BE55B9E1F21772C8-1:0'.</detail></error></tsResponse>
2018-04-10 15:07:32 : Tableau REST API error code is: 400011

I added additional logging in the rest_xml_request.py file and here's line 156, 157 in the file after my change:

self.log(u"HERE#3 !"+url+"DATA"+str(self.__publish_content)+"HEADERS"+str(request_headers))                        
response = requests.post(url, data=self.__publish_content, headers=request_headers)

I see that the request looks OK to my eyes. Here's the logs:

DATA
--0d6a88bdf4b5c30b09ca46e16958c7
Content-Disposition: name="request_payload"
Content-Type: text/xml

<tsRequest>
<workbook name="Workbook Example" showTabs="true">
<project id="ea62d37c-11m6-11e8-8b28-bf4e547648ce" />
</workbook></tsRequest>
--0d6a88bdf4b5c30b09ca46e16958c7

--HEADERS{'X-tableau-auth': 'op42_abcQ--JSvrKm7yvZw|iV9pvJeGt8q5I2b7Ntg1l4pEq6JZlXYZ', 'Content-Type': 'multipart/mixed; boundary=0d6a88bdf4b5c30b09ca46e16958c7'}

(^ Tokens and IDs have been slightly modified when pasting here for security reasons)

Please help!

update in pypi repo

Would it be possible to push changes into PyPi repo? The latest version on that is 4.3.16 from 2/22. Thanks!

Create a new extract data source from scratch

I know this has been asked before in different ways but the previous issues don't really solve my issue.
I have a python project that does my ETL process and sets up postgres tables that Tableau can use. After we create each table we have to manually go into Tableau Desktop and create a new data source that points to the new table set the data source to be an extract and the publish it to Tableau Server where we schedule the refreshes. I'm trying to automate the process of manually creating the data sources on Tableau desktop and then publish them to the server.

Following the docs and looking at the code I was able to do this:

new_tableau_file = TableauFile("test.tds", logger_obj=None, create_new=True, ds_version=u'10.5')
new_tableau_document = new_tableau_file.tableau_document
dses = new_tableau_document.datasources
ds = dses[0]
ds.add_new_connection(ds_type=u'postgres', server=hostname, db_or_schema_name=u'db_name')
conn = ds.connections[0]
conn.port = port
conn.username = username
ds.set_first_table(db_table_name=table_name, table_alias=table_alias, connection=conn.connection_name)
ds.add_column_alias('person_id', caption='Person ID', dimension_or_measure='dimension', discrete_or_continuous='discrete', datatype='integer', calculation=None)
ds.add_column_alias('name', caption='Name', dimension_or_measure='dimension', discrete_or_continuous='discrete', datatype='string', calculation=None)
new_tableau_document.save_file(datasource_name)

And it successfully creates a live connection to my table on my database. Now I want to be able to create a datasource that is an extract and that can be refreshed on a schedule on the server. I tried adding this create_extract(extract_name) just before saving the file, but it fails on save file with the following error:

Traceback (most recent call last):
  File "datasources.py", line 30, in create_tds
    new_tableau_document.save_file(datasource_name)
  File "/.virtualenv/lib/python3.7/site-packages/tableau_tools/tableau_documents/tableau_datasource.py", line 398, in save_file
    ds_string = self.get_datasource_xml()
  File "/.virtualenv/lib/python3.7/site-packages/tableau_tools/tableau_documents/tableau_datasource.py", line 356, in get_datasource_xml
    new_xml.remove(l)
TypeError: remove() argument must be xml.etree.ElementTree.Element, not None

I'm not sure if I'm doing something wrong or if what I want to do is not supported by the library. Could you help me out with this?

Support SSL certificate options for tableau_http.py

Right now it seems as though the TableauHTTP class is agnostic as to whether it receives a URL that uses HTTPS as a protocol vs. unencrypted HTTP. HTTPS is clearly preferred for everything, but we've had issues getting Python running on Windows to properly verify internal certs that we've generated. And some certs may throw verification errors, which we don't care about immediately, but still want to encrypt our traffic.

So, it'd be great if we could support verify / don't verify and cafile / capath options in this class, all as optional parameters.

Thanks!

Failed to update 'Server Administrator' user's SiteRole to 'Unlicensed'

Getting error when I am trying to update 'Server Administrator' user's SiteRole to 'Unlicensed'. I am using Tableau 2019.1 with api version 3.3 and easily change role but not with this Tableau_Tools.

any user with SiteRole = Server Administrator throwing below error for method : t.update_user(username_or_luid=user, site_role=u'Unlicensed')

400 Client Error: Bad Request for url: https://{server.com}/api/3.3/sites/366dsdsd600cb-56fe-4ssabc-88-e99c566369f/users/dsdhtfn7-f5ad-4874-97ce-6bb68d

Is their any reason I can't set Server Administrator user role to Unlicensed? Apart from that role I can change to any role for any user with same method.

Username case sensitive comparison

It seems this script will fail is the case sensitivity of the source username changes over time. Minor issue but noted. Has to do with the xpath check (this is case sensitive) as such, things do get convoluted when considering unicode user names, xml formatting, api access via url, etc, etc.

Change dimension/measure flag and datatype of column in Oracle datasource

Thanks for the library. This is insanely full featured relative to the tableau document api. I'm creating a bunch of workbooks, but would like the resulting columns to correctly be dimensions and/or measure, and also ensure they have the correct datatypes. Is this possible with Oracle datasources? When I try the following code, I only get the one column:

from tableau_tools.tableau_documents import *

tf = TableauFile(filename='template.twb')
ds = tf.tableau_document.datasources[0]

for col in ds.columns.columns_list:
    print(col)

> <Element 'column' at 0x053B9870>

What am I missing?

Adopt a standard license

Great job on this project! The only concern I have is the very brief mention of rights in the README:
This is free and available for anyone to use and modify.

If you are comfortable with doing so, would you mind adding a standard license to the project? (MIT, GPL, etc..) I'm not a lawyer, so this is just something I noticed looking at the project overview.

In the past, I've found the following two sites helpful in understanding the general differences between the various licenses:
[https://tldrlegal.com/](tldr legal)
[http://choosealicense.com/](tldr legal)

Cheers -
Stephen

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.