Skip to content

Commit

Permalink
Merge pull request #108 from tableau/development
Browse files Browse the repository at this point in the history
Release 0.5
  • Loading branch information
Russell Hay authored Nov 1, 2016
2 parents a3b7322 + a426a42 commit 96d8967
Show file tree
Hide file tree
Showing 13 changed files with 141 additions and 62 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 05 (01 November 2016)

* Added ability to set the port for connections (#97)
* Added ability to read and write caption for datasources (#99)
* Added documentation

## 0.4 (07 October 2016)

* Add ability to remove repository location (#86)
Expand Down
33 changes: 0 additions & 33 deletions contributing.md

This file was deleted.

1 change: 1 addition & 0 deletions contributing.md
2 changes: 1 addition & 1 deletion docs/_config.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Site settings
title: Tableau Document API
email: jdominguez@tableau.com
email: github@tableau.com
description: Programmatically update your Tableau workbooks and data sources.
baseurl: "/document-api-python"
permalinks: pretty
Expand Down
2 changes: 1 addition & 1 deletion docs/_layouts/docs.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<div class="content .col-xs-12 .col-sm-8 .col-md-9">
<h1>{{ page.title }}</h1>
<div class="edit-container">
<a href="https://github.com/tableau/document-api-python/edit/master/{{ page.path }}" class="edit-links"><span class="glyphicon glyphicon-pencil"></span> Edit this page</a>
<a href="https://github.com/tableau/document-api-python/edit/master/docs/{{ page.path }}" class="edit-links"><span class="glyphicon glyphicon-pencil"></span> Edit this page</a>
&nbsp;
<a href="https://github.com/tableau/document-api-python/issues" class="edit-links"><span class="glyphicon glyphicon-flag"></span> Submit an issue</a>
</div>
Expand Down
1 change: 1 addition & 0 deletions docs/docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
title: Contributing
layout: docs
---
# Contributing

We welcome contributions to this project!

Expand Down
52 changes: 34 additions & 18 deletions docs/docs/dev-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,67 @@ title: Developer Guide
layout: docs
---

### Making your first patch
<<<<<<< 6ee666bf06d5ab59931100b3955779c35693e33f
## Submitting your first patch

1. Make sure you've signed the CLA
1. Make sure you have [signed the CLA](http://tableau.github.io/#contributor-license-agreement-cla)

1. Clone the repo
1. Fork the repository.

We follow the "Fork and Pull" model as described [here](https://help.github.com/articles/about-collaborative-development-models/).

1. Clone your fork:

```shell
git clone http://github.com/tableau/document-api-python
git clone http://github.com/<your_username>/document-api-python
```

1. Run the tests to make sure everything is peachy
1. Run the tests to make sure everything is peachy:

```shell
python setup.py test
```

1. Set up the feature, fix, or documentation branch.

It is recommended to use the format [issue#]-[type]-[description] (e.g. 13-fix-connection-bug)
It is recommended to use the format issue#-type-description (e.g. 13-fix-connection-bug) like so:

```shell
git checkout -b 13-feature-new-stuff
```

1. Code and Commit!
1. Code and commit!

Here's a quick checklist for ensuring a good pull request:

- Only touch the minimal amount of files possible while still accomplishing the goal.
- Ensure all indentation is done as 4-spaces and your editor is set to unix line endings.
- The code matches PEP8 style guides. If you cloned the repo you can run `pycodestyle .`
- Keep commit messages clean and descriptive.
If the PR is accepted it will get 'Squashed' into a single commit before merging, the commit messages will be used to generate the Merge commit message.

Here's a quick checklist for ensuring a good diff:
1. Add tests.

- The diff touches the minimal amount of files possible while still fufilling the purpose of the diff
- The diff uses Unix line endings
- The diff adheres to our PEP8 style guides. If you've cloned the repo you can run `pycodestyle .`
All of our tests live under the `test/` folder in the repository.
We use `unittest` and the built-in test runner `python setup.py test`.
If a test needs a static file, like a twb/twbx, it should live under `test/assets/`

1. Add Tests
1. Update the documentation.

1. Update Documentation
Our documentation is written in markdown and built with Jekyll on Github Pages. All of the documentation source files can be found in `docs/docs`.

Our documentation is written in markdown and built with [Mkdocs](http://www.mkdocs.org). More information on how to update and build the docs can be found [here](#updating-documentation)
When adding a new feature or improving existing functionality we may ask that you update the documentation along with your code.

If you are just making a PR for documentation updates (adding new docs, fixing typos, improving wording) the easiest method is to use the built in `Edit this file` in the Github UI

1. Run the tests again and make sure they pass!
1. Submit to your fork.

1. Submit to your fork
1. Make a PR as described [here](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) against the 'development' branch.

1. Submit a PR
1. Wait for a review and address any feedback.
While we try and stay on top of all issues and PRs it might take a few days for someone to respond. Politely pinging the PR after a few days with no response is OK, we'll try and respond with a timeline as soon as we are able.

1. Wait for a review, and address any feedback.
1. That's it! When the PR has received :rocket:'s from members of the core team they will merge the PR

<!--
### Updating Documentation
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name='tableaudocumentapi',
version='0.4',
version='0.5',
author='Tableau',
author_email='[email protected]',
url='https://github.com/tableau/document-api-python',
Expand Down
23 changes: 22 additions & 1 deletion tableaudocumentapi/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,20 @@ def __init__(self, connxml):
self._username = connxml.get('username')
self._authentication = connxml.get('authentication')
self._class = connxml.get('class')
self._port = connxml.get('port', None)

def __repr__(self):
return "'<Connection server='{}' dbname='{}' @ {}>'".format(self._server, self._dbname, hex(id(self)))

@classmethod
def from_attributes(cls, server, dbname, username, dbclass, authentication=''):
def from_attributes(cls, server, dbname, username, dbclass, port=None, authentication=''):
root = ET.Element('connection', authentication=authentication)
xml = cls(root)
xml.server = server
xml.dbname = dbname
xml.username = username
xml.dbclass = dbclass
xml.port = port

return xml

Expand Down Expand Up @@ -133,3 +135,22 @@ def dbclass(self, value):

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

###########
# port
###########
@property
def port(self):
return self._port

@port.setter
def port(self, value):
self._port = value
# If port is None we remove the element and don't write it to XML
if value is None:
try:
del self._connectionXML.attrib['port']
except KeyError:
pass
else:
self._connectionXML.set('port', value)
15 changes: 15 additions & 0 deletions tableaudocumentapi/datasource.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ def __init__(self, dsxml, filename=None):
self._name = self._datasourceXML.get('name') or self._datasourceXML.get(
'formatted-name') # TDS files don't have a name attribute
self._version = self._datasourceXML.get('version')
self._caption = self._datasourceXML.get('caption', '')
self._connection_parser = ConnectionParser(
self._datasourceXML, version=self._version)
self._connections = self._connection_parser.get_connections()
Expand Down Expand Up @@ -207,6 +208,20 @@ def name(self):
def version(self):
return self._version

@property
def caption(self):
return self._caption

@caption.setter
def caption(self, value):
self._datasourceXML.set('caption', value)
self._caption = value

@caption.deleter
def caption(self):
del self._datasourceXML.attrib['caption']
self._caption = ''

###########
# connections
###########
Expand Down
2 changes: 1 addition & 1 deletion test/assets/CONNECTION.xml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<connection authentication='sspi' class='sqlserver' dbname='TestV1' odbc-native-protocol='yes' one-time-sql='' server='mssql2012' username=''></connection>
<connection authentication='sspi' class='sqlserver' dbname='TestV1' odbc-native-protocol='yes' one-time-sql='' server='mssql2012' username='' port='1433'></connection>
3 changes: 2 additions & 1 deletion test/assets/datasource_test.tds
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version='1.0' encoding='utf-8' ?>
<datasource formatted-name='postgres.1of3kl00aoax5d1a1ejma1397430' inline='true' source-platform='mac' version='9.3' xmlns:user='http://www.tableausoftware.com/xml/user'>
<datasource caption='foo' formatted-name='postgres.1of3kl00aoax5d1a1ejma1397430' inline='true' source-platform='mac'
version='9.3' xmlns:user='http://www.tableausoftware.com/xml/user'>
<repository-location />
<connection authentication='username-password' class='postgres' dbname='TestV1' odbc-native-protocol='yes' port='5432' server='postgres91.test.tsi.lan' username='test'>
<relation name='xy' table='[public].[xy]' type='table' />
Expand Down
18 changes: 14 additions & 4 deletions test/bvt.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@

TABLEAU_10_TWB = os.path.join(TEST_DIR, 'assets', 'TABLEAU_10_TWB.twb')

TABLEAU_CONNECTION_XML = ET.parse(os.path.join(
TEST_DIR, 'assets', 'CONNECTION.xml')).getroot()
TABLEAU_CONNECTION_XML = os.path.join(TEST_DIR, 'assets', 'CONNECTION.xml')

TABLEAU_10_TWBX = os.path.join(TEST_DIR, 'assets', 'TABLEAU_10_TWBX.twbx')

Expand Down Expand Up @@ -51,7 +50,7 @@ def test_can_extract_federated_connections(self):
class ConnectionModelTests(unittest.TestCase):

def setUp(self):
self.connection = TABLEAU_CONNECTION_XML
self.connection = ET.parse(TABLEAU_CONNECTION_XML).getroot()

def test_can_read_attributes_from_connection(self):
conn = Connection(self.connection)
Expand All @@ -60,15 +59,24 @@ def test_can_read_attributes_from_connection(self):
self.assertEqual(conn.server, 'mssql2012')
self.assertEqual(conn.dbclass, 'sqlserver')
self.assertEqual(conn.authentication, 'sspi')
self.assertEqual(conn.port, '1433')

def test_can_write_attributes_to_connection(self):
conn = Connection(self.connection)
conn.dbname = 'BubblesInMyDrink'
conn.server = 'mssql2014'
conn.username = 'bob'
conn.port = '1337'
self.assertEqual(conn.dbname, 'BubblesInMyDrink')
self.assertEqual(conn.username, 'bob')
self.assertEqual(conn.server, 'mssql2014')
self.assertEqual(conn.port, '1337')

def test_can_delete_port_from_connection(self):
conn = Connection(self.connection)
conn.port = None
self.assertEqual(conn.port, None)
self.assertIsNone(conn._connectionXML.get('port'))

def test_bad_dbclass_rasies_attribute_error(self):
conn = Connection(self.connection)
Expand All @@ -90,11 +98,13 @@ def test_can_create_datasource_from_connections(self):
conn1 = Connection.from_attributes(
server='a', dbname='b', username='c', dbclass='mysql', authentication='d')
conn2 = Connection.from_attributes(
server='1', dbname='2', username='3', dbclass='mysql', authentication='7')
server='1', dbname='2', username='3', dbclass='mysql', port='1337', authentication='7')
ds = Datasource.from_connections('test', connections=[conn1, conn2])

self.assertEqual(ds.connections[0].server, 'a')
self.assertEqual(ds.connections[0].port, None)
self.assertEqual(ds.connections[1].server, '1')
self.assertEqual(ds.connections[1].port, '1337')


class ConnectionParserInComplicatedWorkbooks(unittest.TestCase):
Expand Down
43 changes: 42 additions & 1 deletion test/test_datasource.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import unittest
import os
import os.path
import shutil
import tempfile
import unittest


from tableaudocumentapi import Datasource, Workbook

Expand All @@ -22,6 +26,19 @@ class DataSourceFieldsTDS(unittest.TestCase):

def setUp(self):
self.ds = Datasource.from_file(TEST_TDS_FILE)
self.to_delete = set()

def cleanUp(self):
for path in self.to_delete:
if os.path.isdir(path):
shutil.rmtree(path, ignore_errors=True)
elif os.path.isfile(path):
os.unlink(path)

def get_temp_file(self, filename):
tempdir = tempfile.mkdtemp('tda-datasource')
self.to_delete.add(tempdir)
return os.path.join(tempdir, filename)

def test_datasource_returns_correct_fields(self):
self.assertIsNotNone(self.ds.fields)
Expand Down Expand Up @@ -63,6 +80,30 @@ def test_datasource_field_description(self):
self.assertIsNotNone(actual)
self.assertTrue(u'muted gray' in actual)

def test_datasource_caption(self):
actual = self.ds.caption
self.assertIsNotNone(actual)
self.assertEqual(actual, 'foo')

def test_datasource_can_set_caption(self):
filename = self.get_temp_file('test_datasource_can_set_caption')
self.ds.caption = 'bar'
self.ds.save_as(filename)

actual = Datasource.from_file(filename)
self.assertIsNotNone(actual)
self.assertIsNotNone(actual.caption)
self.assertEqual(actual.caption, 'bar')

def test_datasource_can_remove_caption(self):
filename = self.get_temp_file('test_datasource_can_remove_caption')
del self.ds.caption
self.ds.save_as(filename)

actual = Datasource.from_file(filename)
self.assertIsNotNone(actual)
self.assertEqual(actual.caption, '')

def test_datasource_clear_repository_location(self):
filename = os.path.join(TEST_ASSET_DIR, 'clear-repository-test.tds')

Expand Down

0 comments on commit 96d8967

Please sign in to comment.