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
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
15 changes: 6 additions & 9 deletions 15 Document API/Examples/Replicate Workbook/replicateWorkbook.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import csv # so we can work with our database list (in a CSV file)
import copy # to make copies

############################################################
# Step 1) Use Workbook object from the Document API
Expand All @@ -16,13 +15,11 @@
# create new .twb's with their settings
############################################################
with open('databases.csv') as csvfile:
next(csvfile) # Skip the first line which is our CSV header row
databases = csv.reader(csvfile, delimiter=',', quotechar='"')
databases = csv.DictReader(csvfile, delimiter=',', quotechar='"')
for row in databases:
newWB = copy.copy(sourceWB)

# Set our unique values for this database
newWB.datasources[0].connection.server = row[1] # Server
newWB.datasources[0].connection.dbname = row[2] # Database
newWB.datasources[0].connection.username = row[3] # User
newWB.save_as(row[0] + ' - Superstore' + '.twb') # Save our newly created .twb with the new file name
sourceWB.datasources[0].connection.server = row['Server']
sourceWB.datasources[0].connection.dbname = row['Database']
sourceWB.datasources[0].connection.username = row['User']
# Save our newly created .twb with the new file name
sourceWB.save_as(row['DBFriendlyName'] + ' - Superstore' + '.twb')
22 changes: 12 additions & 10 deletions 22 Document API/tableaudocumentapi/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# Connection - A class for writing connections to Tableau files
#
###############################################################################


class Connection(object):
"""
A class for writing connections to Tableau files.
Expand Down Expand Up @@ -36,13 +38,13 @@ def dbname(self):
def dbname(self, value):
"""
Set the connection's database name property.

Args:
value: New name of the database. String.

Returns:
Nothing.

"""
self._dbname = value
self._connectionXML.set('dbname', value)
Expand All @@ -58,17 +60,17 @@ def server(self):
def server(self, value):
"""
Set the connection's server property.

Args:
value: New server. String.

Returns:
Nothing.

"""
self._server = value
self._connectionXML.set('server', value)

###########
# username
###########
Expand All @@ -80,13 +82,13 @@ def username(self):
def username(self, value):
"""
Set the connection's username property.

Args:
value: New username value. String.

Returns:
Nothing.

"""
self._username = value
self._connectionXML.set('username', value)
9 changes: 5 additions & 4 deletions 9 Document API/tableaudocumentapi/datasource.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import xml.etree.ElementTree as ET
from tableaudocumentapi import Connection


class Datasource(object):
"""
A class for writing datasources to Tableau files.
Expand All @@ -23,23 +24,23 @@ def __init__(self, dsxml):

"""
self._datasourceXML = dsxml
self._name = self._datasourceXML.get('name')
self._name = self._datasourceXML.get('name') or self._datasourceXML.get('formatted-name') # TDS files don't have a name attribute
self._version = self._datasourceXML.get('version')
self._connection = Connection(self._datasourceXML.find('connection'))

@classmethod
def from_file(cls, filename):
"Initialize datasource from file (.tds)"
dsxml = ET.parse(filename).getroot()
return cls(dsxml)

###########
# name
###########
@property
def name(self):
return self._name

###########
# version
###########
Expand Down
45 changes: 21 additions & 24 deletions 45 Document API/tableaudocumentapi/workbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import xml.etree.ElementTree as ET
from tableaudocumentapi import Datasource


class Workbook(object):
"""
A class for writing Tableau workbook files.
Expand All @@ -29,13 +30,14 @@ def __init__(self, filename):
self._filename = filename
self._workbookTree = ET.parse(filename)
self._workbookRoot = self._workbookTree.getroot()

# prepare our datasource objects
self._datasources = self._prepare_datasources(self._workbookRoot) #self.workbookRoot.find('datasources')
self._datasources = self._prepare_datasources(
self._workbookRoot) # self.workbookRoot.find('datasources')
else:
print('Invalid file type. Must be .twb or .tds.')
raise Exception()

@classmethod
def from_file(cls, filename):
"Initialize datasource from file (.tds)"
Expand All @@ -46,14 +48,14 @@ def from_file(cls, filename):
else:
print('Invalid file type. Must be .twb or .tds.')
raise Exception()

###########
# datasources
###########
@property
def datasources(self):
return self._datasources

###########
# filename
###########
Expand All @@ -72,26 +74,26 @@ def save(self):
Nothing.

"""

# save the file
self._workbookTree.write(self._filename)
def save_as(self, value):

def save_as(self, new_filename):
"""
Save our file with the name provided.

Args:
value: New name for the workbook file. String.
new_filename: New name for the workbook file. String.

Returns:
Nothing.

