Skip to content

Commenting and Docstring cleanup. A few very small code cleanups #120

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 30 additions & 35 deletions tableaudocumentapi/connection.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,14 @@
###############################################################################
#
# Connection - A class for writing connections to Tableau files
#
###############################################################################
import xml.etree.ElementTree as ET
from tableaudocumentapi.dbclass import is_valid_dbclass


class Connection(object):
"""
A class for writing connections to Tableau files.

"""

###########################################################################
#
# Public API.
#
###########################################################################
"""A class representing connections inside Data Sources."""

def __init__(self, connxml):
"""
Constructor.
"""Connection is usually instantiated by passing in connection elements
in a Data Source. If creating a connection from scratch you can call
`from_attributes` passing in the connection attributes.

"""
self._connectionXML = connxml
Expand All @@ -37,6 +24,9 @@ def __repr__(self):

@classmethod
def from_attributes(cls, server, dbname, username, dbclass, port=None, authentication=''):
"""Creates a new connection that can be added into a Data Source.
defaults to `''` which will be treated as 'prompt' by Tableau."""

root = ET.Element('connection', authentication=authentication)
xml = cls(root)
xml.server = server
Expand All @@ -47,11 +37,9 @@ def from_attributes(cls, server, dbname, username, dbclass, port=None, authentic

return xml

###########
# dbname
###########
@property
def dbname(self):
"""Database name for the connection. Not the table name."""
return self._dbname

@dbname.setter
Expand All @@ -69,11 +57,9 @@ def dbname(self, value):
self._dbname = value
self._connectionXML.set('dbname', value)

###########
# server
###########
@property
def server(self):
"""Hostname or IP address of the database server. May also be a URL in some connection types."""
return self._server

@server.setter
Expand All @@ -91,11 +77,9 @@ def server(self, value):
self._server = value
self._connectionXML.set('server', value)

###########
# username
###########
@property
def username(self):
"""Username used to authenticate to the database."""
return self._username

@username.setter
Expand All @@ -113,38 +97,49 @@ def username(self, value):
self._username = value
self._connectionXML.set('username', value)

###########
# authentication
###########
@property
def authentication(self):
return self._authentication

###########
# dbclass
###########
@property
def dbclass(self):
"""The type of connection (e.g. 'MySQL', 'Postgresql'). A complete list
can be found in dbclass.py"""
return self._class

@dbclass.setter
def dbclass(self, value):
"""Set the connection's dbclass property.

Args:
value: New dbclass value. String.

Returns:
Nothing.
"""

if not is_valid_dbclass(value):
raise AttributeError("'{}' is not a valid database type".format(value))

self._class = value
self._connectionXML.set('class', value)

###########
# port
###########
@property
def port(self):
"""Port used to connect to the database."""
return self._port

@port.setter
def port(self, value):
"""Set the connection's port property.

Args:
value: New port value. String.

Returns:
Nothing.
"""

self._port = value
# If port is None we remove the element and don't write it to XML
if value is None:
Expand Down
47 changes: 17 additions & 30 deletions tableaudocumentapi/datasource.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
###############################################################################
#
# Datasource - A class for writing datasources to Tableau files
#
###############################################################################
import collections
import itertools
import xml.etree.ElementTree as ET
Expand All @@ -16,7 +11,7 @@

########
# This is needed in order to determine if something is a string or not. It is necessary because
# of differences between python2 (basestring) and python3 (str). If python2 support is every
# of differences between python2 (basestring) and python3 (str). If python2 support is ever
# dropped, remove this and change the basestring references below to str
try:
basestring
Expand All @@ -35,7 +30,7 @@ def _get_metadata_xml_for_field(root_xml, field_name):


def _is_used_by_worksheet(names, field):
return any((y for y in names if y in field.worksheets))
return any(y for y in names if y in field.worksheets)


class FieldDictionary(MultiLookupDict):
Expand Down Expand Up @@ -87,27 +82,32 @@ def base36encode(number):
return sign + base36


def make_unique_name(dbclass):
def _make_unique_name(dbclass):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't ever be called outside of the class so I made it private.

rand_part = base36encode(uuid4().int)
name = dbclass + '.' + rand_part
return name


class ConnectionParser(object):
"""Parser for detecting and extracting connections from differing Tableau file formats."""

def __init__(self, datasource_xml, version):
self._dsxml = datasource_xml
self._dsversion = version

def _extract_federated_connections(self):
connections = list(map(Connection, self._dsxml.findall('.//named-connections/named-connection/*')))
# 'sqlproxy' connections (Tableau Server Connections) are not embedded into named-connection elements
# extract them manually for now
connections.extend(map(Connection, self._dsxml.findall("./connection[@class='sqlproxy']")))
return connections

def _extract_legacy_connection(self):
return list(map(Connection, self._dsxml.findall('connection')))

def get_connections(self):
"""Find and return all connections based on file format version."""

if float(self._dsversion) < 10:
connections = self._extract_legacy_connection()
else:
Expand All @@ -116,16 +116,11 @@ def get_connections(self):


class Datasource(object):
"""
A class for writing datasources to Tableau files.
"""A class representing Tableau Data Sources, embedded in workbook files or
in TDS files.

"""

###########################################################################
#
# Public API.
#
###########################################################################
def __init__(self, dsxml, filename=None):
"""
Constructor. Default is to create datasource from xml.
Expand All @@ -145,21 +140,23 @@ def __init__(self, dsxml, filename=None):

@classmethod
def from_file(cls, filename):
"""Initialize datasource from file (.tds)"""
"""Initialize datasource from file (.tds ot .tdsx)"""

dsxml = xml_open(filename, cls.__name__.lower()).getroot()
dsxml = xml_open(filename, 'datasource').getroot()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realized that this was actually less readable than just making it a literal string, which removed the need for the comment "#Gets the name of the class and normalizes it so it can verify the file it opens"

return cls(dsxml, filename)

@classmethod
def from_connections(cls, caption, connections):
"""Create a new Data Source give a list of Connections."""

root = ET.Element('datasource', caption=caption, version='10.0', inline='true')
outer_connection = ET.SubElement(root, 'connection')
outer_connection.set('class', 'federated')
named_conns = ET.SubElement(outer_connection, 'named-connections')
for conn in connections:
nc = ET.SubElement(named_conns,
'named-connection',
name=make_unique_name(conn.dbclass),
name=_make_unique_name(conn.dbclass),
caption=conn.server)
nc.append(conn._connectionXML)
return cls(root)
Expand Down Expand Up @@ -194,16 +191,10 @@ def save_as(self, new_filename):

xfile._save_file(self._filename, self._datasourceTree, new_filename)

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

###########
# version
###########
@property
def version(self):
return self._version
Expand All @@ -222,9 +213,6 @@ def caption(self):
del self._datasourceXML.attrib['caption']
self._caption = ''

###########
# connections
###########
@property
def connections(self):
return self._connections
Expand All @@ -234,16 +222,15 @@ def clear_repository_location(self):
if tag is not None:
self._datasourceXML.remove(tag)

###########
# fields
###########
@property
def fields(self):
if not self._fields:
self._fields = self._get_all_fields()
return self._fields

def _get_all_fields(self):
# Some columns are represented by `column` tags and others as `metadata-record` tags
# Find them all and chain them into one dictionary
column_field_objects = self._get_column_objects()
existing_column_fields = [x.id for x in column_field_objects]
metadata_only_field_objects = (x for x in self._get_metadata_objects() if x.id not in existing_column_fields)
Expand Down
36 changes: 5 additions & 31 deletions tableaudocumentapi/workbook.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
###############################################################################
#
# Workbook - A class for writing Tableau workbook files
#
###############################################################################
import weakref


Expand All @@ -11,25 +6,18 @@


class Workbook(object):
"""
A class for writing Tableau workbook files.
"""A class for writing Tableau workbook files."""

"""

###########################################################################
#
# Public API.
#
###########################################################################
def __init__(self, filename):
"""
Constructor.
"""Open the workbook at `filename`. This will handle packaged and unpacked
workbook files automatically. This will also parse Data Sources and Worksheets
for access.

"""

self._filename = filename

self._workbookTree = xml_open(self._filename, self.__class__.__name__.lower())
self._workbookTree = xml_open(self._filename, 'workbook')

self._workbookRoot = self._workbookTree.getroot()
# prepare our datasource objects
Expand All @@ -42,23 +30,14 @@ def __init__(self, filename):
self._workbookRoot, self._datasource_index
)

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

###########
# worksheets
###########
@property
def worksheets(self):
return self._worksheets

###########
# filename
###########
@property
def filename(self):
return self._filename
Expand Down Expand Up @@ -92,11 +71,6 @@ def save_as(self, new_filename):
xfile._save_file(
self._filename, self._workbookTree, new_filename)

###########################################################################
#
# Private API.
#
###########################################################################
@staticmethod
def _prepare_datasource_index(datasources):
retval = weakref.WeakValueDictionary()
Expand Down
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy