From dea1dac1161ccc7e80b89c746a94ed2fcfb3bc96 Mon Sep 17 00:00:00 2001 From: Bryant Howell Date: Wed, 13 Nov 2019 09:59:30 -0600 Subject: [PATCH] Changed alias for ElementTree from etree to ET to reflect current Python docs --- README.md | 4 +- tableau_base.py | 10 +-- tableau_documents/tableau_connection.py | 4 +- tableau_documents/tableau_datasource.py | 94 +++++++++++------------ tableau_documents/tableau_file.py | 4 +- tableau_documents/tableau_parameters.py | 26 +++---- tableau_rest_api/content_deployer.py | 37 --------- tableau_rest_api/methods/alert.py | 18 ++--- tableau_rest_api/methods/datasource.py | 36 ++++----- tableau_rest_api/methods/extract.py | 22 +++--- tableau_rest_api/methods/flow.py | 40 +++++----- tableau_rest_api/methods/group.py | 38 ++++----- tableau_rest_api/methods/project.py | 30 ++++---- tableau_rest_api/methods/publishing.py | 14 ++-- tableau_rest_api/methods/rest_api_base.py | 68 ++++++++-------- tableau_rest_api/methods/revision.py | 4 +- tableau_rest_api/methods/site.py | 12 +-- tableau_rest_api/methods/subscription.py | 22 +++--- tableau_rest_api/methods/user.py | 30 ++++---- tableau_rest_api/methods/workbook.py | 48 ++++++------ tableau_rest_api/published_content.py | 38 ++++----- tableau_rest_api/rest_json_request.py | 12 +-- tableau_rest_api/rest_xml_request.py | 24 +++--- 23 files changed, 300 insertions(+), 335 deletions(-) delete mode 100644 tableau_rest_api/content_deployer.py diff --git a/README.md b/README.md index 6b56a82..f48487c 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,8 @@ It should never be necessary to use TableauBase by itself. ### 0.4 tableau_exceptions The tableau_exceptions file defines a variety of Exceptions that are specific to Tableau, particularly the REST API. They are not very complex, and most simply include a msg property that will clarify the problem if logged +### 0.5 ElementTree elements for XML +All XML in tableau_tools is handled through ElementTree. It is aliased ## 1. tableau_rest_api sub-package @@ -365,7 +367,7 @@ tableau_tools handles translations between real world names and LUIDs automatica There are few cases where only the LUID can be accepted. In this case, the parameter will show just "_luid". -#### 1.2.2 Plural querying methods and converting to name : luid dicts +#### 1.2.2 Plural querying methods and converting to { name : luid} Dict The simplest method for getting information from the REST API are the "plural" querying methods `TableauRestApiConnection.query_groups()` diff --git a/tableau_base.py b/tableau_base.py index 139013d..fd0dcc5 100644 --- a/tableau_base.py +++ b/tableau_base.py @@ -4,7 +4,7 @@ import re from io import StringIO -import xml.etree.cElementTree as etree +import xml.etree.ElementTree as ET class TableauBase(object): @@ -20,7 +20,7 @@ def __init__(self): self.tableau_namespace = 'http://tableau.com/api' self.ns_map = {'t': 'http://tableau.com/api'} self.ns_prefix = '{' + self.ns_map['t'] + '}' - etree.register_namespace('t', self.ns_map['t']) + ET.register_namespace('t', self.ns_map['t']) self.site_roles = ( 'Interactor', @@ -444,13 +444,13 @@ def convert_view_content_url_to_embed_url(content_url): # Generic method for XML lists for the "query" actions to name -> id dict @staticmethod - def convert_xml_list_to_name_id_dict(etree_obj): + def convert_xml_list_to_name_id_dict(ET_obj): """ - :type etree_obj: etree.Element + :type ET_obj: ET.Element :return: dict """ d = {} - for element in etree_obj: + for element in ET_obj: e_id = element.get("id") # If list is collection, have to run one deeper if e_id is None: diff --git a/tableau_documents/tableau_connection.py b/tableau_documents/tableau_connection.py index 437eab8..f93cd14 100644 --- a/tableau_documents/tableau_connection.py +++ b/tableau_documents/tableau_connection.py @@ -1,6 +1,6 @@ from ..tableau_base import * from ..tableau_exceptions import * -import xml.etree.cElementTree as etree +import xml.etree.ElementTree as ET # Represents the actual Connection tag of a given datasource @@ -19,7 +19,7 @@ def __init__(self, connection_xml_obj, logger_obj=None): @property def cols(self): """ - :rtype: etree.Element + :rtype: ET.Element """ return self.xml_obj.find('cols') diff --git a/tableau_documents/tableau_datasource.py b/tableau_documents/tableau_datasource.py index fc2bf70..9b5a002 100644 --- a/tableau_documents/tableau_datasource.py +++ b/tableau_documents/tableau_datasource.py @@ -3,7 +3,7 @@ from .tableau_document import TableauDocument from .tableau_columns import TableauColumns -import xml.etree.cElementTree as etree +import xml.etree.ElementTree as ET from ..tableau_exceptions import * import zipfile import os @@ -19,13 +19,13 @@ class TableauDatasource(TableauDocument): def __init__(self, datasource_xml=None, logger_obj=None, ds_version=None): """ - :type datasource_xml: etree.Element + :type datasource_xml: ET.Element :type logger_obj: Logger :type ds_version: unicode """ TableauDocument.__init__(self) self._document_type = 'datasource' - etree.register_namespace('t', self.ns_map['t']) + ET.register_namespace('t', self.ns_map['t']) self.logger = logger_obj self._connections = [] self.ds_name = None @@ -251,7 +251,7 @@ def update_tables_with_new_database_or_schema(self, original_db_or_schema, new_d @staticmethod def create_new_datasource_xml(version): # nsmap = {u"user": u'http://www.tableausoftware.com/xml/user'} - ds_xml = etree.Element("datasource") + ds_xml = ET.Element("datasource") ds_xml.set('version', version) ds_xml.set('inline', "true") return ds_xml @@ -267,11 +267,11 @@ def create_new_connection_xml(ds_version, ds_type, server, db_name, authenticati :type initial_sql: unicode :return: """ - connection = etree.Element("connection") + connection = ET.Element("connection") if ds_version == '9': c = connection elif ds_version in ['10', '10.5']: - nc = etree.Element('named-connection') + nc = ET.Element('named-connection') nc.set('caption', 'Connection') # Connection has a random number of 20 digits appended rnumber = random.randrange(10**20, 10**21) @@ -296,14 +296,14 @@ def add_new_connection(self, ds_type, server=None, db_or_schema_name=None, authe self.start_log_block() self.ds_generator = True conn = self.create_new_connection_xml(self.ds_version_type, ds_type, server, db_or_schema_name, authentication, initial_sql) - print((etree.tostring(conn))) + print((ET.tostring(conn))) if self.ds_version_type == '9': self.xml.append(conn) self._connection_root = conn elif self.ds_version_type in ['10', '10.5']: - c = etree.Element('connection') + c = ET.Element('connection') c.set('class', 'federated') - ncs = etree.Element('named-connections') + ncs = ET.Element('named-connections') ncs.append(copy.deepcopy(conn)) c.append(ncs) self.xml.append(c) @@ -387,7 +387,7 @@ def get_datasource_xml(self): new_xml.append(new_l) new_xml.append(new_sv) - xmlstring = etree.tostring(new_xml) + xmlstring = ET.tostring(new_xml) self.end_log_block() return xmlstring @@ -487,12 +487,12 @@ def generate_extract_section(self): print("Must have correct install of Tableau Extract SDK to add extracts") print('Exception arising from the Tableau Extract SDK itself') raise - e = etree.Element('extract') + e = ET.Element('extract') e.set('count', '-1') e.set('enabled', 'true') e.set('units', 'records') - c = etree.Element('connection') + c = ET.Element('connection') if self.ds_version_type in ['9', '10']: c.set('class', 'dataengine') # 10.5 has hyper data sources instead @@ -511,20 +511,20 @@ def generate_extract_section(self): pretty_date = right_now.strftime("%m/%d/%Y %H:%M:%S %p") c.set('update-time', pretty_date) - r = etree.Element('relation') + r = ET.Element('relation') r.set("name", "Extract") r.set("table", "[Extract].[Extract]") r.set("type", "table") c.append(r) - calcs = etree.Element("calculations") - calc = etree.Element("calculation") + calcs = ET.Element("calculations") + calc = ET.Element("calculation") calc.set("column", "[Number of Records]") calc.set("formula", "1") calcs.append(calc) c.append(calcs) - ref = etree.Element('refresh') + ref = ET.Element('refresh') if self.incremental_refresh_field is not None: ref.set("increment-key", self.incremental_refresh_field) ref.set("incremental-updates", 'true') @@ -651,7 +651,7 @@ def get_stored_proc_parameter_value_by_name(self, parameter_name): def set_stored_proc_parameter_value_by_name(self, parameter_name, parameter_value): # Create if there is none if self._stored_proc_parameters_xml is None: - self._stored_proc_parameters_xml = etree.Element('actual-parameters') + self._stored_proc_parameters_xml = ET.Element('actual-parameters') # Find parameter with that name (if exists) param = self._stored_proc_parameters_xml.find('.//column[@name="{}"]'.format(parameter_name), self.ns_map) @@ -675,11 +675,11 @@ def create_stored_proc_parameter(parameter_name, parameter_value): """ :type parameter_name: unicode :type parameter_value: all - :return: etree.Element + :return: ET.Element """ if parameter_name is None or parameter_value is None: raise InvalidOptionException('Must specify both a parameter_name (starting with @) and a parameter_value') - c = etree.Element('column') + c = ET.Element('column') # Check to see if this varies at all depending on type or whatever c.set('ordinal', '1') if parameter_name[0] != '@': @@ -704,7 +704,7 @@ def create_random_calculation_name(): @staticmethod def create_table_relation(db_table_name, table_alias, connection=None, extract=False): - r = etree.Element("relation") + r = ET.Element("relation") r.set('name', table_alias) if extract is True: r.set("table", "[Extract].[{}]".format(db_table_name)) @@ -717,7 +717,7 @@ def create_table_relation(db_table_name, table_alias, connection=None, extract=F @staticmethod def create_custom_sql_relation(custom_sql, table_alias, connection=None): - r = etree.Element("relation") + r = ET.Element("relation") r.set('name', table_alias) r.text = custom_sql r.set("type", "text") @@ -727,7 +727,7 @@ def create_custom_sql_relation(custom_sql, table_alias, connection=None): @staticmethod def create_stored_proc_relation(custom_sql, table_alias, connection=None, actual_parameters=None): - r = etree.Element("relation") + r = ET.Element("relation") r.set('name', table_alias) r.text = custom_sql r.set("type", "stored-proc") @@ -762,7 +762,7 @@ def generate_relation_section(self, connection_name=None): #if self.relation_xml_obj is not None: # self.relation_xml_obj.clear() #else: - rel_xml_obj = etree.Element("relation") + rel_xml_obj = ET.Element("relation") # There's only a single main relation with only one table if len(self.join_relations) == 0: @@ -779,7 +779,7 @@ def generate_relation_section(self, connection_name=None): #print(self.join_relations) for join_desc in self.join_relations: - r = etree.Element("relation") + r = ET.Element("relation") r.set("join", join_desc["join_type"]) r.set("type", "join") if len(join_desc["on_clauses"]) == 0: @@ -787,21 +787,21 @@ def generate_relation_section(self, connection_name=None): else: and_expression = None if len(join_desc["on_clauses"]) > 1: - and_expression = etree.Element("expression") + and_expression = ET.Element("expression") and_expression.set("op", 'AND') for on_clause in join_desc["on_clauses"]: - c = etree.Element("clause") + c = ET.Element("clause") c.set("type", "join") - e = etree.Element("expression") + e = ET.Element("expression") e.set("op", on_clause["operator"]) - e_field1 = etree.Element("expression") + e_field1 = ET.Element("expression") e_field1_name = '[{}].[{}]'.format(on_clause["left_table_alias"], on_clause["left_field"]) e_field1.set("op", e_field1_name) e.append(e_field1) - e_field2 = etree.Element("expression") + e_field2 = ET.Element("expression") e_field2_name = '[{}].[{}]'.format(on_clause["right_table_alias"], on_clause["right_field"]) e_field2.set("op", e_field2_name) @@ -817,7 +817,7 @@ def generate_relation_section(self, connection_name=None): if join_desc["custom_sql"] is None: # Append the main table first (not sure this works for more deep hierarchies, but let's see - main_rel_xml_obj = etree.Element('relation') + main_rel_xml_obj = ET.Element('relation') for item in list(self.main_table_relation.items()): main_rel_xml_obj.set(item[0], item[1]) if self.main_table_relation.text is not None: @@ -999,18 +999,18 @@ def create_relative_date_filter(self, column_name, period_type, number_of_period def generate_filters(self, filter_array): return_array = [] for filter_def in filter_array: - f = etree.Element('filter') + f = ET.Element('filter') f.set('class', filter_def['type']) f.set('column', filter_def['column_name']) f.set('filter-group', '2') if filter_def['type'] == 'quantitative': f.set('include-values', 'in-range') if filter_def['min'] is not None: - m = etree.Element('min') + m = ET.Element('min') m.text = str(filter_def['min']) f.append(m) if filter_def['max'] is not None: - m = etree.Element('max') + m = ET.Element('max') m.text = str(filter_def['max']) f.append(m) elif filter_def['type'] == 'relative-date': @@ -1021,7 +1021,7 @@ def generate_filters(self, filter_array): f.set('period-type', filter_def['period-type']) elif filter_def['type'] == 'categorical': - gf = etree.Element('groupfilter') + gf = ET.Element('groupfilter') # This attribute has a user namespace gf.set('{' + '{}'.format(self.nsmap['user']) + '}ui-domain', 'database') gf.set('{' + '{}'.format(self.nsmap['user']) + '}ui-enumeration', filter_def['ui-enumeration']) @@ -1031,7 +1031,7 @@ def generate_filters(self, filter_array): if len(filter_def['values']) == 1: if filter_def['ui-enumeration'] == 'exclusive': gf.set('function', 'except') - gf1 = etree.Element('groupfilter') + gf1 = ET.Element('groupfilter') gf1.set('function', 'member') gf1.set('level', filter_def['column_name']) else: @@ -1045,7 +1045,7 @@ def generate_filters(self, filter_array): gf1.set('member', str(filter_def['values'][0])) if filter_def['ui-enumeration'] == 'exclusive': # Single exclude filters include an extra groupfilter set with level-members function - lm = etree.Element('groupfilter') + lm = ET.Element('groupfilter') lm.set('function', 'level-members') lm.set('level', filter_def['column_name']) gf.append(lm) @@ -1057,7 +1057,7 @@ def generate_filters(self, filter_array): else: gf.set('function', 'union') for val in filter_def['values']: - gf1 = etree.Element('groupfilter') + gf1 = ET.Element('groupfilter') gf1.set('function', 'member') gf1.set('level', filter_def['column_name']) # String types need " , ints do not @@ -1080,9 +1080,9 @@ def generate_datasource_filters_section(self): def generate_cols_map_section(self): if len(self.column_mapping) == 0: return False - c = etree.Element("cols") + c = ET.Element("cols") for key in self.column_mapping: - m = etree.Element("map") + m = ET.Element("map") m.set("key", "[{}]".format(key)) m.set("value", self.column_mapping[key]) c.append(m) @@ -1091,25 +1091,25 @@ def generate_cols_map_section(self): @staticmethod def generate_aliases_tag(): # For whatever reason, the aliases tag does not contain the columns, but it always precedes it - a = etree.Element("aliases") + a = ET.Element("aliases") a.set("enabled", "yes") return a def generate_aliases_column_section(self): """ - :rtype: list[etree.Element] + :rtype: list[ET.Element] """ column_aliases_array = [] # Now to put in each column tag for column_alias in self.column_aliases: - c = etree.Element("column") + c = ET.Element("column") print("Column section") - print((etree.tostring(c))) + print((ET.tostring(c))) # Name is the Tableau Field Alias, always surrounded by brackets SQL Server style c.set("name", "[{}]".format(column_alias)) print("Column section 2") - print((etree.tostring(c))) + print((ET.tostring(c))) if self.column_aliases[column_alias]["datatype"] is not None: c.set("datatype", self.column_aliases[column_alias]["datatype"]) if self.column_aliases[column_alias]["caption"] is not None: @@ -1119,20 +1119,20 @@ def generate_aliases_column_section(self): if self.column_aliases[column_alias]["type"] is not None: c.set("type", self.column_aliases[column_alias]["type"]) if self.column_aliases[column_alias]['calculation'] is not None: - calc = etree.Element('calculation') + calc = ET.Element('calculation') calc.set('class', 'tableau') # quoteattr adds an extra real set of quotes around the string, which needs to be sliced out calc.set('formula', quoteattr(self.column_aliases[column_alias]['calculation'])[1:-1]) c.append(calc) print("Column section at end") - print((etree.tostring(c))) + print((ET.tostring(c))) column_aliases_array.append(c) return column_aliases_array def generate_column_instances_section(self): column_instances_array = [] for column_instance in self.column_instances: - ci = etree.Element('column-instance') + ci = ET.Element('column-instance') ci.set('column', column_instance['column']) ci.set('derivation', 'None') ci.set('name', column_instance['name']) diff --git a/tableau_documents/tableau_file.py b/tableau_documents/tableau_file.py index 766454d..4547e3c 100644 --- a/tableau_documents/tableau_file.py +++ b/tableau_documents/tableau_file.py @@ -95,9 +95,9 @@ def __init__(self, filename, logger_obj=None, create_new=False, ds_version='10') o_ds_fh.close() ds_fh.close() - utf8_parser = etree.XMLParser(encoding='utf-8') + utf8_parser = ET.XMLParser(encoding='utf-8') - ds_xml = etree.parse('temp_file.txt', parser=utf8_parser) + ds_xml = ET.parse('temp_file.txt', parser=utf8_parser) self._tableau_document = TableauDatasource(ds_xml.getroot(), self.logger) self.xml_name = None diff --git a/tableau_documents/tableau_parameters.py b/tableau_documents/tableau_parameters.py index f5849f1..eb7fc6f 100644 --- a/tableau_documents/tableau_parameters.py +++ b/tableau_documents/tableau_parameters.py @@ -1,7 +1,7 @@ from ..tableau_base import * from .tableau_document import TableauDocument -import xml.etree.cElementTree as etree +import xml.etree.ElementTree as ET from ..tableau_exceptions import * from xml.sax.saxutils import quoteattr, unescape @@ -12,7 +12,7 @@ class TableauParameters(TableauDocument): def __init__(self, datasource_xml=None, logger_obj=None): """ - :type datasource_xml: etree.Element + :type datasource_xml: ET.Element :type logger_obj: Logger """ TableauDocument.__init__(self) @@ -25,12 +25,12 @@ def __init__(self, datasource_xml=None, logger_obj=None): if datasource_xml is None: self.log('No Parameter XML passed in, building from scratch') - self.ds_xml = etree.Element("datasource") + self.ds_xml = ET.Element("datasource") self.ds_xml.set('name', 'Parameters') # Initialization of the datasource self.ds_xml.set('hasconnection', 'false') self.ds_xml.set('inline', 'true') - a = etree.Element('aliases') + a = ET.Element('aliases') a.set('enabled', 'yes') self.ds_xml.append(a) else: @@ -55,7 +55,7 @@ def __init__(self, datasource_xml=None, logger_obj=None): def get_datasource_xml(self): self.start_log_block() - xmlstring = etree.tostring(self.ds_xml) + xmlstring = ET.tostring(self.ds_xml) self.end_log_block() return xmlstring @@ -103,7 +103,7 @@ class TableauParameter(TableauBase): def __init__(self, parameter_xml=None, parameter_number=None, logger_obj=None, name=None, datatype=None, current_value=None): """ - :type parameter_xml: etree.Element + :type parameter_xml: ET.Element :type logger_obj: Logger """ TableauBase.__init__(self) @@ -120,7 +120,7 @@ def __init__(self, parameter_xml=None, parameter_number=None, logger_obj=None, n else: if parameter_number is None: raise InvalidOptionException('Must pass a parameter_number if creating a new Parameter') - self.p_xml = etree.Element("column") + self.p_xml = ET.Element("column") self.p_xml.set('name', '[Parameter {}]'.format(str(parameter_number))) self.p_xml.set('role', 'measure') # Set allowable_values to all by default @@ -188,7 +188,7 @@ def set_allowable_values_to_range(self, minimum=None, maximum=None, step_size=No # See if a range already exists, otherwise create it r = self.p_xml.find('./range', self.ns_map) if r is None: - r = etree.Element('range') + r = ET.Element('range') self.p_xml.append(r) # Set any new values that come through @@ -230,16 +230,16 @@ def set_allowable_values_to_list(self, list_value_display_as_pairs): self.p_xml.remove(m) aliases = None - members = etree.Element('members') + members = ET.Element('members') for value_pair in list_value_display_as_pairs: for value in value_pair: - member = etree.Element('member') + member = ET.Element('member') member.set('value', str(value)) if value_pair[value] is not None: if aliases is None: - aliases = etree.Element('aliases') - alias = etree.Element('alias') + aliases = ET.Element('aliases') + alias = ET.Element('alias') alias.set('key', str(value)) alias.set('value', str(value_pair[value])) member.set('alias', str(value_pair[value])) @@ -293,7 +293,7 @@ def current_value(self, current_value): actual_value = value # Why there have to be a calculation tag as well? I don't know, but there does - calc = etree.Element('calculation') + calc = ET.Element('calculation') calc.set('class', 'tableau') if isinstance(current_value, str) and self.datatype not in ['date', 'datetime']: self.p_xml.set('value', quoteattr(actual_value)) diff --git a/tableau_rest_api/content_deployer.py b/tableau_rest_api/content_deployer.py deleted file mode 100644 index 4e979e2..0000000 --- a/tableau_rest_api/content_deployer.py +++ /dev/null @@ -1,37 +0,0 @@ -class ContentDeployer: - def __init__(self): - self._current_site_index = 0 - self.sites = [] - - def __iter__(self): - """ - :rtype: TableauRestApiConnection - """ - return self.sites[self._current_site_index] - - def add_site(self, t_rest_api_connection): - """ - :type t_rest_api_connection: TableauRestApiConnection - """ - self.sites.append(t_rest_api_connection) - - @property - def current_site(self): - """ - :rtype: TableauRestApiConnection - """ - return self.sites[self._current_site_index] - - @current_site.setter - def current_site(self, site_content_url): - i = 0 - for site in self.sites: - if site.site_content_url == site_content_url: - self._current_site_index = i - i += 1 - - def __next__(self): - if self._current_site_index < len(self.sites): - self._current_site_index += 1 - else: - raise StopIteration() \ No newline at end of file diff --git a/tableau_rest_api/methods/alert.py b/tableau_rest_api/methods/alert.py index a55041f..0c67810 100644 --- a/tableau_rest_api/methods/alert.py +++ b/tableau_rest_api/methods/alert.py @@ -8,19 +8,19 @@ def __init__(self, rest_api_base: TableauRestApiBase32): def __getattr__(self, attr): return getattr(self.rest_api_base, attr) - def query_data_driven_alerts(self) -> etree.Element: + def query_data_driven_alerts(self) -> ET.Element: self.start_log_block() alerts = self.query_resource("dataAlerts") self.end_log_block() return alerts - def query_data_driven_alerts_for_view(self, view_luid: str) -> etree.Element: + def query_data_driven_alerts_for_view(self, view_luid: str) -> ET.Element: self.start_log_block() alerts = self.query_resource("dataAlerts?filter=viewId:eq:{}".format(view_luid)) self.end_log_block() return alerts - def query_data_driven_alert_details(self, data_alert_luid: str) -> etree.Element: + def query_data_driven_alert_details(self, data_alert_luid: str) -> ET.Element: self.start_log_block() alert_details = self.query_resource("dataAlerts/{}".format(data_alert_luid)) self.end_log_block() @@ -36,8 +36,8 @@ def add_user_to_data_driven_alert(self, data_alert_luid: str, username_or_luid: self.start_log_block() user_luid = self.query_user_luid(username_or_luid) - tsr = etree.Element("tsRequest") - u = etree.Element("user") + tsr = ET.Element("tsRequest") + u = ET.Element("user") u.set("id", user_luid) tsr.append(u) url = self.build_api_url('dataAlerts/{}/users'.format(data_alert_luid)) @@ -46,10 +46,10 @@ def add_user_to_data_driven_alert(self, data_alert_luid: str, username_or_luid: def update_data_driven_alert(self, data_alert_luid: str, subject: Optional[str] = None, frequency: Optional[str] = None, - owner_username_or_luid: Optional[str] = None) -> etree.Element: + owner_username_or_luid: Optional[str] = None) -> ET.Element: self.start_log_block() - tsr = etree.Element("tsRequest") - d = etree.Element("dataAlert") + tsr = ET.Element("tsRequest") + d = ET.Element("dataAlert") if subject is not None: d.set("subject", subject) @@ -62,7 +62,7 @@ def update_data_driven_alert(self, data_alert_luid: str, subject: Optional[str] if owner_username_or_luid is not None: owner_luid = self.query_user_luid(owner_username_or_luid) - o = etree.Element('owner') + o = ET.Element('owner') o.set("id", owner_luid) d.append(o) diff --git a/tableau_rest_api/methods/datasource.py b/tableau_rest_api/methods/datasource.py index 09fa3bb..d1ee22d 100644 --- a/tableau_rest_api/methods/datasource.py +++ b/tableau_rest_api/methods/datasource.py @@ -9,7 +9,7 @@ def __getattr__(self, attr): def query_datasources(self, project_name_or_luid: Optional[str] = None, all_fields: Optional[bool] = True, updated_at_filter: Optional[UrlFilter] = None, created_at_filter: Optional[UrlFilter] = None, tags_filter: Optional[UrlFilter] = None, datasource_type_filter: Optional[UrlFilter] = None, - sorts: Optional[List[Sort]] = None, fields: Optional[List[str]] = None) -> etree.Element: + sorts: Optional[List[Sort]] = None, fields: Optional[List[str]] = None) -> ET.Element: self.start_log_block() if fields is None: @@ -26,7 +26,7 @@ def query_datasources(self, project_name_or_luid: Optional[str] = None, all_fiel if project_name_or_luid is not None: project_luid = self.query_project_luid(project_name_or_luid) dses_in_project = datasources.findall('.//t:project[@id="{}"]/..'.format(project_luid), self.ns_map) - dses = etree.Element(self.ns_prefix + 'datasources') + dses = ET.Element(self.ns_prefix + 'datasources') for ds in dses_in_project: dses.append(ds) else: @@ -56,7 +56,7 @@ def query_datasources_json(self, all_fields: Optional[bool] = True, updated_at_f return datasources # Tries to guess name or LUID, hope there is only one - def query_datasource(self, ds_name_or_luid: str, proj_name_or_luid: Optional[str] = None) -> etree.Element: + def query_datasource(self, ds_name_or_luid: str, proj_name_or_luid: Optional[str] = None) -> ET.Element: self.start_log_block() ds_luid = self.query_datasource_luid(ds_name_or_luid, proj_name_or_luid) @@ -85,20 +85,20 @@ def delete_datasources(self, datasource_name_or_luid_s: Union[List[str], str]): def update_datasource(self, datasource_name_or_luid: str, datasource_project_name_or_luid: Optional[str] = None, new_datasource_name: Optional[str] = None, new_project_luid: Optional[str] = None, - new_owner_luid: Optional[str] = None) -> etree.Element: + new_owner_luid: Optional[str] = None) -> ET.Element: self.start_log_block() datasource_luid = self.query_datasource_luid(datasource_name_or_luid, datasource_project_name_or_luid) - tsr = etree.Element("tsRequest") - d = etree.Element("datasource") + tsr = ET.Element("tsRequest") + d = ET.Element("datasource") if new_datasource_name is not None: d.set('name', new_datasource_name) if new_project_luid is not None: - p = etree.Element('project') + p = ET.Element('project') p.set('id', new_project_luid) d.append(p) if new_owner_luid is not None: - o = etree.Element('owner') + o = ET.Element('owner') o.set('id', new_owner_luid) d.append(o) @@ -112,7 +112,7 @@ def update_datasource(self, datasource_name_or_luid: str, datasource_project_nam def update_datasource_connection_by_luid(self, datasource_luid: str, new_server_address: Optional[str] = None, new_server_port: Optional[str] = None, new_connection_username: Optional[str] = None, - new_connection_password: Optional[str] = None) -> etree.Element: + new_connection_password: Optional[str] = None) -> ET.Element: self.start_log_block() tsr = self.__build_connection_update_xml(new_server_address, new_server_port, new_connection_username, @@ -170,17 +170,17 @@ def download_datasource(self, ds_name_or_luid: str, filename_no_extension: str, # Tags can be scalar string or list def add_tags_to_datasource(self, ds_name_or_luid: str, tag_s: Union[List[str], str], - proj_name_or_luid: Optional[str] = None) -> etree.Element: + proj_name_or_luid: Optional[str] = None) -> ET.Element: self.start_log_block() ds_luid = self.query_workbook_luid(ds_name_or_luid, proj_name_or_luid) url = self.build_api_url("datasources/{}/tags".format(ds_luid)) - tsr = etree.Element("tsRequest") - ts = etree.Element("tags") + tsr = ET.Element("tsRequest") + ts = ET.Element("tags") tags = self.to_list(tag_s) for tag in tags: - t = etree.Element("tag") + t = ET.Element("tag") t.set("label", tag) ts.append(t) tsr.append(ts) @@ -209,15 +209,15 @@ def __init__(self, rest_api_base: TableauRestApiBase27): def update_datasource(self, datasource_name_or_luid: str, datasource_project_name_or_luid: Optional[str] = None, new_datasource_name: Optional[str] = None, new_project_luid: Optional[str] = None, new_owner_luid: Optional[str] = None, certification_status: Optional[str] = None, - certification_note: Optional[str] = None) -> etree.Element: + certification_note: Optional[str] = None) -> ET.Element: self.start_log_block() if certification_status not in [None, False, True]: raise InvalidOptionException('certification_status must be None, False, or True') datasource_luid = self.query_datasource_luid(datasource_name_or_luid, datasource_project_name_or_luid) - tsr = etree.Element("tsRequest") - d = etree.Element("datasource") + tsr = ET.Element("tsRequest") + d = ET.Element("datasource") if new_datasource_name is not None: d.set('name', new_datasource_name) if certification_status is not None: @@ -225,11 +225,11 @@ def update_datasource(self, datasource_name_or_luid: str, datasource_project_nam if certification_note is not None: d.set('certificationNote', certification_note) if new_project_luid is not None: - p = etree.Element('project') + p = ET.Element('project') p.set('id', new_project_luid) d.append(p) if new_owner_luid is not None: - o = etree.Element('owner') + o = ET.Element('owner') o.set('id', new_owner_luid) d.append(o) diff --git a/tableau_rest_api/methods/extract.py b/tableau_rest_api/methods/extract.py index 4587319..f9579f4 100644 --- a/tableau_rest_api/methods/extract.py +++ b/tableau_rest_api/methods/extract.py @@ -6,13 +6,13 @@ def __init__(self, rest_api_base: TableauRestApiBase): def __getattr__(self, attr): return getattr(self.rest_api_base, attr) - def get_extract_refresh_tasks(self) -> etree.Element: + def get_extract_refresh_tasks(self) -> ET.Element: self.start_log_block() extract_tasks = self.query_resource('tasks/extractRefreshes') self.end_log_block() return extract_tasks - def get_extract_refresh_task(self, task_luid: str) -> etree.Element: + def get_extract_refresh_task(self, task_luid: str) -> ET.Element: self.start_log_block() extract_task = self.query_resource('tasks/extractRefreshes/{}'.format(task_luid)) self.start_log_block() @@ -32,7 +32,7 @@ def get_extract_refresh_tasks_on_schedule(self, schedule_name_or_luid: str): def run_extract_refresh_task(self, task_luid:str) -> str: self.start_log_block() - tsr = etree.Element('tsRequest') + tsr = ET.Element('tsRequest') url = self.build_api_url('tasks/extractRefreshes/{}/runNow'.format(task_luid)) response = self.send_add_request(url, tsr) self.end_log_block() @@ -69,7 +69,7 @@ def run_extract_refresh_for_datasource(self, ds_name_or_luid: str, proj_name_or_ self.end_log_block() # Checks status of AD sync process or extract - def query_job(self, job_luid: str) -> etree.Element: + def query_job(self, job_luid: str) -> ET.Element: self.start_log_block() job = self.query_resource("jobs/{}".format(job_luid)) self.end_log_block() @@ -84,13 +84,13 @@ def __init__(self, rest_api_base: TableauRestApiBase28): self.rest_api_base = rest_api_base def update_datasource_now(self, ds_name_or_luid: str, - project_name_or_luid: Optional[str] = None) -> etree.Element: + project_name_or_luid: Optional[str] = None) -> ET.Element: self.start_log_block() ds_luid = self.query_datasource_luid(ds_name_or_luid, project_name_or_luid=project_name_or_luid) # Has an empty request but is POST because it makes a - tsr = etree.Element('tsRequest') + tsr = ET.Element('tsRequest') url = self.build_api_url('datasources/{}/refresh'.format(ds_luid)) response = self.send_add_request(url, tsr) @@ -98,12 +98,12 @@ def update_datasource_now(self, ds_name_or_luid: str, self.end_log_block() return response - def update_workbook_now(self, wb_name_or_luid: str, project_name_or_luid: Optional[str] = None) -> etree.Element: + def update_workbook_now(self, wb_name_or_luid: str, project_name_or_luid: Optional[str] = None) -> ET.Element: self.start_log_block() wb_luid = self.query_workbook_luid(wb_name_or_luid, proj_name_or_luid=project_name_or_luid) # Has an empty request but is POST because it makes a - tsr = etree.Element('tsRequest') + tsr = ET.Element('tsRequest') url = self.build_api_url('workbooks/{}/refresh'.format(wb_luid)) response = self.send_add_request(url, tsr) @@ -112,12 +112,12 @@ def update_workbook_now(self, wb_name_or_luid: str, project_name_or_luid: Option return response def run_extract_refresh_for_workbook(self, wb_name_or_luid: str, - proj_name_or_luid: Optional[str] = None) -> etree.Element: + proj_name_or_luid: Optional[str] = None) -> ET.Element: return self.update_workbook_now(wb_name_or_luid, proj_name_or_luid) # Use the specific refresh rather than the schedule task in 2.8 def run_extract_refresh_for_datasource(self, ds_name_or_luid: str, - proj_name_or_luid: Optional[str] = None) -> etree.Element: + proj_name_or_luid: Optional[str] = None) -> ET.Element: return self.update_datasource_now(ds_name_or_luid, proj_name_or_luid) @@ -133,7 +133,7 @@ def query_jobs(self, progress_filter: Optional[UrlFilter] = None, job_type_filte created_at_filter: Optional[UrlFilter] = None, started_at_filter: Optional[UrlFilter] = None, ended_at_filter: Optional[UrlFilter] = None, title_filter: Optional[UrlFilter] = None, subtitle_filter: Optional[UrlFilter] = None, - notes_filter: Optional[UrlFilter] = None) -> etree.Element: + notes_filter: Optional[UrlFilter] = None) -> ET.Element: self.start_log_block() filter_checks = {'progress': progress_filter, 'jobType': job_type_filter, 'createdAt': created_at_filter, 'title': title_filter, diff --git a/tableau_rest_api/methods/flow.py b/tableau_rest_api/methods/flow.py index daa03b1..c1d0df2 100644 --- a/tableau_rest_api/methods/flow.py +++ b/tableau_rest_api/methods/flow.py @@ -28,7 +28,7 @@ def query_flows_for_a_site(self, project_name_or_luid: Optional[str] = None, all created_at_filter: Optional[UrlFilter] = None, flow_name_filter: Optional[UrlFilter] = None, owner_name_filter: Optional[UrlFilter] = None, sorts: Optional[List[Sort]] = None, - fields: Optional[List[str]] = None) -> etree.Element: + fields: Optional[List[str]] = None) -> ET.Element: self.start_log_block() if fields is None: if all_fields is True: @@ -53,7 +53,7 @@ def query_flows_for_a_site(self, project_name_or_luid: Optional[str] = None, all self.end_log_block() return flows - def query_flows_for_a_user(self, username_or_luid: str, is_owner_flag: Optional[bool] = False) -> etree.Element: + def query_flows_for_a_user(self, username_or_luid: str, is_owner_flag: Optional[bool] = False) -> ET.Element: self.start_log_block() user_luid = self.query_user_luid(username_or_luid) additional_url_params = "" @@ -64,7 +64,7 @@ def query_flows_for_a_user(self, username_or_luid: str, is_owner_flag: Optional[ self.end_log_block() return flows - def query_flow(self, flow_name_or_luid: str, project_name_or_luid: Optional[str] = None) -> etree.Element: + def query_flow(self, flow_name_or_luid: str, project_name_or_luid: Optional[str] = None) -> ET.Element: self.start_log_block() flow_luid = self.query_flow_luid(flow_name_or_luid, project_name_or_luid=project_name_or_luid) @@ -74,7 +74,7 @@ def query_flow(self, flow_name_or_luid: str, project_name_or_luid: Optional[str] return flow def query_flow_connections(self, flow_name_or_luid: str, - project_name_or_luid: Optional[str] = None) -> etree.Element: + project_name_or_luid: Optional[str] = None) -> ET.Element: self.start_log_block() flow_luid = self.query_flow_luid(flow_name_or_luid, project_name_or_luid=project_name_or_luid) connections = self.query_resource('flows/{}/connections'.format(flow_luid)) @@ -82,13 +82,13 @@ def query_flow_connections(self, flow_name_or_luid: str, return connections - def get_flow_run_tasks(self) -> etree.Element: + def get_flow_run_tasks(self) -> ET.Element: self.start_log_block() tasks = self.query_resource('tasks/runFlow') self.end_log_block() return tasks - def get_flow_run_task(self, task_luid: str) -> etree.Element: + def get_flow_run_task(self, task_luid: str) -> ET.Element: self.start_log_block() task = self.query_resource('tasks/runFlow/{}'.format(task_luid)) self.end_log_block() @@ -107,13 +107,13 @@ def run_flow_now(self, flow_name_or_luid: str, flow_output_step_ids: Optional[Li if flow_output_step_ids is not None: pass - tsr = etree.Element('tsRequest') + tsr = ET.Element('tsRequest') url = self.build_api_url("flows/{}/run{}".format(flow_luid, additional_url_params)) job_luid = self.send_add_request(url, tsr) self.end_log_block() return job_luid - def run_flow_task(self, task_luid: str) -> etree.Element: + def run_flow_task(self, task_luid: str) -> ET.Element: self.start_log_block() url = self.build_api_url('tasks/runFlow/{}/runNow'.format(task_luid)) response = self.send_post_request(url) @@ -122,7 +122,7 @@ def run_flow_task(self, task_luid: str) -> etree.Element: def update_flow(self, flow_name_or_luid: str, project_name_or_luid: Optional[str] = None, - owner_username_or_luid: Optional[str] = None) -> etree.Element: + owner_username_or_luid: Optional[str] = None) -> ET.Element: self.start_log_block() if project_name_or_luid is None and owner_username_or_luid is None: raise InvalidOptionException('Must include at least one change, either project or owner or both') @@ -130,18 +130,18 @@ def update_flow(self, flow_name_or_luid: str, project_name_or_luid: Optional[str if self.is_luid(flow_name_or_luid): flow_luid = self.query_flow_luid(flow_name_or_luid) - tsr = etree.Element('tsRequest') - f = etree.Element('flow') + tsr = ET.Element('tsRequest') + f = ET.Element('flow') if project_name_or_luid is not None: proj_luid = self.query_project_luid(project_name_or_luid) - p = etree.Element('project') + p = ET.Element('project') p.set('id', proj_luid) f.append(p) if owner_username_or_luid is not None: owner_luid = self.query_user_luid(owner_username_or_luid) - o = etree.Element('owner') + o = ET.Element('owner') o.set('id', owner_luid) f.append(o) @@ -156,11 +156,11 @@ def update_flow(self, flow_name_or_luid: str, project_name_or_luid: Optional[str def update_flow_connection(self, flow_luid: str, flow_connection_luid: str, server_address: Optional[str] = None, port: Optional[str] = None, connection_username: Optional[str] = None, connection_password: Optional[str] = None, - embed_password: Optional[bool] = False) -> etree.Element: + embed_password: Optional[bool] = False) -> ET.Element: self.start_log_block() - tsr = etree.Element('tsRequest') - c = etree.Element('connection') + tsr = ET.Element('tsRequest') + c = ET.Element('connection') updates_count = 0 if server_address is not None: c.set('serverAddress', server_address) @@ -200,10 +200,10 @@ def add_flow_task_to_schedule(self, flow_name_or_luid: str, schedule_name_or_lui flow_luid = self.query_flow_luid(flow_name_or_luid) sched_luid = self.query_schedule_luid(schedule_name_or_luid) - tsr = etree.Element('tsRequest') - t = etree.Element('task') - fr = etree.Element('flowRun') - f = etree.Element('flow') + tsr = ET.Element('tsRequest') + t = ET.Element('task') + fr = ET.Element('flowRun') + f = ET.Element('flow') f.set('id', flow_luid) fr.append(f) t.append(fr) diff --git a/tableau_rest_api/methods/group.py b/tableau_rest_api/methods/group.py index 948fce2..59a28d8 100644 --- a/tableau_rest_api/methods/group.py +++ b/tableau_rest_api/methods/group.py @@ -7,7 +7,7 @@ def __init__(self, rest_api_base: TableauRestApiBase): def __getattr__(self, attr): return getattr(self.rest_api_base, attr) - def query_groups(self) -> etree.Element: + def query_groups(self) -> ET.Element: self.start_log_block() groups = self.query_resource("groups") for group in groups: @@ -31,7 +31,7 @@ def query_groups_json(self, page_number: Optional[int]=None) -> str: self.end_log_block() return groups - def query_group(self, group_name_or_luid: str) -> etree.Element: + def query_group(self, group_name_or_luid: str) -> ET.Element: self.start_log_block() group = self.query_single_element_from_endpoint('group', group_name_or_luid) # Add to group_name : luid cache @@ -71,14 +71,14 @@ def query_group_name(self, group_luid: str) -> str: return group_name # Returns the LUID of an existing group if one already exists - def create_group(self, group_name: Optional[str] = None, direct_xml_request: Optional[etree.Element] = None) -> str: + def create_group(self, group_name: Optional[str] = None, direct_xml_request: Optional[ET.Element] = None) -> str: self.start_log_block() if direct_xml_request is not None: tsr = direct_xml_request else: - tsr = etree.Element("tsRequest") - g = etree.Element("group") + tsr = ET.Element("tsRequest") + g = ET.Element("group") g.set("name", group_name) tsr.append(g) @@ -103,10 +103,10 @@ def create_group_from_ad_group(self, ad_group_name: str, ad_domain_name: str, if default_site_role not in self._site_roles: raise InvalidOptionException('"{}" is not an acceptable site role'.format(default_site_role)) - tsr = etree.Element("tsRequest") - g = etree.Element("group") + tsr = ET.Element("tsRequest") + g = ET.Element("group") g.set("name", ad_group_name) - i = etree.Element("import") + i = ET.Element("import") i.set("source", "ActiveDirectory") i.set("domainName", ad_domain_name) i.set("siteRole", default_site_role) @@ -127,7 +127,7 @@ def create_group_from_ad_group(self, ad_group_name: str, ad_domain_name: str, return group[0].get('id') # Take a single user_luid string or a collection of luid_strings - def add_users_to_group(self, username_or_luid_s: List[str], group_name_or_luid: str) -> etree.Element: + def add_users_to_group(self, username_or_luid_s: List[str], group_name_or_luid: str) -> ET.Element: self.start_log_block() group_luid = self.query_group_luid(group_name_or_luid) @@ -135,8 +135,8 @@ def add_users_to_group(self, username_or_luid_s: List[str], group_name_or_luid: for user in users: user_luid = self.query_user_luid(user) - tsr = etree.Element("tsRequest") - u = etree.Element("user") + tsr = ET.Element("tsRequest") + u = ET.Element("user") u.set("id", user_luid) tsr.append(u) @@ -150,12 +150,12 @@ def add_users_to_group(self, username_or_luid_s: List[str], group_name_or_luid: self.end_log_block() # Local Authentication update group - def update_group(self, name_or_luid: str, new_group_name: str) -> etree.Element: + def update_group(self, name_or_luid: str, new_group_name: str) -> ET.Element: self.start_log_block() group_luid = self.query_group_luid(name_or_luid) - tsr = etree.Element("tsRequest") - g = etree.Element("group") + tsr = ET.Element("tsRequest") + g = ET.Element("group") g.set("name", new_group_name) tsr.append(g) @@ -176,10 +176,10 @@ def sync_ad_group(self, group_name_or_luid: str, ad_group_name: str, ad_domain: raise InvalidOptionException("'{}' is not a valid site role in Tableau".format(default_site_role)) # Check that the group exists self.query_group(group_name_or_luid) - tsr = etree.Element('tsRequest') - g = etree.Element('group') + tsr = ET.Element('tsRequest') + g = ET.Element('group') g.set('name', ad_group_name) - i = etree.Element('import') + i = ET.Element('import') i.set('source', 'ActiveDirectory') i.set('domainName', ad_domain) i.set('siteRole', default_site_role) @@ -244,7 +244,7 @@ def query_groups(self, name_filter: Optional[UrlFilter] = None, domain_name_filt domain_nickname_filter: Optional[UrlFilter] = None, is_local_filter: Optional[UrlFilter] = None, user_count_filter: Optional[UrlFilter] = None, minimum_site_role_filter: Optional[UrlFilter] = None, - sorts: Optional[List[Sort]] = None) -> etree.Element: + sorts: Optional[List[Sort]] = None) -> ET.Element: filter_checks = {'name': name_filter, 'domainName': domain_name_filter, 'domainNickname': domain_nickname_filter, 'isLocal': is_local_filter, @@ -281,7 +281,7 @@ def query_groups_json(self, name_filter: Optional[UrlFilter] = None, domain_name # # No basic verb for querying a single group, so run a query_groups - def query_group(self, group_name_or_luid: str) -> etree.Element: + def query_group(self, group_name_or_luid: str) -> ET.Element: self.start_log_block() group = self.query_single_element_from_endpoint_with_filter('group', group_name_or_luid) # Add to group_name : luid cache diff --git a/tableau_rest_api/methods/project.py b/tableau_rest_api/methods/project.py index 3f4a100..dc5b983 100644 --- a/tableau_rest_api/methods/project.py +++ b/tableau_rest_api/methods/project.py @@ -6,7 +6,7 @@ def __init__(self, rest_api_base: TableauRestApiBase): def __getattr__(self, attr): return getattr(self.rest_api_base, attr) - def query_projects(self) -> etree.Element: + def query_projects(self) -> ET.Element: self.start_log_block() projects = self.query_resource("projects") self.end_log_block() @@ -21,13 +21,13 @@ def query_projects_json(self, page_number: Optional[int] = None) -> str: def create_project(self, project_name: Optional[str] = None, project_desc: Optional[str] = None, locked_permissions: bool = True, publish_samples: bool = False, no_return: Optional[bool] = False, - direct_xml_request: Optional[etree.Element] = None) -> Project: + direct_xml_request: Optional[ET.Element] = None) -> Project: self.start_log_block() if direct_xml_request is not None: tsr = direct_xml_request else: - tsr = etree.Element("tsRequest") - p = etree.Element("project") + tsr = ET.Element("tsRequest") + p = ET.Element("project") p.set("name", project_name) if project_desc is not None: @@ -55,7 +55,7 @@ def create_project(self, project_name: Optional[str] = None, project_desc: Optio - def query_project_xml_object(self, project_name_or_luid: str) -> etree.Element: + def query_project_xml_object(self, project_name_or_luid: str) -> ET.Element: self.start_log_block() luid = self.query_project_luid(project_name_or_luid) proj_xml = self.query_single_element_from_endpoint('project', luid) @@ -68,8 +68,8 @@ def update_project(self, name_or_luid: str, new_project_name: Optional[str] = No self.start_log_block() project_luid = self.query_project_luid(name_or_luid) - tsr = etree.Element("tsRequest") - p = etree.Element("project") + tsr = ET.Element("tsRequest") + p = ET.Element("project") if new_project_name is not None: p.set('name', new_project_name) if new_project_description is not None: @@ -106,7 +106,7 @@ def __init__(self, rest_api_base: TableauRestApiBase27): def query_projects(self, name_filter: Optional[UrlFilter] = None, owner_name_filter: Optional[UrlFilter] = None, updated_at_filter: Optional[UrlFilter] = None, created_at_filter: Optional[UrlFilter] = None, owner_domain_filter: Optional[UrlFilter] = None, owner_email_filter: Optional[UrlFilter] = None, - sorts: Optional[List[Sort]] = None) -> etree.Element: + sorts: Optional[List[Sort]] = None) -> ET.Element: filter_checks = {'name': name_filter, 'ownerName': owner_name_filter, 'updatedAt': updated_at_filter, 'createdAt': created_at_filter, 'ownerDomain': owner_domain_filter, 'ownerEmail': owner_email_filter} @@ -145,7 +145,7 @@ def query_project(self, project_name_or_luid: str) -> Project: self.end_log_block() return proj - def query_project_xml_object(self, project_name_or_luid: str) -> etree.Element: + def query_project_xml_object(self, project_name_or_luid: str) -> ET.Element: self.start_log_block() luid = self.query_project_luid(project_name_or_luid) proj_xml = self.query_single_element_from_endpoint_with_filter('project', luid) @@ -157,7 +157,7 @@ def __init__(self, rest_api_base: TableauRestApiBase28): self.rest_api_base = rest_api_base def get_published_project_object(self, project_name_or_luid: str, - project_xml_obj: Optional[etree.Element] = None) -> Project28: + project_xml_obj: Optional[ET.Element] = None) -> Project28: luid = self.query_project_luid(project_name_or_luid) @@ -172,14 +172,14 @@ def get_published_project_object(self, project_name_or_luid: str, def create_project(self, project_name: Optional[str] = None, parent_project_name_or_luid: Optional[str] = None, project_desc: Optional[str] = None, locked_permissions: Optional[bool] = True, publish_samples: Optional[bool] = False, no_return: Optional[bool] = False, - direct_xml_request: Optional[etree.Element] = None) -> Project28: + direct_xml_request: Optional[ET.Element] = None) -> Project28: self.start_log_block() if direct_xml_request is not None: tsr = direct_xml_request else: - tsr = etree.Element("tsRequest") - p = etree.Element("project") + tsr = ET.Element("tsRequest") + p = ET.Element("project") p.set("name", project_name) if project_desc is not None: @@ -217,8 +217,8 @@ def update_project(self, name_or_luid: str, parent_project_name_or_luid: Optiona self.start_log_block() project_luid = self.query_project_luid(name_or_luid) - tsr = etree.Element("tsRequest") - p = etree.Element("project") + tsr = ET.Element("tsRequest") + p = ET.Element("project") if new_project_name is not None: p.set('name', new_project_name) if new_project_description is not None: diff --git a/tableau_rest_api/methods/publishing.py b/tableau_rest_api/methods/publishing.py index 6c07274..8d54438 100644 --- a/tableau_rest_api/methods/publishing.py +++ b/tableau_rest_api/methods/publishing.py @@ -115,9 +115,9 @@ def publish_content(self, content_type, content_filename, content_name, project_ publish_request += bytes('Content-Type: text/xml\r\n\r\n'.encode('utf-8')) # Build publish request in ElementTree then convert at publish - publish_request_xml = etree.Element('tsRequest') + publish_request_xml = ET.Element('tsRequest') # could be either workbook, datasource, or flow - t1 = etree.Element(content_type) + t1 = ET.Element(content_type) t1.set('name', content_name) if show_tabs is not False: t1.set('showTabs', str(show_tabs).lower()) @@ -129,7 +129,7 @@ def publish_content(self, content_type, content_filename, content_name, project_ t1.set('generateThumbnailsAsUser', thumbnail_user_luid) if connection_username is not None: - cc = etree.Element('connectionCredentials') + cc = ET.Element('connectionCredentials') cc.set('name', connection_username) if oauth_flag is True: cc.set('oAuth', "True") @@ -141,9 +141,9 @@ def publish_content(self, content_type, content_filename, content_name, project_ # Views to Hide in Workbooks from 3.2 if views_to_hide_list is not None: if len(views_to_hide_list) > 0: - vs = etree.Element('views') + vs = ET.Element('views') for view_name in views_to_hide_list: - v = etree.Element('view') + v = ET.Element('view') v.set('name', view_name) v.set('hidden', 'true') t1.append(vs) @@ -151,12 +151,12 @@ def publish_content(self, content_type, content_filename, content_name, project_ # Description only allowed for Flows as of 3.3 if description is not None: t1.set('description', description) - p = etree.Element('project') + p = ET.Element('project') p.set('id', project_luid) t1.append(p) publish_request_xml.append(t1) - encoded_request = etree.tostring(publish_request_xml, encoding='utf-8') + encoded_request = ET.tostring(publish_request_xml, encoding='utf-8') publish_request += bytes(encoded_request) publish_request += bytes("\r\n--{}".format(boundary_string).encode('utf-8')) diff --git a/tableau_rest_api/methods/rest_api_base.py b/tableau_rest_api/methods/rest_api_base.py index 51d615c..2a225e4 100644 --- a/tableau_rest_api/methods/rest_api_base.py +++ b/tableau_rest_api/methods/rest_api_base.py @@ -23,7 +23,7 @@ def __init__(self, server: str, username: str, password: str, site_content_url: if server.find('http') == -1: raise InvalidOptionException('Server URL must include http:// or https://') - etree.register_namespace('t', self.ns_map['t']) + ET.register_namespace('t', self.ns_map['t']) self.server: str = server self.site_content_url: str = site_content_url self.username: str = username @@ -143,8 +143,8 @@ def build_site_request_xml(site_name: Optional[str] = None, content_url: Optiona storage_quota: Optional[str] = None, disable_subscriptions: Optional[bool] = None, state: Optional[str] = None, revision_history_enabled: Optional[bool] = None, revision_limit: Optional[str] = None): - tsr = etree.Element("tsRequest") - s = etree.Element('site') + tsr = ET.Element("tsRequest") + s = ET.Element('site') if site_name is not None: s.set('name', site_name) @@ -169,8 +169,8 @@ def build_site_request_xml(site_name: Optional[str] = None, content_url: Optiona return tsr # This is specifically for replication from one site to another - def build_request_from_response(self, request: etree.Element) -> etree.Element: - tsr = etree.Element('tsRequest') + def build_request_from_response(self, request: ET.Element) -> ET.Element: + tsr = ET.Element('tsRequest') request_copy = copy.deepcopy(request) # If the object happens to include the tsResponse root tag, strip it out if request_copy.tag.find("tsResponse") != -1: @@ -190,9 +190,9 @@ def build_request_from_response(self, request: etree.Element) -> etree.Element: def __build_connection_update_xml(new_server_address: Optional[str] = None, new_server_port: Optional[str] = None, new_connection_username: Optional[str] = None, - new_connection_password: Optional[str] = None) -> etree.Element: - tsr = etree.Element('tsRequest') - c = etree.Element("connection") + new_connection_password: Optional[str] = None) -> ET.Element: + tsr = ET.Element('tsRequest') + c = ET.Element("connection") if new_server_address is not None: c.set('serverAddress', new_server_address) if new_server_port is not None: @@ -208,7 +208,7 @@ def __build_connection_update_xml(new_server_address: Optional[str] = None, # Factory methods for PublishedContent and Permissions objects # def get_published_project_object(self, project_name_or_luid: str, - project_xml_obj: Optional[etree.Element] = None) -> Project: + project_xml_obj: Optional[ET.Element] = None) -> Project: if self.is_luid(project_name_or_luid): luid = project_name_or_luid else: @@ -240,18 +240,18 @@ def get_published_datasource_object(self, datasource_name_or_luid: str, def signin(self, user_luid_to_impersonate: Optional[str] = None): self.start_log_block() - tsr = etree.Element("tsRequest") - c = etree.Element("credentials") + tsr = ET.Element("tsRequest") + c = ET.Element("credentials") c.set("name", self.username) c.set("password", self._password) - s = etree.Element("site") + s = ET.Element("site") if self.site_content_url.lower() not in ['default', '']: s.set("contentUrl", self.site_content_url) c.append(s) if user_luid_to_impersonate is not None: - u = etree.Element('user') + u = ET.Element('user') u.set('id', user_luid_to_impersonate) c.append(u) @@ -267,7 +267,7 @@ def signin(self, user_luid_to_impersonate: Optional[str] = None): verify_ssl_cert=self.verify_ssl_cert) self._request_obj.xml_request = tsr self._request_obj.http_verb = 'post' - self.log('Login payload is\n {}'.format(etree.tostring(tsr))) + self.log('Login payload is\n {}'.format(ET.tostring(tsr))) self._request_obj.request_from_api(0) # self.log(api.get_raw_response()) @@ -321,7 +321,7 @@ def signout(self, session_token: Optional[str] = None): # baseline method for any get request. appends to base url def query_resource(self, url_ending: str, server_level:bool = False, filters: Optional[List[UrlFilter]] = None, sorts: Optional[List[Sort]] = None, additional_url_ending: Optional[str] = None, - fields: Optional[List[str]] = None) -> etree.Element: + fields: Optional[List[str]] = None) -> ET.Element: self.start_log_block() if self.token == "": raise NotSignedInException('Must use .signin() to create REST API session first') @@ -370,7 +370,7 @@ def query_resource(self, url_ending: str, server_level:bool = False, filters: Op return xml def query_elements_from_endpoint_with_filter(self, element_name: str, name_or_luid: Optional[str] = None, - all_fields: bool = True) -> etree.Element: + all_fields: bool = True) -> ET.Element: self.start_log_block() # A few elements have singular endpoints @@ -397,7 +397,7 @@ def query_elements_from_endpoint_with_filter(self, element_name: str, name_or_lu def query_single_element_from_endpoint_with_filter(self, element_name: str, name_or_luid: Optional[str] = None, - all_fields: bool = True) -> etree.Element: + all_fields: bool = True) -> ET.Element: self.start_log_block() elements = self.query_elements_from_endpoint_with_filter(element_name, name_or_luid, all_fields=all_fields) @@ -521,7 +521,7 @@ def query_resource_json(self, url_ending: str, server_level: bool = False, return json_response def query_single_element_from_endpoint(self, element_name: str, name_or_luid: str, - server_level: bool = False) -> etree.Element: + server_level: bool = False) -> ET.Element: self.start_log_block() # A few elements have singular endpoints @@ -544,7 +544,7 @@ def query_single_element_from_endpoint(self, element_name: str, name_or_luid: st self.end_log_block() raise NoMatchFoundException("No {} found with name or luid {}".format(element_name, name_or_luid)) - def send_post_request(self, url: str) -> etree.Element: + def send_post_request(self, url: str) -> ET.Element: self.start_log_block() if self.token == "": raise NotSignedInException('Must use .signin() to create REST API session first') @@ -557,7 +557,7 @@ def send_post_request(self, url: str) -> etree.Element: self.end_log_block() return xml - def send_add_request(self, url: str, request: etree.Element) -> etree.Element: + def send_add_request(self, url: str, request: ET.Element) -> ET.Element: self.start_log_block() if self.token == "": @@ -574,7 +574,7 @@ def send_add_request(self, url: str, request: etree.Element) -> etree.Element: self.end_log_block() return xml - def send_update_request(self, url: str, request: etree.Element) -> etree.Element: + def send_update_request(self, url: str, request: ET.Element) -> ET.Element: self.start_log_block() if self.token == "": raise NotSignedInException('Must use .signin() to create REST API session first') @@ -611,8 +611,8 @@ def send_delete_request(self, url: str) -> int: except: raise - def send_publish_request(self, url: str, xml_request: etree.Element, content, - boundary_string: str) -> etree.Element: + def send_publish_request(self, url: str, xml_request: ET.Element, content, + boundary_string: str) -> ET.Element: self.start_log_block() if self.token == "": raise NotSignedInException('Must use .signin() to create REST API session first') @@ -630,7 +630,7 @@ def send_publish_request(self, url: str, xml_request: etree.Element, content, self.end_log_block() return xml - def send_append_request(self, url: str, request, boundary_string: str) -> etree.Element: + def send_append_request(self, url: str, request, boundary_string: str) -> ET.Element: self.start_log_block() if self.token == "": raise NotSignedInException('Must use .signin() to create REST API session first') @@ -699,7 +699,7 @@ def _query_data_file(self, download_type: str, view_name_or_luid: str, high_reso e.tableau_error_code)) self.end_log_block() raise - def query_server_info(self) -> etree.Element: + def query_server_info(self) -> ET.Element: self.start_log_block() server_info = self.query_resource("serverinfo", server_level=True) self.end_log_block() @@ -782,9 +782,9 @@ def build_site_request_xml(site_name: Optional[str] = None, content_url: Optiona admin_mode: Optional[str] = None, tier_creator_capacity: Optional[str] = None, tier_explorer_capacity: Optional[str] = None, tier_viewer_capacity: Optional[str] = None, storage_quota: Optional[str] = None, disable_subscriptions: Optional[bool] = None, - state: Optional[str] = None) -> etree.Element: - tsr = etree.Element("tsRequest") - s = etree.Element('site') + state: Optional[str] = None) -> ET.Element: + tsr = ET.Element("tsRequest") + s = ET.Element('site') if site_name is not None: s.set('name', site_name) @@ -870,7 +870,7 @@ def __init__(self, server: str, username: Optional[str] = None, password: Option if server.find('http') == -1: raise InvalidOptionException('Server URL must include http:// or https://') - etree.register_namespace('t', self.ns_map['t']) + ET.register_namespace('t', self.ns_map['t']) self.server: str = server self.site_content_url: str = site_content_url self.username: str = username @@ -904,8 +904,8 @@ def __init__(self, server: str, username: Optional[str] = None, password: Option def signin(self, user_luid_to_impersonate: Optional[str] = None): self.start_log_block() - tsr = etree.Element("tsRequest") - c = etree.Element("credentials") + tsr = ET.Element("tsRequest") + c = ET.Element("credentials") if self._pat_name is not None: if self._pat_secret is not None: c.set('personalAccessTokenName', self._pat_name) @@ -919,7 +919,7 @@ def signin(self, user_luid_to_impersonate: Optional[str] = None): c.set("password", self._password) else: raise InvalidOptionException('Must include both username and password to login without PAT') - s = etree.Element("site") + s = ET.Element("site") if self.site_content_url.lower() not in ['default', '']: s.set("contentUrl", self.site_content_url) @@ -930,7 +930,7 @@ def signin(self, user_luid_to_impersonate: Optional[str] = None): if self._pat_name is not None: raise InvalidOptionException('Impersonation is not available when using PAT login') else: - u = etree.Element('user') + u = ET.Element('user') u.set('id', user_luid_to_impersonate) c.append(u) @@ -946,7 +946,7 @@ def signin(self, user_luid_to_impersonate: Optional[str] = None): verify_ssl_cert=self.verify_ssl_cert) self._request_obj.xml_request = tsr self._request_obj.http_verb = 'post' - self.log('Login payload is\n {}'.format(etree.tostring(tsr))) + self.log('Login payload is\n {}'.format(ET.tostring(tsr))) self._request_obj.request_from_api(0) # self.log(api.get_raw_response()) diff --git a/tableau_rest_api/methods/revision.py b/tableau_rest_api/methods/revision.py index 0f4efb6..9884941 100644 --- a/tableau_rest_api/methods/revision.py +++ b/tableau_rest_api/methods/revision.py @@ -8,7 +8,7 @@ def __getattr__(self, attr): return getattr(self.rest_api_base, attr) def get_workbook_revisions(self, workbook_name_or_luid: str, username_or_luid: Optional[str] = None, - project_name_or_luid: Optional[str] = None) -> etree.Element: + project_name_or_luid: Optional[str] = None) -> ET.Element: self.start_log_block() wb_luid = self.query_workbook_luid(workbook_name_or_luid, project_name_or_luid, username_or_luid) wb_revisions = self.query_resource('workbooks/{}/revisions'.format(wb_luid)) @@ -16,7 +16,7 @@ def get_workbook_revisions(self, workbook_name_or_luid: str, username_or_luid: O return wb_revisions def get_datasource_revisions(self, datasource_name_or_luid: str, - project_name_or_luid: Optional[str] = None) -> etree.Element: + project_name_or_luid: Optional[str] = None) -> ET.Element: self.start_log_block() ds_luid = self.query_datasource_luid(datasource_name_or_luid, project_name_or_luid) wb_revisions = self.query_resource('workbooks/{}/revisions'.format(ds_luid)) diff --git a/tableau_rest_api/methods/site.py b/tableau_rest_api/methods/site.py index a8079cb..ed4cab3 100644 --- a/tableau_rest_api/methods/site.py +++ b/tableau_rest_api/methods/site.py @@ -10,7 +10,7 @@ def __getattr__(self, attr): # # Site queries don't have the site portion of the URL, so login option gets correct format - def query_sites(self) -> etree.Element: + def query_sites(self) -> ET.Element: self.start_log_block() sites = self.query_resource("sites", server_level=True) self.end_log_block() @@ -35,7 +35,7 @@ def query_all_site_content_urls(self) -> List[str]: return site_content_urls # You can only query a site you have logged into this way. Better to use methods that run through query_sites - def query_current_site(self) -> etree.Element: + def query_current_site(self) -> ET.Element: self.start_log_block() site = self.query_resource("sites/{}".format(self.site_luid), server_level=True) self.end_log_block() @@ -46,7 +46,7 @@ def create_site(self, new_site_name: str, new_content_url: str, admin_mode: Opti user_quota: Optional[str] = None, storage_quota: Optional[str] = None, disable_subscriptions: Optional[bool] = None, revision_history_enabled: Optional[bool] = None, revision_limit: Optional[str] = None, - direct_xml_request: Optional[etree.Element] = None) -> str: + direct_xml_request: Optional[ET.Element] = None) -> str: if direct_xml_request is not None: add_request = direct_xml_request @@ -72,7 +72,7 @@ def update_site(self, site_name: Optional[str] = None, content_url: Optional[str admin_mode: Optional[str] = None, user_quota: Optional[str] = None, storage_quota: Optional[str] = None, disable_subscriptions: Optional[bool] = None, state: Optional[str] = None, revision_history_enabled: Optional[bool] = None, - revision_limit: Optional[str] = None) -> etree.Element: + revision_limit: Optional[str] = None) -> ET.Element: self.start_log_block() tsr = self.build_site_request_xml(site_name, content_url, admin_mode, user_quota, storage_quota, disable_subscriptions, state, revision_limit=revision_limit) @@ -106,7 +106,7 @@ def create_site(self, new_site_name: str, new_content_url: str, admin_mode: Opti storage_quota: Optional[str] = None, disable_subscriptions: Optional[bool] = None, revision_history_enabled: Optional[bool] = None, revision_limit: Optional[str] = None, - direct_xml_request: Optional[etree.Element] = None) -> str: + direct_xml_request: Optional[ET.Element] = None) -> str: add_request = self.build_site_request_xml(new_site_name, new_content_url, admin_mode, tier_creator_capacity, tier_explorer_capacity, tier_viewer_capacity, storage_quota, disable_subscriptions, @@ -131,7 +131,7 @@ def update_site(self, site_name: Optional[str] = None, content_url: Optional[str storage_quota: Optional[str] = None, disable_subscriptions: Optional[bool] = None, revision_history_enabled: Optional[bool] = None, revision_limit: Optional[str] = None, state: Optional[str] = None, - direct_xml_request: Optional[etree.Element] = None) -> etree.Element: + direct_xml_request: Optional[ET.Element] = None) -> ET.Element: self.start_log_block() tsr = self.build_site_request_xml(site_name, content_url, admin_mode, tier_creator_capacity, tier_explorer_capacity, tier_viewer_capacity, storage_quota, diff --git a/tableau_rest_api/methods/subscription.py b/tableau_rest_api/methods/subscription.py index 21372b3..08ba8b5 100644 --- a/tableau_rest_api/methods/subscription.py +++ b/tableau_rest_api/methods/subscription.py @@ -11,7 +11,7 @@ def query_subscriptions(self, username_or_luid: Optional[str] = None, schedule_n subscription_subject: Optional[str] = None, view_or_workbook: Optional[str] = None, content_name_or_luid: Optional[str] = None, project_name_or_luid: Optional[str] = None, - wb_name_or_luid: Optional[str] = None) -> etree.Element: + wb_name_or_luid: Optional[str] = None) -> ET.Element: self.start_log_block() subscriptions = self.query_resource('subscriptions') @@ -63,7 +63,7 @@ def create_subscription(self, subscription_subject: Optional[str] = None, view_o content_name_or_luid: Optional[str] = None, schedule_name_or_luid: Optional[str] = None, username_or_luid: Optional[str] = None, project_name_or_luid: Optional[str] = None, wb_name_or_luid: Optional[str] = None, - direct_xml_request: Optional[etree.Element] = None) -> str: + direct_xml_request: Optional[ET.Element] = None) -> str: self.start_log_block() if direct_xml_request is not None: tsr = direct_xml_request @@ -87,15 +87,15 @@ def create_subscription(self, subscription_subject: Optional[str] = None, view_o else: raise InvalidOptionException("view_or_workbook must be 'Workbook' or 'View'") - tsr = etree.Element('tsRequest') - s = etree.Element('subscription') + tsr = ET.Element('tsRequest') + s = ET.Element('subscription') s.set('subject', subscription_subject) - c = etree.Element('content') + c = ET.Element('content') c.set('type', view_or_workbook) c.set('id', content_luid) - sch = etree.Element('schedule') + sch = ET.Element('schedule') sch.set('id', schedule_luid) - u = etree.Element('user') + u = ET.Element('user') u.set('id', user_luid) s.append(c) s.append(sch) @@ -131,17 +131,17 @@ def create_subscription_to_view(self, subscription_subject: str, view_name_or_lu return luid def update_subscription(self, subscription_luid: str, subject: Optional[str] = None, - schedule_luid: Optional[str] = None) -> etree.Element: + schedule_luid: Optional[str] = None) -> ET.Element: if subject is None and schedule_luid is None: raise InvalidOptionException("You must pass one of subject or schedule_luid, or both") - tsr = etree.Element('tsRequest') - s = etree.Element('subscription') + tsr = ET.Element('tsRequest') + s = ET.Element('subscription') if subject is not None: s.set('subject', subject) if schedule_luid is not None: - sch = etree.Element('schedule') + sch = ET.Element('schedule') sch.set('id', schedule_luid) s.append(sch) tsr.append(s) diff --git a/tableau_rest_api/methods/user.py b/tableau_rest_api/methods/user.py index 24a80e1..897c541 100644 --- a/tableau_rest_api/methods/user.py +++ b/tableau_rest_api/methods/user.py @@ -11,13 +11,13 @@ def __getattr__(self, attr): # The reference has this name, so for consistency adding an alias def get_users(self, all_fields: bool = True, last_login_filter: Optional[UrlFilter] = None, site_role_filter: Optional[UrlFilter] = None, sorts: Optional[List[Sort]] = None, - fields: Optional[List[str] ] =None) -> etree.Element: + fields: Optional[List[str] ] =None) -> ET.Element: return self.query_users(all_fields=all_fields, last_login_filter=last_login_filter, site_role_filter=site_role_filter, sorts=sorts, fields=fields) def query_users(self, all_fields: bool = True, last_login_filter: Optional[UrlFilter] = None, site_role_filter: Optional[UrlFilter] = None, username_filter: Optional[UrlFilter] = None, - sorts: Optional[List[Sort]] = None, fields: Optional[List[str] ] =None) -> etree.Element: + sorts: Optional[List[Sort]] = None, fields: Optional[List[str] ] =None) -> ET.Element: self.start_log_block() if fields is None: if all_fields is True: @@ -59,7 +59,7 @@ def query_users_json(self, all_fields: bool = True, last_login_filter: Optional[ self.end_log_block() return users - def query_user(self, username_or_luid: str, all_fields: bool = True) -> etree.Element: + def query_user(self, username_or_luid: str, all_fields: bool = True) -> ET.Element: self.start_log_block() user = self.query_single_element_from_endpoint_with_filter("user", username_or_luid, all_fields=all_fields) user_luid = user.get("id") @@ -80,7 +80,7 @@ def query_username(self, user_luid: str) -> str: self.end_log_block() return username - def query_users_in_group(self, group_name_or_luid: str) -> etree.Element: + def query_users_in_group(self, group_name_or_luid: str) -> ET.Element: self.start_log_block() luid = self.query_group_luid(group_name_or_luid) users = self.query_resource("groups/{}/users".format(luid)) @@ -89,7 +89,7 @@ def query_users_in_group(self, group_name_or_luid: str) -> etree.Element: def add_user_by_username(self, username: Optional[str] = None, site_role: Optional[str] = 'Unlicensed', auth_setting: Optional[str] = None, update_if_exists: Optional[bool] = False, - direct_xml_request: Optional[etree.Element] = None) -> str: + direct_xml_request: Optional[ET.Element] = None) -> str: self.start_log_block() # Check to make sure role that is passed is a valid role in the API @@ -103,8 +103,8 @@ def add_user_by_username(self, username: Optional[str] = None, site_role: Option if direct_xml_request is not None: tsr = direct_xml_request else: - tsr = etree.Element("tsRequest") - u = etree.Element("user") + tsr = ET.Element("tsRequest") + u = ET.Element("user") u.set("name", username) u.set("siteRole", site_role) if auth_setting is not None: @@ -138,7 +138,7 @@ def add_user_by_username(self, username: Optional[str] = None, site_role: Option def add_user(self, username: Optional[str] = None, fullname: Optional[str] = None, site_role: Optional[str] = 'Unlicensed', password: Optional[str] = None, email: Optional[str] = None, auth_setting: Optional[str] = None, - update_if_exists: Optional[bool] = False, direct_xml_request: Optional[etree.Element] = None) -> str: + update_if_exists: Optional[bool] = False, direct_xml_request: Optional[ET.Element] = None) -> str: self.start_log_block() @@ -146,8 +146,8 @@ def add_user(self, username: Optional[str] = None, fullname: Optional[str] = Non # Add username first, then update with full name if direct_xml_request is not None: # Parse to second level, should be - new_user_tsr = etree.Element('tsRequest') - new_user_u = etree.Element('user') + new_user_tsr = ET.Element('tsRequest') + new_user_u = ET.Element('user') for t in direct_xml_request: if t.tag != 'user': raise InvalidOptionException('Must submit a tsRequest with a user element') @@ -157,8 +157,8 @@ def add_user(self, username: Optional[str] = None, fullname: Optional[str] = Non new_user_tsr.append(new_user_u) new_user_luid = self.add_user_by_username(direct_xml_request=new_user_tsr) - update_tsr = etree.Element('tsRequest') - update_u = etree.Element('user') + update_tsr = ET.Element('tsRequest') + update_u = ET.Element('user') for t in direct_xml_request: for a in t.attrib: if a in ['fullName', 'email', 'password', 'siteRole', 'authSetting']: @@ -179,7 +179,7 @@ def add_user(self, username: Optional[str] = None, fullname: Optional[str] = Non def update_user(self, username_or_luid: str, full_name: Optional[str] = None, site_role: Optional[str] =None, password: Optional[str] = None, - email: Optional[str] = None, direct_xml_request: Optional[etree.Element] = None) -> etree.Element: + email: Optional[str] = None, direct_xml_request: Optional[ET.Element] = None) -> ET.Element: self.start_log_block() user_luid = self.query_user_luid(username_or_luid) @@ -187,8 +187,8 @@ def update_user(self, username_or_luid: str, full_name: Optional[str] = None, si if direct_xml_request is not None: tsr = direct_xml_request else: - tsr = etree.Element("tsRequest") - u = etree.Element("user") + tsr = ET.Element("tsRequest") + u = ET.Element("user") if full_name is not None: u.set('fullName', full_name) if site_role is not None: diff --git a/tableau_rest_api/methods/workbook.py b/tableau_rest_api/methods/workbook.py index 1820e3e..0235ed4 100644 --- a/tableau_rest_api/methods/workbook.py +++ b/tableau_rest_api/methods/workbook.py @@ -11,7 +11,7 @@ def query_workbooks(self, username_or_luid: Optional[str] = None, project_name_o all_fields: Optional[bool] = True, created_at_filter: Optional[UrlFilter] = None, updated_at_filter: Optional[UrlFilter] = None, owner_name_filter: Optional[UrlFilter] = None, tags_filter: Optional[UrlFilter] = None, sorts: Optional[List[Sort]] = None, - fields: Optional[List[str]] = None) -> etree.Element: + fields: Optional[List[str]] = None) -> ET.Element: self.start_log_block() if fields is None: if all_fields is True: @@ -31,13 +31,13 @@ def query_workbooks(self, username_or_luid: Optional[str] = None, project_name_o if project_name_or_luid is not None: project_luid = self.query_project_luid(project_name_or_luid) wbs_in_project = wbs.findall('.//t:project[@id="{}"]/..'.format(project_luid), self.ns_map) - wbs = etree.Element(self.ns_prefix + 'workbooks') + wbs = ET.Element(self.ns_prefix + 'workbooks') for wb in wbs_in_project: wbs.append(wb) self.end_log_block() return wbs - def query_workbooks_for_user(self, username_or_luid: str) -> etree.Element: + def query_workbooks_for_user(self, username_or_luid: str) -> ET.Element: self.start_log_block() wbs = self.query_workbooks(username_or_luid) self.end_log_block() @@ -72,7 +72,7 @@ def query_workbooks_json(self, username_or_luid: Optional[str] = None, all_field # Because a workbook can have the same pretty name in two projects, requires more logic def query_workbook(self, wb_name_or_luid: str, proj_name_or_luid: Optional[str] = None, - username_or_luid: Optional[str] = None) -> etree.Element: + username_or_luid: Optional[str] = None) -> ET.Element: self.start_log_block() workbooks = self.query_workbooks(username_or_luid) if self.is_luid(wb_name_or_luid): @@ -112,7 +112,7 @@ def query_workbooks_in_project(self, project_name_or_luid: str, username_or_luid workbooks = self.query_workbooks(user_luid) # This brings back the workbook itself wbs_in_project = workbooks.findall('.//t:project[@id="{}"]/..'.format(project_luid), self.ns_map) - wbs = etree.Element(self.ns_prefix + 'workbooks') + wbs = ET.Element(self.ns_prefix + 'workbooks') for wb in wbs_in_project: wbs.append(wb) self.end_log_block() @@ -120,21 +120,21 @@ def query_workbooks_in_project(self, project_name_or_luid: str, username_or_luid def update_workbook(self, workbook_name_or_luid: str, workbook_project_name_or_luid: str, new_project_name_or_luid: Optional[str] = None, new_owner_username_or_luid: Optional[str] = None, - show_tabs: Optional[bool] = True) -> etree.Element: + show_tabs: Optional[bool] = True) -> ET.Element: self.start_log_block() workbook_luid = self.query_workbook_luid(workbook_name_or_luid, workbook_project_name_or_luid, self.username) new_owner_luid = self.query_user_luid(new_owner_username_or_luid) new_project_luid = self.query_project_luid(new_project_name_or_luid) - tsr = etree.Element("tsRequest") - w = etree.Element("workbook") + tsr = ET.Element("tsRequest") + w = ET.Element("workbook") w.set('showTabs', str(show_tabs).lower()) if new_project_luid is not None: - p = etree.Element('project') + p = ET.Element('project') p.set('id', new_project_luid) w.append(p) if new_owner_luid is not None: - o = etree.Element('owner') + o = ET.Element('owner') o.set('id', new_owner_luid) w.append(o) tsr.append(w) @@ -150,7 +150,7 @@ def update_workbook_connection_by_luid(self, wb_luid: str, connection_luid: str, new_server_address: Optional[str] = None, new_server_port: Optional[str] = None, new_connection_username: Optional[str] = None, - new_connection_password: Optional[str] = None) -> etree.Element: + new_connection_password: Optional[str] = None) -> ET.Element: self.start_log_block() tsr = self.__build_connection_update_xml(new_server_address, new_server_port, new_connection_username, new_connection_password) @@ -196,7 +196,7 @@ def save_view_image(self, wb_name_or_luid: Optional[str] = None, view_name_or_lu def query_workbook_views(self, wb_name_or_luid: str, proj_name_or_luid: Optional[str] = None, - username_or_luid: Optional[str] = None, usage: Optional[bool] = False) -> etree.Element: + username_or_luid: Optional[str] = None, usage: Optional[bool] = False) -> ET.Element: self.start_log_block() if usage not in [True, False]: raise InvalidOptionException('Usage can only be set to True or False') @@ -220,7 +220,7 @@ def query_workbook_views_json(self, wb_name_or_luid: str, proj_name_or_luid: Opt def query_workbook_view(self, wb_name_or_luid, view_name_or_luid: Optional[str] = None, view_content_url: Optional[str] = None, proj_name_or_luid: Optional[str] = None, - username_or_luid: Optional[str] = None, usage: Optional[bool] = False) -> etree.Element: + username_or_luid: Optional[str] = None, usage: Optional[bool] = False) -> ET.Element: self.start_log_block() if usage not in [True, False]: @@ -247,7 +247,7 @@ def query_workbook_view(self, wb_name_or_luid, view_name_or_luid: Optional[str] # This should be the key to updating the connections in a workbook. Seems to return # LUIDs for connections and the datatypes, but no way to distinguish them def query_workbook_connections(self, wb_name_or_luid: str, proj_name_or_luid: Optional[str] = None, - username_or_luid: Optional[str] = None) -> etree.Element: + username_or_luid: Optional[str] = None) -> ET.Element: self.start_log_block() wb_luid = self.query_workbook_luid(wb_name_or_luid, proj_name_or_luid, username_or_luid) conns = self.query_resource("workbooks/{}/connections".format(wb_luid)) @@ -257,7 +257,7 @@ def query_workbook_connections(self, wb_name_or_luid: str, proj_name_or_luid: Op def query_views(self, all_fields: Optional[bool] = True, usage: Optional[bool] = False, created_at_filter: Optional[UrlFilter] = None, updated_at_filter: Optional[UrlFilter] = None, tags_filter: Optional[UrlFilter] = None, sorts: Optional[UrlFilter] = None, - fields: Optional[UrlFilter] = None) -> etree.Element: + fields: Optional[UrlFilter] = None) -> ET.Element: self.start_log_block() if fields is None: @@ -295,7 +295,7 @@ def query_views_json(self, all_fields: Optional[bool] = True, usage: Optional[bo self.end_log_block() return vws - def query_view(self, vw_name_or_luid: str) -> etree.Element: + def query_view(self, vw_name_or_luid: str) -> ET.Element: self.start_log_block() vw = self.query_single_element_from_endpoint_with_filter('view', vw_name_or_luid) self.end_log_block() @@ -448,16 +448,16 @@ def save_workbook_preview_image(self, wb_name_or_luid: str, filename_no_extensio # Tags can be scalar string or list def add_tags_to_workbook(self, wb_name_or_luid: str, tag_s: List[str], - proj_name_or_luid: Optional[str] = None) -> etree.Element: + proj_name_or_luid: Optional[str] = None) -> ET.Element: self.start_log_block() wb_luid = self.query_workbook_luid(wb_name_or_luid, proj_name_or_luid) url = self.build_api_url("workbooks/{}/tags".format(wb_luid)) - tsr = etree.Element("tsRequest") - ts = etree.Element("tags") + tsr = ET.Element("tsRequest") + ts = ET.Element("tags") tags = self.to_list(tag_s) for tag in tags: - t = etree.Element("tag") + t = ET.Element("tag") t.set("label", tag) ts.append(t) tsr.append(ts) @@ -479,16 +479,16 @@ def delete_tags_from_workbook(self, wb_name_or_luid: str, tag_s: Union[List[str] # Tags can be scalar string or list def add_tags_to_view(self, view_name_or_luid: str, workbook_name_or_luid: str, tag_s: List[str], - proj_name_or_luid: Optional[str] = None) -> etree.Element: + proj_name_or_luid: Optional[str] = None) -> ET.Element: self.start_log_block() vw_luid = self.query_workbook_view_luid(workbook_name_or_luid, view_name_or_luid, proj_name_or_luid) url = self.build_api_url("views/{}/tags".format(vw_luid)) - tsr = etree.Element("tsRequest") - ts = etree.Element("tags") + tsr = ET.Element("tsRequest") + ts = ET.Element("tags") tags = self.to_list(tag_s) for tag in tags: - t = etree.Element("tag") + t = ET.Element("tag") t.set("label", tag) ts.append(t) tsr.append(ts) diff --git a/tableau_rest_api/published_content.py b/tableau_rest_api/published_content.py index 4201c13..9e74dd8 100644 --- a/tableau_rest_api/published_content.py +++ b/tableau_rest_api/published_content.py @@ -137,10 +137,10 @@ def convert_permissions_obj_list_from_orig_site_to_current_site(self, permission def convert_permissions_xml_object_from_orig_site_to_current_site(self, permissions_xml_request, orig_site, username_map=None): """ - :type permissions_xml_request: etree.Element + :type permissions_xml_request: ET.Element :type orig_site: TableauRestApiConnection :type username_map: dict[unicode, unicode] - :rtype: etree.Element + :rtype: ET.Element """ # If the site is the same, skip the whole thing and just return the original if self.t_rest_api.site_content_url == orig_site.site_content_url \ @@ -213,7 +213,7 @@ def replicate_permissions(self, orig_content): self.end_log_block() @staticmethod - def _fix_permissions_request_for_replication(tsr: etree.Element) -> etree.Element: + def _fix_permissions_request_for_replication(tsr: ET.Element) -> ET.Element: # Remove the project tag from the original response proj_element = None for t in tsr.iter(): @@ -302,12 +302,12 @@ def build_capabilities_xml_from_dict(self, capabilities_dict, obj_type): """ :type capabilities_dict: dict :type obj_type: unicode - :return: etree.Element + :return: ET.Element """ if obj_type not in self.permissionable_objects: error_text = 'objtype can only be "project", "workbook" or "datasource", was given {}' raise InvalidOptionException(error_text.format('obj_type')) - c = etree.Element('capabilities') + c = ET.Element('capabilities') for cap in capabilities_dict: # Skip if the capability is set to None @@ -328,7 +328,7 @@ def build_capabilities_xml_from_dict(self, capabilities_dict, obj_type): if cap not in self.available_capabilities[self.api_version]["workbook"]: self.log('{} is not a valid capability for a workbook'.format(cap)) continue - capab = etree.Element('capability') + capab = ET.Element('capability') capab.set('name', cap) capab.set('mode', capabilities_dict[cap]) c.append(capab) @@ -337,15 +337,15 @@ def build_capabilities_xml_from_dict(self, capabilities_dict, obj_type): def build_add_permissions_request(self, permissions_obj): """ :param permissions_obj: Permissions - :return: etree.Element + :return: ET.Element """ - tsr = etree.Element('tsRequest') - p = etree.Element('permissions') + tsr = ET.Element('tsRequest') + p = ET.Element('permissions') capabilities_dict = permissions_obj.get_capabilities_dict() c = self.build_capabilities_xml_from_dict(capabilities_dict, self.obj_type) - gcap = etree.Element('granteeCapabilities') - t = etree.Element(permissions_obj.group_or_user) + gcap = ET.Element('granteeCapabilities') + t = ET.Element(permissions_obj.group_or_user) t.set('id', permissions_obj.luid) gcap.append(t) gcap.append(c) @@ -360,7 +360,7 @@ def convert_capabilities_xml_into_obj_list(self, xml_obj): def get_permissions_from_server(self, obj_perms_xml=None): """ - :type obj_perms_xml: etree.Element + :type obj_perms_xml: ET.Element :return: """ self.start_log_block() @@ -389,7 +389,7 @@ def get_permissions_obj_list(self): # This one doesn't do any of the checking or determining if there is a need to change. Only for pure replication def set_permissions_by_permissions_direct_xml(self, direct_xml_request): """ - :type direct_xml_request: etree.Element + :type direct_xml_request: ET.Element :return: """ self.start_log_block() @@ -534,7 +534,7 @@ def luid(self, name_or_luid): def convert_capabilities_xml_into_obj_list(self, xml_obj): """ - :type xml_obj: etree.Element + :type xml_obj: ET.Element :rtype: list[ProjectPermissions21] """ self.start_log_block() @@ -849,7 +849,7 @@ def parent_project_luid(self): def query_child_projects(self): """ - :rtype: etree.Element + :rtype: ET.Element """ self.start_log_block() projects = self.t_rest_api.query_projects() @@ -857,7 +857,7 @@ def query_child_projects(self): self.end_log_block() return child_projects - def convert_capabilities_xml_into_obj_list(self, xml_obj: etree.Element) -> List[ProjectPermissions21]: + def convert_capabilities_xml_into_obj_list(self, xml_obj: ET.Element) -> List[ProjectPermissions21]: self.start_log_block() obj_list = [] xml = xml_obj.findall('.//t:granteeCapabilities', self.ns_map) @@ -931,7 +931,7 @@ def luid(self, name_or_luid: str): luid = self.t_rest_api.query_workbook_luid(name_or_luid) self._luid = luid - def convert_capabilities_xml_into_obj_list(self, xml_obj: etree.Element) -> List[WorkbookPermissions21]: + def convert_capabilities_xml_into_obj_list(self, xml_obj: ET.Element) -> List[WorkbookPermissions21]: self.start_log_block() obj_list = [] @@ -977,7 +977,7 @@ def luid(self, name_or_luid: str): ds_luid = self.t_rest_api.query_datasource_luid(name_or_luid) self._luid = ds_luid - def convert_capabilities_xml_into_obj_list(self, xml_obj: etree.Element) -> List[DatasourcePermissions21]: + def convert_capabilities_xml_into_obj_list(self, xml_obj: ET.Element) -> List[DatasourcePermissions21]: self.start_log_block() obj_list = [] xml = xml_obj.findall('.//t:granteeCapabilities', self.ns_map) @@ -1023,7 +1023,7 @@ def luid(self, luid: str): # Maybe implement a search at some point self._luid = luid - def convert_capabilities_xml_into_obj_list(self, xml_obj: etree.Element) -> List[WorkbookPermissions21]: + def convert_capabilities_xml_into_obj_list(self, xml_obj: ET.Element) -> List[WorkbookPermissions21]: self.start_log_block() obj_list = [] xml = xml_obj.findall('.//t:granteeCapabilities', self.ns_map) diff --git a/tableau_rest_api/rest_json_request.py b/tableau_rest_api/rest_json_request.py index 89f99fb..3ce6114 100644 --- a/tableau_rest_api/rest_json_request.py +++ b/tableau_rest_api/rest_json_request.py @@ -1,6 +1,6 @@ from ..tableau_base import * from ..tableau_exceptions import * -import xml.etree.cElementTree as etree +import xml.etree.ElementTree as ET # from HTMLParser import HTMLParser # from StringIO import StringIO from io import BytesIO @@ -39,7 +39,7 @@ def __init__(self, url=None, token=None, logger=None, ns_map_url='http://tableau self.__last_response_headers = None self.__json_object = None self.ns_map = {'t': ns_map_url} - etree.register_namespace('t', ns_map_url) + ET.register_namespace('t', ns_map_url) self.logger = logger self.log('RestJsonRequest intialized') self.__publish = None @@ -152,11 +152,11 @@ def __make_request(self, page_number=1): # Log the XML request being sent encoded_request = "" if self.xml_request is not None: - self.log("Request XML: {}".format(etree.tostring(self.xml_request, encoding='utf-8').decode('utf-8'))) + self.log("Request XML: {}".format(ET.tostring(self.xml_request, encoding='utf-8').decode('utf-8'))) if isinstance(self.xml_request, str): encoded_request = self.xml_request.encode('utf-8') else: - encoded_request = etree.tostring(self.xml_request, encoding='utf-8') + encoded_request = ET.tostring(self.xml_request, encoding='utf-8') if self.__publish_content is not None: encoded_request = self.__publish_content try: @@ -212,8 +212,8 @@ def _handle_http_error(self, response, e): self.log("Received a {} error, here was response:".format(str(status_code))) self.log(raw_error_response.decode('utf8')) - utf8_parser = etree.XMLParser(encoding='utf-8') - xml = etree.parse(BytesIO(raw_error_response), parser=utf8_parser) + utf8_parser = ET.XMLParser(encoding='utf-8') + xml = ET.parse(BytesIO(raw_error_response), parser=utf8_parser) try: tableau_error = xml.findall('.//t:error', namespaces=self.ns_map) error_code = tableau_error[0].get('code') diff --git a/tableau_rest_api/rest_xml_request.py b/tableau_rest_api/rest_xml_request.py index 449e8a6..0224f0f 100644 --- a/tableau_rest_api/rest_xml_request.py +++ b/tableau_rest_api/rest_xml_request.py @@ -1,6 +1,6 @@ from ..tableau_base import * from ..tableau_exceptions import * -import xml.etree.cElementTree as etree +import xml.etree as ET # from HTMLParser import HTMLParser # from StringIO import StringIO from io import BytesIO @@ -37,7 +37,7 @@ def __init__(self, url=None, token=None, logger=None, ns_map_url='http://tableau self.__last_response_headers = None self.__xml_object = None self.ns_map = {'t': ns_map_url} - etree.register_namespace('t', ns_map_url) + ET.register_namespace('t', ns_map_url) self.logger = logger self.log('RestXmlRequest intialized') self.__publish = None @@ -119,7 +119,7 @@ def get_last_response_content_type(self): def get_response(self): if self.__response_type == 'xml' and self.__xml_object is not None: - self.log_debug("XML Object Response: {}".format(etree.tostring(self.__xml_object, encoding='utf-8').decode('utf-8'))) + self.log_debug("XML Object Response: {}".format(ET.tostring(self.__xml_object, encoding='utf-8').decode('utf-8'))) return self.__xml_object else: return self.__raw_response @@ -147,11 +147,11 @@ def __make_request(self, page_number=1): # Log the XML request being sent encoded_request = "" if self.xml_request is not None: - self.log("Request XML: {}".format(etree.tostring(self.xml_request, encoding='utf-8').decode('utf-8'))) + self.log("Request XML: {}".format(ET.tostring(self.xml_request, encoding='utf-8').decode('utf-8'))) if isinstance(self.xml_request, str): encoded_request = self.xml_request.encode('utf-8') else: - encoded_request = etree.tostring(self.xml_request, encoding='utf-8') + encoded_request = ET.tostring(self.xml_request, encoding='utf-8') if self.__publish_content is not None: encoded_request = self.__publish_content try: @@ -207,8 +207,8 @@ def _handle_http_error(self, response, e): self.log("Received a {} error, here was response:".format(str(status_code))) self.log(raw_error_response.decode('utf8')) - utf8_parser = etree.XMLParser(encoding='utf-8') - xml = etree.parse(BytesIO(raw_error_response), parser=utf8_parser) + utf8_parser = ET.XMLParser(encoding='utf-8') + xml = ET.parse(BytesIO(raw_error_response), parser=utf8_parser) try: tableau_error = xml.findall('.//t:error', namespaces=self.ns_map) error_code = tableau_error[0].get('code') @@ -264,9 +264,9 @@ def request_from_api(self, page_number=1): if self.__response_type == 'xml': if self.__raw_response == '' or self.__raw_response is None or len(self.__raw_response) == 0: return True - utf8_parser = etree.XMLParser(encoding='UTF-8') + utf8_parser = ET.XMLParser(encoding='UTF-8') sio = BytesIO(self.__raw_response) - xml = etree.parse(sio, parser=utf8_parser) + xml = ET.parse(sio, parser=utf8_parser) # Set the XML object to the first returned. Will be replaced if there is pagination self.__xml_object = xml.getroot() @@ -287,8 +287,8 @@ def request_from_api(self, page_number=1): for i in range(2, total_pages + 1): self.__make_request(i) # Get next page - utf8_parser2 = etree.XMLParser(encoding='utf-8') - xml = etree.parse(BytesIO(self.__raw_response), parser=utf8_parser2) + utf8_parser2 = ET.XMLParser(encoding='utf-8') + xml = ET.parse(BytesIO(self.__raw_response), parser=utf8_parser2) for obj in xml.getroot(): if obj.tag != 'pagination': full_xml_obj = obj @@ -298,7 +298,7 @@ def request_from_api(self, page_number=1): self.__xml_object = combined_xml_obj self.log_debug("Logging the combined xml object") - self.log_debug(etree.tostring(self.__xml_object, encoding='utf-8').decode('utf-8')) + self.log_debug(ET.tostring(self.__xml_object, encoding='utf-8').decode('utf-8')) self.log("Request succeeded") return True elif self.__response_type in ['binary', 'png', 'csv']: