8000 Fixes #50 - materialize the properties for completion (#52) · digideskio/document-api-python@f46f3d9 · GitHub
[go: up one dir, main page]

Skip to content < 8000 react-partial partial-name="keyboard-shortcuts-dialog" data-ssr="false" data-attempted-ssr="false" >

Commit f46f3d9

Browse files
author
Russell Hay
authored
Fixes tableau#50 - materialize the properties for completion (tableau#52)
* Fixes tableau#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)
0