"""

# We have a valid type of input file
if self._is_valid_file(value):
if self._is_valid_file(new_filename):
# save the file
self._workbookTree.write(value)
self._workbookTree.write(new_filename)
else:
print('Invalid file type. Must be .twb or .tds.')
raise Exception()
Expand All @@ -103,21 +105,16 @@ def save_as(self, value):
###########################################################################
def _prepare_datasources(self, xmlRoot):
datasources = []

# loop through our datasources and append
for datasource in xmlRoot.find('datasources'):
ds = Datasource(datasource)
datasources.append(ds)

return datasources
def _is_valid_file(self, filename):
valid = 0

@staticmethod
def _is_valid_file(filename):
fileExtension = os.path.splitext(filename)[-1].lower()

if fileExtension == ".twb":
valid = 1
elif fileExtension == ".tds":
valid = 1

return valid
return fileExtension in ('.twb', '.tds')

110 changes: 110 additions & 0 deletions 110 Document API/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import unittest
import io
import os
import xml.etree.ElementTree as ET

from tableaudocumentapi import Workbook, Datasource, Connection

TABLEAU_93_WORKBOOK = '''<?xml version='1.0' encoding='utf-8' ?>
<workbook source-build='9.3.1 (9300.16.0510.0100)' source-platform='mac' version='9.3' xmlns:user='http://www.tableausoftware.com/xml/user'>
<datasources>
<datasource caption='xy (TestV1)' inline='true' name='sqlserver.17u3bqc16tjtxn14e2hxh19tyvpo' version='9.3'>
<connection authentication='sspi' class='sqlserver' dbname='TestV1' odbc-native-protocol='yes' one-time-sql='' server='mssql2012.test.tsi.lan' username=''>
</connection>
</datasource>
</datasources>
</workbook>'''

TABLEAU_93_TDS = '''<?xml version='1.0' encoding='utf-8' ?>
<datasource formatted-name='sqlserver.17u3bqc16tjtxn14e2hxh19tyvpo' inline='true' source-platform='mac' version='9.3' xmlns:user='http://www.tableausoftware.com/xml/user'>
<connection authentication='sspi' class='sqlserver' dbname='TestV1' odbc-native-protocol='yes' one-time-sql='' server='mssql2012.test.tsi.lan' username=''>
</connection>
</datasource>'''

TABLEAU_CONNECTION_XML = ET.fromstring(
'''<connection authentication='sspi' class='sqlserver' dbname='TestV1' odbc-native-protocol='yes' one-time-sql='' server='mssql2012.test.tsi.lan' username=''></connection>''')


class HelperMethodTests(unittest.TestCase):

def test_is_valid_file_with_valid_inputs(self):
self.assertTrue(Workbook._is_valid_file('file1.tds'))
self.assertTrue(Workbook._is_valid_file('file2.twb'))
self.assertTrue(Workbook._is_valid_file('tds.twb'))

def test_is_valid_file_with_invalid_inputs(self):
self.assertFalse(Workbook._is_valid_file(''))
self.assertFalse(Workbook._is_valid_file('file1.tds2'))
self.assertFalse(Workbook._is_valid_file('file2.twb3'))


class ConnectionModelTests(unittest.TestCase):

def setUp(self):
self.connection = TABLEAU_CONNECTION_XML

def test_can_read_attributes_from_connection(self):
conn = Connection(self.connection)
self.assertEqual(conn.dbname, 'TestV1')
self.assertEqual(conn.username, '')
self.assertEqual(conn.server, 'mssql2012.test.tsi.lan')

def test_can_write_attributes_to_connection(self):
conn = Connection(self.connection)
conn.dbname = 'BubblesInMyDrink'
conn.server = 'mssql2014.test.tsi.lan'
self.assertEqual(conn.dbname, 'BubblesInMyDrink')
self.assertEqual(conn.username, '')
self.assertEqual(conn.server, 'mssql2014.test.tsi.lan')


class DatasourceModelTests(unittest.TestCase):

def setUp(self):
self.tds_file = io.FileIO('test.tds', 'w')
self.tds_file.write(TABLEAU_93_TDS.encode('utf8'))
self.tds_file.seek(0)

def tearDown(self):
self.tds_file.close()
os.unlink(self.tds_file.name)

def test_can_extract_datasource_from_file(self):
ds = Datasource.from_file(self.tds_file.name)
self.assertEqual(ds.name, 'sqlserver.17u3bqc16tjtxn14e2hxh19tyvpo')
self.assertEqual(ds.version, '9.3')

def test_can_extract_connection(self):
ds = Datasource.from_file(self.tds_file.name)
self.assertIsInstance(ds.connection, Connection)


class WorkbookModelTests(unittest.TestCase):

def setUp(self):
self.workbook_file = io.FileIO('test.twb', 'w')
self.workbook_file.write(TABLEAU_93_WORKBOOK.encode('utf8'))
self.workbook_file.seek(0)

def tearDown(self):
self.workbook_file.close()
os.unlink(self.workbook_file.name)

def test_can_extract_datasource(self):
wb = Workbook(self.workbook_file.name)
self.assertEqual(len(wb.datasources), 1)
self.assertIsInstance(wb.datasources[0], Datasource)
self.assertEqual(wb.datasources[0].name,
'sqlserver.17u3bqc16tjtxn14e2hxh19tyvpo')

def test_can_update_datasource_connection_and_save(self):
original_wb = Workbook(self.workbook_file.name)
original_wb.datasources[0].connection.dbname = 'newdb.test.tsi.lan'
original_wb.save()

new_wb = Workbook(self.workbook_file.name)
self.assertEqual(new_wb.datasources[0].connection.dbname, 'newdb.test.tsi.lan')


if __name__ == '__main__':
unittest.main()
Morty Proxy This is a proxified and sanitized view of the page, visit original site.