Skip to content

Commit f46f3d9

Browse files
author
Russell Hay
authored
Fixes #50 - materialize the properties for completion (#52)
* Fixes #50 - materialize the properties for completion This also fixes a bug where captions and aliases weren't being populated into the multidict correctly due to insufficient testing on my part originally * fixing example to be correct * really fix it this time * Add docstrings for is_* properties * Cleaner version of get for mld * Move to is not instead of standard equality and fixed py3
1 parent 00a9649 commit f46f3d9

File tree

6 files changed

+108
-13
lines changed

6 files changed

+108
-13
lines changed

Examples/GetFields/show_fields.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
if field.calculation:
2121
print(' the formula is {}'.format(field.calculation))
2222
blank_line = True
23-
if field.aggregation:
24-
print(' the default aggregation is {}'.format(field.aggregation))
23+
if field.default_aggregation:
24+
print(' the default aggregation is {}'.format(field.default_aggregation))
2525
blank_line = True
2626

2727
if blank_line:

tableaudocumentapi/field.py

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,6 @@ def apply_metadata(self, metadata_record):
4141
def from_xml(cls, xmldata):
4242
return cls(xmldata)
4343

44-
def __getattr__(self, item):
45-
private_name = '_{}'.format(item)
46-
if item in _ATTRIBUTES or item in _METADATA_ATTRIBUTES:
47-
return getattr(self, private_name)
48-
raise AttributeError(item)
49-
5044
def _apply_attribute(self, xmldata, attrib, default_func):
5145
if hasattr(self, '_read_{}'.format(attrib)):
5246
value = getattr(self, '_read_{}'.format(attrib))(xmldata)
@@ -71,6 +65,62 @@ def name(self):
7165

7266
return self.id
7367

68+
@property
69+
def id(self):
70+
""" Name of the field as specified in the file, usually surrounded by [ ] """
71+
return self._id
72+
73+
@property
74+
def caption(self):
75+
""" Name of the field as displayed in Tableau unless an aliases is defined """
76+
return self._caption
77+
78+
@property
79+
def alias(self):
80+
""" Name of the field as displayed in Tableau if the default name isn't wanted """
81+
return self._alias
82+
83+
@property
84+
def datatype(self):
85+
""" Type of the field within Tableau (string, integer, etc) """
86+
return self._datatype
87+
88+
@property
89+
def role(self):
90+
""" Dimension or Measure """
91+
return self._role
92+
93+
@property
94+
def is_quantitative(self):
95+
""" A dependent value, usually a measure of something
96+
97+
e.g. Profit, Gross Sales """
98+
return self._type == 'quantitative'
99+
100+
@property
101+
def is_ordinal(self):
102+
""" Is this field a categorical field that has a specific order
103+
104+
e.g. How do you feel? 1 - awful, 2 - ok, 3 - fantastic """
105+
return self._type == 'ordinal'
106+
107+
@property
108+
def is_nominal(self):
109+
""" Is this field a categorical field that does not have a specific order
110+
111+
e.g. What color is your hair? """
112+
return self._type == 'nominal'
113+
114+
@property
115+
def calculation(self):
116+
""" If this field is a calculated field, this will be the formula """
117+
return self._calculation
118+
119+
@property
120+
def default_aggregation(self):
121+
""" The default type of aggregation on the field (e.g Sum, Avg)"""
122+
return self._aggregation
123+
74124
######################################
75125
# Special Case handling methods for reading the values from the XML
76126
######################################

tableaudocumentapi/multilookup_dict.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
import weakref
22

33

4+
_no_default_value = object()
5+
6+
47
def _resolve_value(key, value):
8+
retval = None
59
try:
6-
retval = value.get(key, None)
10+
if hasattr(value, 'get'):
11+
retval = value.get(key, None)
12+
713
if retval is None:
8-
retval = value.getattr(key, None)
14+
retval = getattr(value, key, None)
915
except AttributeError:
1016
retval = None
1117
return retval
@@ -43,6 +49,14 @@ def __setitem__(self, key, value):
4349

4450
dict.__setitem__(self, key, value)
4551

52+
def get(self, key, default_value=_no_default_value):
53+
try:
54+
return self[key]
55+
except KeyError:
56+
if default_value is not _no_default_value:
57+
return default_value
58+
raise
59+
4660
def __getitem__(self, key):
4761
if key in self._indexes['alias']:
4862
key = self._indexes['alias'][key]

test/assets/datasource_test.tds

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
</column>
7878
<column caption='A' datatype='string' name='[a]' role='dimension' type='nominal' />
7979
<column caption='Today&apos;s Date' datatype='string' name='[Today&apos;s Date]' role='dimension' type='nominal' />
80-
<column caption='X' datatype='integer' name='[x]' role='measure' type='quantitative' />
80+
<column caption='X' datatype='integer' name='[x]' role='measure' type='ordinal' />
8181
<column caption='Y' datatype='integer' name='[y]' role='measure' type='quantitative' />
8282
<layout dim-ordering='alphabetic' dim-percentage='0.5' measure-ordering='alphabetic' measure-percentage='0.5' show-structure='true' />
8383
<semantic-values>

test/test_datasource.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import unittest
22
import os.path
3-
import functools
43

54
from tableaudocumentapi import Datasource
65

@@ -23,7 +22,23 @@ def test_datasource_returns_calculation_from_fields(self):
2322
self.assertEqual('1', self.ds.fields['[Number of Records]'].calculation)
2423

2524
def test_datasource_uses_metadata_record(self):
26-
self.assertEqual('Sum', self.ds.fields['[x]'].aggregation)
25+
self.assertEqual('Sum', self.ds.fields['[x]'].default_aggregation)
2726

2827
def test_datasource_column_name_contains_apostrophy(self):
2928
self.assertIsNotNone(self.ds.fields.get("[Today's Date]", None))
29+
30+
def test_datasource_field_can_get_caption(self):
31+
self.assertEqual(self.ds.fields['[a]'].caption, 'A')
32+
self.assertEqual(getattr(self.ds.fields['[a]'], 'caption', None), 'A')
33+
34+
def test_datasource_field_caption_can_be_used_to_query(self):
35+
self.assertIsNotNone(self.ds.fields.get('A', None))
36+
37+
def test_datasource_field_is_nominal(self):
38+
self.assertTrue(self.ds.fields['[a]'].is_nominal)
39+
40+
def test_datasource_field_is_quantitative(self):
41+
self.assertTrue(self.ds.fields['[y]'].is_quantitative)
42+
43+
def test_datasource_field_is_ordinal(self):
44+
self.assertTrue(self.ds.fields['[x]'].is_ordinal)

test/test_multidict.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,19 @@ def test_mutlilookupdict_can_still_find_caption_even_with_alias(self):
4545
def test_mutlilookupdict_can_still_find_id_even_with_caption(self):
4646
actual = self.mld['[bar]']
4747
self.assertEqual(2, actual['value'])
48+
49+
def test_multilookupdict_gives_key_error_on_invalid_key(self):
50+
try:
51+
self.mld.get('foobar')
52+
self.fail('should have thrown key error')
53+
except KeyError as ex:
54+
self.assertEqual(str(ex), "'foobar'")
55+
56+
def test_multilookupdict_get_returns_default_value(self):
57+
default_value = ('default', 'return', 'value')
58+
actual = self.mld.get('foobar', default_value)
59+
self.assertEqual(actual, default_value)
60+
61+
def test_multilookupdict_get_returns_value(self):
62+
actual = self.mld.get('baz')
63+
self.assertEqual(1, actual['value'])

0 commit comments

Comments
 (0)
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