diff --git a/Examples/GetFields/show_fields.py b/Examples/GetFields/show_fields.py
index b04a056..ee45f87 100644
--- a/Examples/GetFields/show_fields.py
+++ b/Examples/GetFields/show_fields.py
@@ -20,8 +20,8 @@
if field.calculation:
print(' the formula is {}'.format(field.calculation))
blank_line = True
- if field.aggregation:
- print(' the default aggregation is {}'.format(field.aggregation))
+ if field.default_aggregation:
+ print(' the default aggregation is {}'.format(field.default_aggregation))
blank_line = True
if blank_line:
diff --git a/tableaudocumentapi/field.py b/tableaudocumentapi/field.py
index 1eb68ef..8162cdb 100644
--- a/tableaudocumentapi/field.py
+++ b/tableaudocumentapi/field.py
@@ -41,12 +41,6 @@ def apply_metadata(self, metadata_record):
def from_xml(cls, xmldata):
return cls(xmldata)
- def __getattr__(self, item):
- private_name = '_{}'.format(item)
- if item in _ATTRIBUTES or item in _METADATA_ATTRIBUTES:
- return getattr(self, private_name)
- raise AttributeError(item)
-
def _apply_attribute(self, xmldata, attrib, default_func):
if hasattr(self, '_read_{}'.format(attrib)):
value = getattr(self, '_read_{}'.format(attrib))(xmldata)
@@ -71,6 +65,62 @@ def name(self):
return self.id
+ @property
+ def id(self):
+ """ Name of the field as specified in the file, usually surrounded by [ ] """
+ return self._id
+
+ @property
+ def caption(self):
+ """ Name of the field as displayed in Tableau unless an aliases is defined """
+ return self._caption
+
+ @property
+ def alias(self):
+ """ Name of the field as displayed in Tableau if the default name isn't wanted """
+ return self._alias
+
+ @property
+ def datatype(self):
+ """ Type of the field within Tableau (string, integer, etc) """
+ return self._datatype
+
+ @property
+ def role(self):
+ """ Dimension or Measure """
+ return self._role
+
+ @property
+ def is_quantitative(self):
+ """ A dependent value, usually a measure of something
+
+ e.g. Profit, Gross Sales """
+ return self._type == 'quantitative'
+
+ @property
+ def is_ordinal(self):
+ """ Is this field a categorical field that has a specific order
+
+ e.g. How do you feel? 1 - awful, 2 - ok, 3 - fantastic """
+ return self._type == 'ordinal'
+
+ @property
+ def is_nominal(self):
+ """ Is this field a categorical field that does not have a specific order
+
+ e.g. What color is your hair? """
+ return self._type == 'nominal'
+
+ @property
+ def calculation(self):
+ """ If this field is a calculated field, this will be the formula """
+ return self._calculation
+
+ @property
+ def default_aggregation(self):
+ """ The default type of aggregation on the field (e.g Sum, Avg)"""
+ return self._aggregation
+
######################################
# Special Case handling methods for reading the values from the XML
######################################
diff --git a/tableaudocumentapi/multilookup_dict.py b/tableaudocumentapi/multilookup_dict.py
index 21e2736..64b742a 100644
--- a/tableaudocumentapi/multilookup_dict.py
+++ b/tableaudocumentapi/multilookup_dict.py
@@ -1,11 +1,17 @@
import weakref
+_no_default_value = object()
+
+
def _resolve_value(key, value):
+ retval = None
try:
- retval = value.get(key, None)
+ if hasattr(value, 'get'):
+ retval = value.get(key, None)
+
if retval is None:
- retval = value.getattr(key, None)
+ retval = getattr(value, key, None)
except AttributeError:
retval = None
return retval
@@ -43,6 +49,14 @@ def __setitem__(self, key, value):
dict.__setitem__(self, key, value)
+ def get(self, key, default_value=_no_default_value):
+ try:
+ return self[key]
+ except KeyError:
+ if default_value is not _no_default_value:
+ return default_value
+ raise
+
def __getitem__(self, key):
if key in self._indexes['alias']:
key = self._indexes['alias'][key]
diff --git a/test/assets/datasource_test.tds b/test/assets/datasource_test.tds
index 227e006..a1e78a8 100644
--- a/test/assets/datasource_test.tds
+++ b/test/assets/datasource_test.tds
@@ -77,7 +77,7 @@
-
+
diff --git a/test/test_datasource.py b/test/test_datasource.py
index da956ee..0a2457e 100644
--- a/test/test_datasource.py
+++ b/test/test_datasource.py
@@ -1,6 +1,5 @@
import unittest
import os.path
-import functools
from tableaudocumentapi import Datasource
@@ -23,7 +22,23 @@ def test_datasource_returns_calculation_from_fields(self):
self.assertEqual('1', self.ds.fields['[Number of Records]'].calculation)
def test_datasource_uses_metadata_record(self):
- self.assertEqual('Sum', self.ds.fields['[x]'].aggregation)
+ self.assertEqual('Sum', self.ds.fields['[x]'].default_aggregation)
def test_datasource_column_name_contains_apostrophy(self):
self.assertIsNotNone(self.ds.fields.get("[Today's Date]", None))
+
+ def test_datasource_field_can_get_caption(self):
+ self.assertEqual(self.ds.fields['[a]'].caption, 'A')
+ self.assertEqual(getattr(self.ds.fields['[a]'], 'caption', None), 'A')
+
+ def test_datasource_field_caption_can_be_used_to_query(self):
+ self.assertIsNotNone(self.ds.fields.get('A', None))
+
+ def test_datasource_field_is_nominal(self):
+ self.assertTrue(self.ds.fields['[a]'].is_nominal)
+
+ def test_datasource_field_is_quantitative(self):
+ self.assertTrue(self.ds.fields['[y]'].is_quantitative)
+
+ def test_datasource_field_is_ordinal(self):
+ self.assertTrue(self.ds.fields['[x]'].is_ordinal)
diff --git a/test/test_multidict.py b/test/test_multidict.py
index abb01c5..0a78e9d 100644
--- a/test/test_multidict.py
+++ b/test/test_multidict.py
@@ -45,3 +45,19 @@ def test_mutlilookupdict_can_still_find_caption_even_with_alias(self):
def test_mutlilookupdict_can_still_find_id_even_with_caption(self):
actual = self.mld['[bar]']
self.assertEqual(2, actual['value'])
+
+ def test_multilookupdict_gives_key_error_on_invalid_key(self):
+ try:
+ self.mld.get('foobar')
+ self.fail('should have thrown key error')
+ except KeyError as ex:
+ self.assertEqual(str(ex), "'foobar'")
+
+ def test_multilookupdict_get_returns_default_value(self):
+ default_value = ('default', 'return', 'value')
+ actual = self.mld.get('foobar', default_value)
+ self.assertEqual(actual, default_value)
+
+ def test_multilookupdict_get_returns_value(self):
+ actual = self.mld.get('baz')
+ self.assertEqual(1, actual['value'])