8000 New sample: Migrate with Datasources · SnarkyPapi/server-client-python@f310f3d · GitHub
[go: up one dir, main page]

Skip to content

Commit f310f3d

Browse files
benlowerRussell Hay
authored and
Russell Hay
committed
New sample: Migrate with Datasources
This sample shows how to migrate workbooks from one site to another and change their datasources using the Tableau Document API
1 parent 01235ea commit f310f3d

File tree

1 file changed

+147
-0
lines changed

1 file changed

+147
-0
lines changed

samples/migrate_with_datasources.py

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
####
2+
# This script will move workbooks from one site to another. It will find workbooks with a given tag, download them,
3+
# and then publish them to the destination site. Before moving the workbooks, we (optionally) modify them to point to
4+
# production datasources based on information contained in a CSV file.
5+
#
6+
# If a CSV file is used, it is assumed to have two columns: source_ds and dest_ds.
7+
#
8+
# To run the script, you must have installed Python 2.7.9 or later.
9+
####
10+
11+
12+
import argparse
13+
import csv
14+
import getpass
15+
import logging
16+
import shutil
17+
import tableaudocumentapi as TDA
18+
import tableauserverclient as TSC
19+
import tempfile
20+
21+
22+
def main():
23+
parser = argparse.ArgumentParser(description='Move workbooks with the given tag from one project to another.')
24+
parser.add_argument('--server', '-s', required=True, help='server address')
25+
parser.add_argument('--username', '-u', required=True, help='username to sign into server')
26+
parser.add_argument('--source-site', '-ss', required=True, help='source site to get workbooks from')
27+
parser.add_argument('--dest-site', '-ds', required=True, help='destination site to copy workbooks to')
28+
parser.add_argument('--tag', '-t', required=True, help='tag to search for')
29+
parser.add_argument('--csv', '-c', required=False, help='CSV file containing database info')
30+
parser.add_argument('--delete-source', '-d', required=False, help='use true to delete source wbs after migration')
31+
parser.add_argument('--logging-level', '-l', choices=['debug', 'info',
32+
'error'], default='error', help='desired logging level (set to error by default)')
33+
args = parser.parse_args()
34+
db_info = None
35+
password = getpass.getpass("Password: ")
36+
37+
# Set logging level based on user input, or error by default
38+
logging_level = getattr(logging, args.logging_level.upper())
39+
logging.basicConfig(level=logging_level)
40+
41+
# Step 1: Sign-in to server twice because the destination site has a
42+
# different site id and requires second server object
43+
auth_source = TSC.TableauAuth(args.username, password, args.source_site)
44+
auth_dest = TSC.TableauAuth(args.username, password, args.dest_site)
45+
46+
server = TSC.Server(args.server)
47+
dest_server = TSC.Server(args.server)
48+
49+
with server.auth.sign_in(auth_source):
50+
# Step 2: Verify our source and destination sites exist
51+
found_source_site = False
52+
found_dest_site = False
53+
54+
found_source_site, found_dest_site = verify_sites(server, args.source_site, args.dest_site)
55+
56+
# Step 3: get all workbooks with the tag (e.g. 'ready-for-prod') using a filter
57+
req_option = TSC.RequestOptions()
58+
req_option.filter.add(TSC.Filter(TSC.RequestOptions.Field.Tags, TSC.RequestOptions.Operator.Equals, args.tag))
59+
all_workbooks, pagination_item = server.workbooks.get(req_option)
60+
61+
# Step 4: Download workbooks to a temp dir and loop thru them
62+
if len(all_workbooks) > 0:
63+
tmpdir = tempfile.mkdtemp()
64+
65+
try:
66+
# We got a CSV so let's make a dictionary
67+
if args.csv:
68+
db_info = dict_from_csv(args.csv)
69+
70+
# Signing into another site requires another server object b/c of the different auth token and site ID
71+
with dest_server.auth.sign_in(auth_dest):
72+
for wb in all_workbooks:
73+
wb_path = server.workbooks.download(wb.id, tmpdir)
74+
75+
# Step 5: If we have a CSV of data sources then update each workbook db connection per our CSV
76+
if db_info:
77+
source_wb = TDA.Workbook(wb_path)
78+
79+
# if we have more than one datasource we need to loop
80+
for ds in source_wb.datasources:
81+
for c in ds.connections:
82+
if c.dbname in db_info.keys():
83+
c.dbname = db_info[c.dbname]
84+
ds.caption = c.dbname
85+
86+
source_wb.save_as(wb_path)
87+
88+
# Step 6: Find destination site's default project
89+
dest_sites, _ = dest_server.projects.get()
90+
target_project = next((project for project in dest_sites if project.is_default()), None)
91+
92+
# Step 7: If default project is found, form a new workbook item and publish
93+
if target_project is not None:
94+
new_workbook = TSC.WorkbookItem(name=wb.name, project_id=target_project.id)
95+
new_workbook = dest_server.workbooks.publish(
96+
new_workbook, wb_path, mode=TSC.Server.PublishMode.Overwrite)
97+
98+
print("Successfully moved {0} ({1})".format(
99+
new_workbook.name, new_workbook.id))
100+
else:
101+
error = "The default project could not be found."
102+
raise LookupError(error)
103+
104+
# Step 8: (if requested) Delete workbook from source site and delete temp directory
105+
if args.delete_source:
106+
server.workbooks.delete(wb.id)
107+
finally:
108+
shutil.rmtree(tmpdir)
109+
110+
# No workbooks found
111+
else:
112+
print('No workbooks with tag {} found.'.format(args.tag))
113+
114+
115+
# Takes a Tableau Server URL and two site names. Returns true, true if the sites exist on the server
116+
117+
def verify_sites(server, site1, site2):
118+
found_site1 = False
119+
found_site2 = False
120+
121+
# Use the Pager to get all the sites
122+
for site in TSC.Pager(server.sites):
123+
if site1.lower() == site.content_url.lower():
124+
found_site1 = B893 True
125+
if site2.lower() == site.content_url.lower():
126+
found_site2 = True
127+
128+
if not found_site1:
129+
error = "Site named {} not found.".format(site1)
130+
raise LookupError(error)
131+
132+
if not found_site2:
133+
error = "Site named {} not found.".format(site2)
134+
raise LookupError(error)
135+
136+
return found_site1, found_site2
137+
138+
139+
# Returns a dictionary from a CSV file
140+
141+
def dict_from_csv(csv_file):
142+
with open(csv_file) as csvfile:
143+
return {value['source_ds']: value['dest_ds'] for value in csv.DictReader(csvfile)}
144+
145+
146+
if __name__ == "__main__":
147+
main()

0 commit comments

Comments
 (0)
0