8000 Added materialized views settings to site/workbook item, and a sample… · SnarkyPapi/server-client-python@c84bf26 · GitHub
[go: up one dir, main page]

Skip to content

Commit c84bf26

Browse files
author
bzhang
committed
Added materialized views settings to site/workbook item, and a sample script to update these settings
1 parent c50e080 commit c84bf26

File tree

10 files changed

+172
-25
lines changed

10 files changed

+172
-25
lines changed

samples/materialize_workbooks.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import argparse
2+
import getpass
3+
import logging
4+
5+
import tableauserverclient as TSC
6+
7+
8+
def main():
9+
parser = argparse.ArgumentParser(description='Materialized views settings for sites/workbooks.')
10+
parser.add_argument('--server', '-s', required=True, help='Tableau server address')
11+
parser.add_argument('--username', '-u', required=True, help='username to sign into server')
12+
parser.add_argument('--password', '-p', required=False, help='password to sign into server')
13+
parser.add_argument('--mode', '-m', required=False, choices=['enable', 'disable'],
14+
help='enable/disable materialized views for sites/workbooks')
15+
parser.add_argument('--status', '-st', required=False, action='store_true',
16+
help='show materialized views enabled sites/workbooks')
17+
parser.add_argument('--site-id', '-si', required=False,
18+
help='set to Default site by default')
19+
parser.add_argument('--logging-level', '-l', choices=['debug', 'info', 'error'], default='error',
20+
help='desired logging level (set to error by default)')
21+
parser.add_argument('--type', '-t', required=False, choices=['site', 'workbook'],
22+
help='type of content you want to update materialized views settings on')
23+
24+
args = parser.parse_args()
25+
26+
if args.password:
27+
password = args.password
28+
else:
29+
password = getpass.getpass("Password: ")
30+
31+
logging_level = getattr(logging, args.logging_level.upper())
32+
logging.basicConfig(level=logging_level)
33+
34+
# site content url is the TSC term for site id
35+
site_content_url = args.site_id if args.site_id is not None else ""
36+
enable_materialized_views = args.mode == "enable"
37+
38+
if (args.type is None) != (args.mode is None):
39+
print("Use '--type <workbook/site> --mode <enable/disable>' to update materialized views settings.")
40+
return
41+
42+
if args.type == 'site':
43+
update_site(args, enable_materialized_views, password, site_content_url)
44+
45+
elif args.type == 'workbook':
46+
update_workbook(args, enable_materialized_views, password, site_content_url)
47+
48+
if args.status:
49+
show_materialized_views_status(args, password, site_content_url)
50+
51+
52+
def show_materialized_views_status(args, password, site_content_url):
53+
tableau_auth = TSC.TableauAuth(args.username, password, site_id=site_content_url)
54+
server = TSC.Server(args.server)
55+
enabled_sites = set()
56+
with server.auth.sign_in(tableau_auth):
57+
# For server admin, this will prints all the materialized views enabled sites
58+
# For other users, this only prints the status of the site they belong to
59+
print("Materialized views is enabled on sites:")
60+
for site in TSC.Pager(server.sites):
61+
if site.materialized_views_enabled:
62+
enabled_sites.add(site)
63+
print "Site name:", site.name
64+
print
65+
print("Materialized views is enabled on workbooks:")
66+
# Individual workbooks can be enabled only when the sites they belong to are enabled too
67+
for site in enabled_sites:
68+
site_auth = TSC.TableauAuth(args.username, password, site.content_url)
69+
with server.auth.sign_in(site_auth):
70+
for workbook in TSC.Pager(server.workbooks):
71+
if workbook.materialized_views_enabled:
72+
print "Workbook:", workbook.name, "from site:", site.name
73+
74+
75+
def update_workbook(args, enable_materialized_views, password, site_content_url):
76+
tableau_auth = TSC.TableauAuth(args.username, password, site_id=site_content_url)
77+
server = TSC.Server(args.server)
78+
# Now it updates all the workbooks in the site
79+
# To update selected ones please use filter:
80+
# https://github.com/tableau/server-client-python/blob/master/docs/docs/filter-sort.md
81+
# This only updates the workbooks in the site you are signing into
82+
with server.auth.sign_in(tableau_auth):
83+
for workbook in TSC.Pager(server.workbooks):
84+
workbook.materialized_views_enabled = enable_materialized_views
85+
86+
server.workbooks.update(workbook)
87+
site = server.sites.get_by_content_url(site_content_url)
88+
print "Updated materialized views settings for workbook:", workbook.name, "from site:", site.name
89+
print
90+
91+
92+
def update_site(args, enable_materialized_views, password, site_content_url):
93+
tableau_auth = TSC.TableauAuth(args.username, password, site_id=site_content_url)
94+
server = TSC.Server(args.server)
95+
with server.auth.sign_in(tableau_auth):
96+
site_to_update = server.sites.get_by_content_url(site_content_url)
97+
site_to_update.materialized_views_enabled = enable_materialized_views
98+
99+
server.sites.update(site_to_update)
100+
print "Updated materialized views settings for site:", site_to_update.name
101+
print
102+
103+
104+
if __name__ == "__main__":
105+
main()

