diff --git a/samples/show-fields/nested.tds b/samples/show-fields/nested.tds new file mode 100644 index 0000000..ed5c3dd --- /dev/null +++ b/samples/show-fields/nested.tds @@ -0,0 +1,780 @@ + + + + <_.fcp.ObjectModelEncapsulateLegacy.true...ObjectModelEncapsulateLegacy/> + <_.fcp.ObjectModelTableType.true...ObjectModelTableType/> + <_.fcp.SchemaViewerObjectModel.true...SchemaViewerObjectModel/> + + + + + + + + <_.fcp.ObjectModelEncapsulateLegacy.false...relation connection="sqlserver.1nzmabo1alszdd1dqm0c11g0qr0m" name="TestData" table="[dbo].[TestData]" type="table"/> + <_.fcp.ObjectModelEncapsulateLegacy.true...relation connection="sqlserver.1nzmabo1alszdd1dqm0c11g0qr0m" name="TestData" table="[dbo].[TestData]" type="table"/> + + + Account Account Name + 130 + [Account Account Name] + [TestData] + Account Account Name + 1 + string + Count + 255 + true + true + + + "SQL_WVARCHAR" + "SQL_C_WCHAR" + "true" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Account Number + 5 + [Account Number] + [TestData] + Account Number + 2 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Account Number Burst Out Account + 130 + [Account Number Burst Out Account] + [TestData] + Account Number Burst Out Account + 3 + string + Count + 255 + true + true + + + "SQL_WVARCHAR" + "SQL_C_WCHAR" + "true" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Acct Name + 130 + [Acct Name] + [TestData] + Acct Name + 4 + string + Count + 255 + true + true + + + "SQL_WVARCHAR" + "SQL_C_WCHAR" + "true" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Burst Out + 130 + [Burst Out] + [TestData] + Burst Out + 5 + string + Count + 255 + true + true + + + "SQL_WVARCHAR" + "SQL_C_WCHAR" + "true" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Burst Out Join + 130 + [Burst Out Join] + [TestData] + Burst Out Join + 6 + string + Count + 255 + true + true + + + "SQL_WVARCHAR" + "SQL_C_WCHAR" + "true" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Burst Out Set list + 5 + [Burst Out Set list] + [TestData] + Burst Out Set list + 7 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Burst Out View + 5 + [Burst Out View] + [TestData] + Burst Out View + 8 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Count JE Number + 5 + [Count JE Number] + [TestData] + Count JE Number + 9 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Entity ID + 130 + [Entity ID] + [TestData] + Entity ID + 10 + string + Count + 255 + true + true + + + "SQL_WVARCHAR" + "SQL_C_WCHAR" + "true" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Filter + 5 + [Filter] + [TestData] + Filter + 11 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Fiscal Year + 130 + [Fiscal Year] + [TestData] + Fiscal Year + 12 + string + Count + 255 + true + true + + + "SQL_WVARCHAR" + "SQL_C_WCHAR" + "true" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Flag + 11 + [Flag] + [TestData] + Flag + 13 + boolean + Count + false + + "SQL_BIT" + "SQL_C_BIT" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Flag__copy_ + 11 + [Flag__copy_] + [TestData] + Flag__copy_ + 14 + boolean + Count + false + + "SQL_BIT" + "SQL_C_BIT" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + FS Line + 5 + [FS Line] + [TestData] + FS Line + 15 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + FS Line Burst Out Account + 130 + [FS Line Burst Out Account] + [TestData] + FS Line Burst Out Account + 16 + string + Count + 255 + true + true + + + "SQL_WVARCHAR" + "SQL_C_WCHAR" + "true" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Group By + 5 + [Group By] + [TestData] + Group By + 17 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Image + 130 + [Image] + [TestData] + Image + 18 + string + Count + 255 + true + true + + + "SQL_WVARCHAR" + "SQL_C_WCHAR" + "true" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Note Line + 5 + [Note Line] + [TestData] + Note Line + 19 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Note Line Burst Out Account + 130 + [Note Line Burst Out Account] + [TestData] + Note Line Burst Out Account + 20 + string + Count + 255 + true + true + + + "SQL_WVARCHAR" + "SQL_C_WCHAR" + "true" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Selection + 5 + [Selection] + [TestData] + Selection + 21 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + show + 130 + [show] + [TestData] + show + 22 + string + Count + 255 + true + true + + + "SQL_WVARCHAR" + "SQL_C_WCHAR" + "true" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Show Cycle Based + 130 + [Show Cycle Based] + [TestData] + Show Cycle Based + 23 + string + Count + 255 + true + true + + + "SQL_WVARCHAR" + "SQL_C_WCHAR" + "true" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Sub Class + 5 + [Sub Class] + [TestData] + Sub Class + 24 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + SubClass Burst Out Account + 130 + [SubClass Burst Out Account] + [TestData] + SubClass Burst Out Account + 25 + string + Count + 255 + true + true + + + "SQL_WVARCHAR" + "SQL_C_WCHAR" + "true" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Type + 130 + [Type] + [TestData] + Type + 26 + string + Count + 255 + true + true + + + "SQL_WVARCHAR" + "SQL_C_WCHAR" + "true" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Amount + 130 + [Amount] + [TestData] + Amount + 27 + string + Count + 255 + true + true + + + "SQL_WVARCHAR" + "SQL_C_WCHAR" + "true" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Amount1 + 130 + [Amount1] + [TestData] + Amount1 + 28 + string + Count + 255 + true + true + + + "SQL_WVARCHAR" + "SQL_C_WCHAR" + "true" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Count of Amount Calculation + 5 + [Count of Amount Calculation] + [TestData] + Count of Amount Calculation + 29 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Number of Records + 5 + [Number of Records] + [TestData] + Number of Records + 30 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Number of Records1 + 5 + [Number of Records1] + [TestData] + Number of Records1 + 31 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Select Burst Out + 5 + [Select Burst Out] + [TestData] + Select Burst Out + 32 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Select Transaction Analysis view + 5 + [Select Transaction Analysis view] + [TestData] + Select Transaction Analysis view + 33 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Sum Cy + 5 + [Sum Cy] + [TestData] + Sum Cy + 34 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Sum Py1 + 5 + [Sum Py1] + [TestData] + Sum Py1 + 35 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Sum Py2 + 5 + [Sum Py2] + [TestData] + Sum Py2 + 36 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Sum Py3 + 5 + [Sum Py3] + [TestData] + Sum Py3 + 37 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Sum Py4 + 5 + [Sum Py4] + [TestData] + Sum Py4 + 38 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Total Credits + 5 + [Total Credits] + [TestData] + Total Credits + 39 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + Total Debits + 5 + [Total Debits] + [TestData] + Total Debits + 40 + real + Sum + 15 + true + + "SQL_FLOAT" + "SQL_C_DOUBLE" + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-id>[TestData_44D2C885FAEF453C846AC2CCD3577055] + + + + + + + + + + + + + + + + + + + + <_.fcp.ObjectModelTableType.true...column caption="TestData" datatype="table" name="[__tableau_internal_object_id__].[TestData_44D2C885FAEF453C846AC2CCD3577055]" role="measure" type="quantitative"/> + + + + + + + + + + <_.fcp.ObjectModelEncapsulateLegacy.true...object-graph> + + + + + + + + + \ No newline at end of file diff --git a/samples/show-fields/show_fields.py b/samples/show-fields/show_fields.py index e12a817..f0fe493 100644 --- a/samples/show-fields/show_fields.py +++ b/samples/show-fields/show_fields.py @@ -6,30 +6,34 @@ ############################################################ # Step 2) Open the .tds we want to inspect ############################################################ -sourceTDS = Datasource.from_file('world.tds') +datasources = [Datasource.from_file('world.tds'), Datasource.from_file('nested.tds')] +for sourceTDS in datasources: -############################################################ -# Step 3) Print out all of the fields and what type they are -############################################################ -print('----------------------------------------------------------') -print('-- Info for our .tds:') -print('-- name:\t{0}'.format(sourceTDS.name)) -print('-- version:\t{0}'.format(sourceTDS.version)) -print('----------------------------------------------------------') -print('--- {} total fields in this datasource'.format(len(sourceTDS.fields))) -print('----------------------------------------------------------') -for count, field in enumerate(sourceTDS.fields.values()): - print('{:>4}: {} is a {}'.format(count+1, field.name, field.datatype)) - blank_line = False - if field.calculation: - print(' the formula is {}'.format(field.calculation)) - blank_line = True - if field.default_aggregation: - print(' the default aggregation is {}'.format(field.default_aggregation)) - blank_line = True - if field.description: - print(' the description is {}'.format(field.description)) + ############################################################ + # Step 3) Print out all of the fields and what type they are + ############################################################ + print('----------------------------------------------------------') + print('-- Info for our .tds:') + print('-- name:\t{0}'.format(sourceTDS.name)) + print('-- version:\t{0}'.format(sourceTDS.version)) + print('----------------------------------------------------------') + print('--- {} total fields in this datasource'.format(len(sourceTDS.fields))) + print('----------------------------------------------------------') + for count, field in enumerate(sourceTDS.fields.values()): + blank_line = False + if field.calculation: + print('{:>4}: field named `{}` is a `{}`'.format(count+1, field.name, field.datatype)) + print(' field id, caption, calculation: `{}`, `{}`, `{}`'.format(field.id, field.caption, + field.calculation)) + blank_line = True + if field.default_aggregation: + print('{:>4}: `{}` is a `{}`, default aggregation is `{}`'.format(count+1, field.name, field.datatype, + field.default_aggregation)) + + if field.description: + print('{:>4}: `{}` is a `{}`, description is `{}`'.format(count+1, field.name, field.datatype, + field.description)) - if blank_line: - print('') -print('----------------------------------------------------------') + if blank_line: + print('') + print('----------------------------------------------------------') diff --git a/samples/show_workbook_info/show_workbook_info.py b/samples/show_workbook_info/show_workbook_info.py index e2a6034..d83d254 100644 --- a/samples/show_workbook_info/show_workbook_info.py +++ b/samples/show_workbook_info/show_workbook_info.py @@ -2,7 +2,7 @@ # Step 1) Use Datasource object from the Document API ############################################################ from tableaudocumentapi import Workbook -import xml.etree.ElementTree as ET +from lxml import etree as ET ############################################################ # Step 2) Open the .tds we want to explore diff --git a/setup.py b/setup.py index 3ea2643..3f5ba92 100644 --- a/setup.py +++ b/setup.py @@ -9,5 +9,6 @@ packages=['tableaudocumentapi'], license='MIT', description='A Python module for working with Tableau files.', - test_suite='test' + test_suite='test', + install_requires=['lxml'] ) diff --git a/tableaudocumentapi/connection.py b/tableaudocumentapi/connection.py index 2fb13ed..caa5a61 100644 --- a/tableaudocumentapi/connection.py +++ b/tableaudocumentapi/connection.py @@ -1,4 +1,4 @@ -import xml.etree.ElementTree as ET +from lxml import etree as ET from tableaudocumentapi.dbclass import is_valid_dbclass diff --git a/tableaudocumentapi/datasource.py b/tableaudocumentapi/datasource.py index ccf6c91..abeb603 100644 --- a/tableaudocumentapi/datasource.py +++ b/tableaudocumentapi/datasource.py @@ -1,6 +1,6 @@ import collections import itertools -import xml.etree.ElementTree as ET +from lxml import etree as ET import xml.sax.saxutils as sax from uuid import uuid4 diff --git a/tableaudocumentapi/field.py b/tableaudocumentapi/field.py index e85500c..8df87bf 100644 --- a/tableaudocumentapi/field.py +++ b/tableaudocumentapi/field.py @@ -1,5 +1,5 @@ import functools -import xml.etree.ElementTree as ET +from lxml import etree as ET from xml.dom import minidom from tableaudocumentapi.property_decorators import argument_is_one_of @@ -277,7 +277,7 @@ def add_alias(self, key, value): # determine whether there already is an aliases-tag aliases = self._xml.find('aliases') # and create it if there isn't - if not aliases: + if not aliases: # ignore the FutureWarning, does not apply to our usage aliases = ET.Element('aliases') self._xml.append(aliases) @@ -298,7 +298,7 @@ def aliases(self): Returns: Key-value mappings of all registered aliases. Dict. """ - aliases_tag = self._xml.find('aliases') or [] + aliases_tag = self._xml.find('aliases') or [] # ignore the FutureWarning, does not apply to our usage return {a.get('key', 'None'): a.get('value', 'None') for a in list(aliases_tag)} ######################################## diff --git a/tableaudocumentapi/xfile.py b/tableaudocumentapi/xfile.py index 10d517e..8d2bb9f 100644 --- a/tableaudocumentapi/xfile.py +++ b/tableaudocumentapi/xfile.py @@ -3,7 +3,7 @@ import shutil import tempfile import zipfile -import xml.etree.ElementTree as ET +from lxml import etree as ET from distutils.version import LooseVersion as Version diff --git a/test/bvt.py b/test/bvt.py index c2854ad..f8fd43b 100644 --- a/test/bvt.py +++ b/test/bvt.py @@ -1,7 +1,7 @@ import os import unittest -import xml.etree.ElementTree as ET +from lxml import etree as ET from test.assets.index import * from tableaudocumentapi import Workbook, Datasource, Connection, ConnectionParser from tableaudocumentapi.xfile import TableauInvalidFileException, TableauVersionNotSupportedException @@ -160,7 +160,7 @@ def test_save_has_xml_declaration(self): with open(self.tds_file.name) as f: first_line = f.readline().strip() # first line should be xml tag self.assertEqual( - first_line, "") + first_line, "") class DatasourceModelV10Tests(unittest.TestCase): @@ -323,7 +323,7 @@ def test_save_has_xml_declaration(self): with open(self.workbook_file.name) as f: first_line = f.readline().strip() # first line should be xml tag self.assertEqual( - first_line, "") + first_line, "") class WorkbookModelV10TWBXTests(unittest.TestCase): diff --git a/test/test_field_change.py b/test/test_field_change.py index 7a84f1e..ae64259 100644 --- a/test/test_field_change.py +++ b/test/test_field_change.py @@ -2,7 +2,7 @@ import os.path from tableaudocumentapi import Datasource -import xml.etree.ElementTree as ET +from lxml import etree as ET TEST_ASSET_DIR = os.path.join( diff --git a/test/test_xfile.py b/test/test_xfile.py index 3da133a..c9686f5 100644 --- a/test/test_xfile.py +++ b/test/test_xfile.py @@ -53,21 +53,10 @@ def test_save_preserves_namespace_twb(self): wb.save_as(new_name) self.assertContainsUserNamespace(new_name) - ''' def demo_bug_ns_not_preserved_if_not_used(self): filename = TABLEAU_10_TDS self.assertContainsUserNamespace(filename) wb = Datasource.from_file(filename) - #wb.save() new_name = 'saved-as-tds.tds' wb.save_as(new_name) - self.assertContainsUserNamespace(new_name) <- throws - - If there is no namespace in the document when you begin working with it, - none will be added. - If there is a namespace but it *is not used* in the document, it will be stripped - - Fix will be something like - https://stackoverflow.com/questions/41937624/elementtree-why-are-my-namespace-declarations-stripped-out - - ''' + self.assertContainsUserNamespace(new_name)