tableauserverclient/models/site_item.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class State:
1717

1818
def __init__(self, name, content_url, admin_mode=None, user_quota=None, storage_quota=None,
1919
disable_subscriptions=False, subscribe_others_enabled=True, revision_history_enabled=False,
20-
revision_limit=None):
20+
revision_limit=None, materialized_views_enabled=False):
2121
self._admin_mode = None
2222
self._id = None
2323
self._num_users = None
@@ -33,6 +33,7 @@ def __init__(self, name, content_url, admin_mode=None, user_quota=None, storage_
3333
self.revision_history_enabled = revision_history_enabled
3434
self.subscribe_others_enabled = subscribe_others_enabled
3535
self.admin_mode = admin_mode
36+
self.materialized_views_enabled = materialized_views_enabled
3637

3738
@property
3839
def admin_mode(self):
@@ -123,6 +124,15 @@ def subscribe_others_enabled(self):
123124
def subscribe_others_enabled(self, value):
124125
self._subscribe_others_enabled = value
125126

127+
@property
128+
def materialized_views_enabled(self):
129+
return self._materialized_views_enabled
130+
131+
@materialized_views_enabled.setter
132+
@property_is_boolean
133+
def materialized_views_enabled(self, value):
134+
self._materialized_views_enabled = value
135+
126136
def is_default(self):
127137
return self.name.lower() == 'default'
128138

@@ -132,16 +142,17 @@ def _parse_common_tags(self, site_xml, ns):
132142
if site_xml is not None:
133143
(_, name, content_url, _, admin_mode, state,
134144
subscribe_others_enabled, disable_subscriptions, revision_history_enabled,
135-
user_quota, storage_quota, revision_limit, num_users, storage) = self._parse_element(site_xml, ns)
145+
user_quota, storage_quota, revision_limit, num_users, storage,
146+
materialized_views_enabled) = self._parse_element(site_xml, ns)
136147

137148
self._set_values(None, name, content_url, None, admin_mode, state, subscribe_others_enabled,
138149
disable_subscriptions, revision_history_enabled, user_quota, storage_quota,
139-
revision_limit, num_users, storage)
150+
revision_limit, num_users, storage, materialized_views_enabled)
140151
return self
141152

142153
def _set_values(self, id, name, content_url, status_reason, admin_mode, state,
143154
subscribe_others_enabled, disable_subscriptions, revision_history_enabled,
144-
user_quota, storage_quota, revision_limit, num_users, storage):
155+
user_quota, storage_quota, revision_limit, num_users, storage, materialized_views_enabled):
145156
if id is not None:
146157
self._id = id
147158
if name:
@@ -170,6 +181,8 @@ def _set_values(self, id, name, content_url, status_reason, admin_mode, state,
170181
self._num_users = num_users
171182
if storage:
172183
self._storage = storage
184+
if materialized_views_enabled:
185+
self._materialized_views_enabled = materialized_views_enabled
173186

174187
@classmethod
175188
def from_response(cls, resp, ns):
@@ -179,12 +192,13 @@ def from_response(cls, resp, ns):
179192
for site_xml in all_site_xml:
180193
(id, name, content_url, status_reason, admin_mode, state, subscribe_others_enabled,
181194
disable_subscriptions, revision_history_enabled, user_quota, storage_quota,
182-
revision_limit, num_users, storage) = cls._parse_element(site_xml, ns)
195+
revision_limit, num_users, storage, materialized_views_enabled) = cls._parse_element(site_xml, ns)
183196

184197
site_item = cls(name, content_url)
185198
site_item._set_values(id, name, content_url, status_reason, admin_mode, state,
186199
subscribe_others_enabled, disable_subscriptions, revision_history_enabled,
187-
user_quota, storage_quota, revision_limit, num_users, storage)
200+
user_quota, storage_quota, revision_limit, num_users, storage,
201+
materialized_views_enabled)
188202
all_site_items.append(site_item)
189203
return all_site_items
190204

@@ -219,9 +233,11 @@ def _parse_element(site_xml, ns):
219233
num_users = usage_elem.get('numUsers', None)
220234
storage = usage_elem.get('storage', None)
221235

236+
materialized_views_enabled = string_to_bool(site_xml.get('materializedViewsEnabled', ''))
237+
222238
return id, name, content_url, status_reason, admin_mode, state, subscribe_others_enabled,\
223239
disable_subscriptions, revision_history_enabled, user_quota, storage_quota,\
224-
revision_limit, num_users, storage
240+
revision_limit, num_users, storage, materialized_views_enabled
225241

226242

227243
# Used to convert string represented boolean to a boolean type

tableauserverclient/models/workbook_item.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def __init__(self, project_id, name=None, show_tabs=False):
2424
self.project_id = project_id
2525
self.show_tabs = show_tabs
2626
self.tags = set()
27+
self.materialized_views_enabled = None
2728

2829
@property
2930
def connections(self):
@@ -112,15 +113,18 @@ def _parse_common_tags(self, workbook_xml, ns):
112113
workbook_xml = ET.fromstring(workbook_xml).find('.//t:workbook', namespaces=ns)
113114
if workbook_xml is not None:
114115
(_, _, _, _, updated_at, _, show_tabs,
115-
project_id, project_name, owner_id, _, _) = self._parse_element(workbook_xml, ns)
116+
project_id, project_name, owner_id, _, _,
117+
materialized_views_enabled) = self._parse_element(workbook_xml, ns)
116118

117119
self._set_values(None, None, None, None, updated_at,
118-
None, show_tabs, project_id, project_name, owner_id, None, None)
120+
None, show_tabs, project_id, project_name, owner_id, None, None,
121+
materialized_views_enabled)
119122

120123
return self
121124

122125
def _set_values(self, id, name, content_url, created_at, updated_at,
123-
size, show_tabs, project_id, project_name, owner_id, tags, views):
126+
size, show_tabs, project_id, project_name, owner_id, tags, views,
127+
materialized_views_enabled):
124128
if id is not None:
125129
self._id = id
126130
if name:
@@ -146,6 +150,8 @@ def _set_values(self, id, name, content_url, created_at, updated_at,
146150
self._initial_tags = copy.copy(tags)
147151
if views:
148152
self._views = views
153+
if materialized_views_enabled is not None:
154+
self.materialized_views_enabled = materialized_views_enabled
149155

150156
@classmethod
151157
def from_response(cls, resp, ns):
@@ -154,11 +160,13 @@ def from_response(cls, resp, ns):
154160
all_workbook_xml = parsed_response.findall('.//t:workbook', namespaces=ns)
155161
for workbook_xml in all_workbook_xml:
156162
(id, name, content_url, created_at, updated_at, size, show_tabs,
157-
project_id, project_name, owner_id, tags, views) = cls._parse_element(workbook_xml, ns)
163+
project_id, project_name, owner_id, tags, views,
164+
materialized_views_enabled) = cls._parse_element(workbook_xml, ns)
158165

159166
workbook_item = cls(project_id)
160167
workbook_item._set_values(id, name, content_url, created_at, updated_at,
161-
size, show_tabs, None, project_name, owner_id, tags, views)
168+
size, show_tabs, None, project_name, owner_id, tags, views,
169+
materialized_views_enabled)
162170
all_workbook_items.append(workbook_item)
163171
return all_workbook_items
164172

@@ -199,8 +207,10 @@ def _parse_element(workbook_xml, ns):
199207
if views_elem is not None:
200208
views = ViewItem.from_xml_element(views_elem, ns)
201209

210+
materialized_views_enabled = string_to_bool(workbook_xml.get('materializedViewsEnabled', ''))
211+
202212
return id, name, content_url, created_at, updated_at, size, show_tabs,\
203-
project_id, project_name, owner_id, tags, views
213+
project_id, project_name, owner_id, tags, views, materialized_views_enabled
204214

205215

206216
# Used to convert string represented boolean to a boolean type

tableauserverclient/server/endpoint/sites_endpoint.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,19 @@ def get_by_name(self, site_name):
4444
server_response = self.get_request(url)
4545
return SiteItem.from_response(server_response.content, self.parent_srv.namespace)[0]
4646

47+
# Gets 1 site by content url
48+
@api(version="3.3")
49+
def get_by_content_url(self, content_url):
50+
if content_url is None:
51+
error = "Content URL undefined."
52+
raise ValueError(error)
53+
logger.info('Querying single site (Content URL: {0})'.format(content_url))
54+
url = "{0}/{1}?key=contentUrl".format(self.baseurl, content_url)
55+
server_response = self.get_request(url)
56+
return SiteItem.from_response(server_response.content, self.parent_srv.namespace)[0]
57+
4758
# Update site
48-
@api(version="2.0")
59+
@api(version="3.3")
4960
def update(self, site_item):
5061
if not site_item.id:
5162
error = "Site item missing ID."

tableauserverclient/server/endpoint/workbooks_endpoint.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def baseurl(self):
3131
return "{0}/sites/{1}/workbooks".format(self.parent_srv.baseurl, self.parent_srv.site_id)
3232

3333
# Get all workbooks on site
34-
@api(version="2.0")
34+
@api(version="3.3")
3535
def get(self, req_options=None):
3636
logger.info('Querying all workbooks on site')
3737
url = self.baseurl
@@ -41,7 +41,7 @@ def get(self, req_options=None):
4141
return all_workbook_items, pagination_item
4242

4343
# Get 1 workbook
44-
@api(version="2.0")
44+
@api(version="3.3")
4545
def get_by_id(self, workbook_id):
4646
if not workbook_id:
4747
error = "Workbook ID undefined."
@@ -70,7 +70,7 @@ def delete(self, workbook_id):
7070
logger.info('Deleted single workbook (ID: {0})'.format(workbook_id))
7171

7272
# Update workbook
73-
@api(version="2.0")
73+
@api(version="3.3")
7474
def update(self, workbook_item):
7575
if not workbook_item.id:
7676
error = "Workbook item missing ID. Workbook must be retrieved from server first."

tableauserverclient/server/request_factory.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -282,14 +282,16 @@ def update_req(self, site_item):
282282
site_element.attrib['state'] = site_item.state
283283
if site_item.storage_quota:
284284
site_element.attrib['storageQuota'] = str(site_item.storage_quota)
285-
if site_item.disable_subscriptions:
285+
if site_item.disable_subscriptions is not None:
286286
site_element.attrib['disableSubscriptions'] = str(site_item.disable_subscriptions).lower()
287-
if site_item.subscribe_others_enabled:
287+
if site_item.subscribe_others_enabled is not None:
288288
site_element.attrib['subscribeOthersEnabled'] = str(site_item.subscribe_others_enabled).lower()
289289
if site_item.revision_limit:
290290
site_element.attrib['revisionLimit'] = str(site_item.revision_limit)
291-
if site_item.subscribe_others_enabled:
291+
if site_item.revision_history_enabled is not None:
292292
site_element.attrib['revisionHistoryEnabled'] = str(site_item.revision_history_enabled).lower()
293+
if site_item.materialized_views_enabled is not None:
294+
site_element.attrib['materializedViewsEnabled'] = str(site_item.materialized_views_enabled).lower()
293295
return ET.tostring(xml_request)
294296

295297
def create_req(self, site_item):
@@ -380,6 +382,8 @@ def update_req(self, workbook_item):
380382
if workbook_item.owner_id:
381383
owner_element = ET.SubElement(workbook_element, 'owner')
382384
owner_element.attrib['id'] = workbook_item.owner_id
385+
if workbook_item.materialized_views_enabled is not None:
386+
workbook_element.attrib['materializedViewsEnabled'] = str(workbook_item.materialized_views_enabled).lower()
383387
return ET.tostring(xml_request)
384388

385389
def publish_req(self, workbook_item, filename, file_contents, connection_credentials=None, connections=None):

tableauserverclient/server/server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def __init__(self, server_address, use_server_version=False):
3636
self._session = requests.Session()
3737
self._http_options = dict()
3838

39-
self.version = "2.3"
39+
self.version = "3.3"
4040
self.auth = Auth(self)
4141
self.views = Views(self)
4242
self.users = Users(self)

test/test_group.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ def test_add_user_before_populating(self):
123123
add_user_response = f.read().decode('utf-8')
124124
with requests_mock.mock() as m:
125125
m.get(self.baseurl, text=get_xml_response)
126-
m.post('http://test/api/2.3/sites/dad65087-b08b-4603-af4e-2887b8aafc67/groups/ef8b19c0-43b6-11e6-af50'
126+
m.post('http://test/api/3.3/sites/dad65087-b08b-4603-af4e-2887b8aafc67/groups/ef8b19c0-43b6-11e6-af50'
127127
'-63f5805dbe3c/users', text=add_user_response)
128128
all_groups, pagination_item = self.server.groups.get()
129129
single_group = all_groups[0]
@@ -151,7 +151,7 @@ def test_remove_user_before_populating(self):
151151
response_xml = f.read().decode('utf-8')
152152
with requests_mock.mock() as m:
153153
m.get(self.baseurl, text=response_xml)
154-
m.delete('http://test/api/2.3/sites/dad65087-b08b-4603-af4e-2887b8aafc67/groups/ef8b19c0-43b6-11e6-af50'
154+
m.delete('http://test/api/3.3/sites/dad65087-b08b-4603-af4e-2887b8aafc67/groups/ef8b19c0-43b6-11e6-af50'
155155
'-63f5805dbe3c/users/5de011f8-5aa9-4d5b-b991-f462c8dd6bb7',
156156
text='ok')
157157
all_groups, pagination_item = self.server.groups.get()

test/test_schedule.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def test_add_workbook(self):
201201
self.assertEqual(0, len(result), "Added properly")
202202

203203
def test_add_datasource(self):
204-
self.server.version = "2.8"
204+
self.server.version = "3.3"
205205
baseurl = "{}/sites/{}/schedules".format(self.server.baseurl, self.server.site_id)
206206

207207
with open(DATASOURCE_GET_BY_ID_XML, "rb") as f:

test/test_site.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ def test_update(self):
9191
single_site = TSC.SiteItem(name='Tableau', content_url='tableau',
9292
admin_mode=TSC.SiteItem.AdminMode.ContentAndUsers,
9393
user_quota=15, storage_quota=1000,
94-
disable_subscriptions=True, revision_history_enabled=False)
94+
disable_subscriptions=True, revision_history_enabled=False,
95+
materialized_views_enabled=False)
9596
single_site._id = '6b7179ba-b82b-4f0f-91ed-812074ac5da6'
9697
single_site = self.server.sites.update(single_site)
9798

0 commit comments

Comments
 (0)
0