Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 56 additions & 31 deletions tabcmd/commands/datasources_and_workbooks/export_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,6 @@ def define_args(export_parser):
help="View filter to apply to the view",
)

# TODO: ARGUMENT --COMPLETE

@staticmethod
def get_content_url_for_workbook(url):
# check the size of list
separated_list = url.split("/")
reversed_list = separated_list[::-1]
return reversed_list[1]

@staticmethod
def get_content_url_for_view(url):
# check the size of list
separated_list = url.split("/")
if len(separated_list) > 2:
print("error")
workbook_name = separated_list[0]
view_name = separated_list[1]
return DatasourcesAndWorkbooks.get_view_url_from_names(workbook_name, view_name)

"""
Command to Export a view_name or workbook from Tableau Server and save
it to a file. This command can also export just the data used for a view_name
Expand All @@ -73,57 +54,91 @@ def run_command(args):
if not view_content_url and not wb_content_url:
Errors.exit_with_error(logger, _("export.errors.requires_workbook_view_param").format(ExportCommand))

try:
logger.debug(args.pagelayout, args.pagesize, args.filename, args.width, args.height, args.filter)

try:
if args.fullpdf: # it's a workbook
workbook_item = ExportCommand.get_wb_by_content_url(logger, server, wb_content_url)
output = ExportCommand.download_wb_pdf(server, workbook_item)
output = ExportCommand.download_wb_pdf(server, workbook_item, args.url, logger)
default_filename = "{}.pdf".format(workbook_item.name)

elif args.pdf or args.png or args.csv: # it's a view
view_item = ExportCommand.get_view_by_content_url(logger, server, view_content_url)

if args.pdf:
output = ExportCommand.download_view_pdf(server, view_item)
output = ExportCommand.download_view_pdf(server, view_item, args.url, logger)
default_filename = "{}.pdf".format(view_item.name)
elif args.csv:
output = ExportCommand.download_csv(server, view_item)
output = ExportCommand.download_csv(server, view_item, args.url, logger)
default_filename = "{}.csv".format(view_item.name)
elif args.png:
output = ExportCommand.download_png(server, view_item)
output = ExportCommand.download_png(server, view_item, args.url, logger)
default_filename = "{}.png".format(view_item.name)

except Exception as e:
Errors.exit_with_error(logger, _("publish.errors.unexpected_server_response").format(""), e)

try:
save_name = args.filename or default_filename
ExportCommand.save_to_file(logger, output, save_name)
if args.csv:
ExportCommand.save_to_data_file(logger, output, save_name)
else:
ExportCommand.save_to_file(logger, output, save_name)

except Exception as e:
Errors.exit_with_error(logger, "Error saving to file", e)

@staticmethod
def download_wb_pdf(server, workbook_item):
def extract_filter_values_from_url_params(request_options: TSC.PDFRequestOptions, url, logger=None) -> None:
try:
# todo make logging better
logger = logger or log(ExportCommand.__class__.__name__, "DEBUG")
logger.debug(url)

if "?" in url:
query = url.split("?")[1]
else:
return

params = query.split("&")
logger.trace(params)
for value in params:
data_filter = value.split("=")
request_options.vf(data_filter[0], data_filter[1])
except BaseException as e:
logger.error("Error building filter params", e)
ExportCommand.log_stack(logger) # type: ignore

@staticmethod
def download_wb_pdf(server, workbook_item, url, logger):
logger.trace(url)
pdf = TSC.PDFRequestOptions(maxage=1)
ExportCommand.extract_filter_values_from_url_params(pdf, url)
server.workbooks.populate_pdf(workbook_item, pdf)
return workbook_item.pdf

@staticmethod
def download_view_pdf(server, view_item):
def download_view_pdf(server, view_item, url, logger):
logger.trace(url)
pdf = TSC.PDFRequestOptions(maxage=1)
ExportCommand.extract_filter_values_from_url_params(pdf, url)
logger.trace(pdf.view_filters)
server.views.populate_pdf(view_item, pdf)
return view_item.pdf

@staticmethod
def download_csv(server, view_item):
def download_csv(server, view_item, url, logger):
logger.trace(url)
csv = TSC.CSVRequestOptions(maxage=1)
ExportCommand.extract_filter_values_from_url_params(csv, url)
server.views.populate_csv(view_item, csv)
return view_item.csv

@staticmethod
def download_png(server, view_item):
def download_png(server, view_item, url, logger):
logger.trace(url)
req_option_image = TSC.ImageRequestOptions(maxage=1)
ExportCommand.extract_filter_values_from_url_params(req_option_image, url)
server.views.populate_image(view_item, req_option_image)
return view_item.png

Expand All @@ -132,7 +147,10 @@ def parse_export_url_to_workbook_and_view(logger, url):
logger.info(_("export.status").format(url))
if " " in url:
Errors.exit_with_error(logger, _("export.errors.white_space_workbook_view"))
# input should be workbook_name/view_name
if "?" in url:
url = url.split("?")[0]
# input should be workbook_name/view_name or /workbook_name/view_name
url = url.lstrip("/") # strip opening / if present
if not url.find("/"):
return None, None
name_parts = url.split("/")
Expand All @@ -142,9 +160,16 @@ def parse_export_url_to_workbook_and_view(logger, url):
view = "{}/sheets/{}".format(workbook, name_parts[1])
return view, workbook

@staticmethod
def save_to_data_file(logger, output, filename):
logger.info(_("httputils.found_attachment").format(filename))
with open(filename, "wb") as f:
f.writelines(output)
logger.info(_("export.success").format("", filename))

@staticmethod
def save_to_file(logger, output, filename):
logger.info(_("httputils.found_attachment").format(filename))
with open(filename, "wb") as f:
f.write(output)
logger.info(_("export.success").format(filename, ""))
logger.info(_("export.success").format("", filename))
2 changes: 2 additions & 0 deletions tabcmd/execution/logger_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@ def configure_log(name: str, logging_level_input: str):

def log(file_name, logging_level):
logger = configure_log(file_name, logging_level)
if not hasattr(logger, "trace"):
logger.trace = logger.debug
return logger
80 changes: 79 additions & 1 deletion tests/commands/test_geturl_utils.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import unittest
from typing import Iterator
from unittest import mock

import tableauserverclient

from tabcmd.commands.datasources_and_workbooks.get_url_command import *
from tabcmd.commands.datasources_and_workbooks.export_command import *
from tabcmd.commands.server import Server

mock_logger = mock.MagicMock()

fake_item = mock.MagicMock(TSC.ViewItem)
fake_item.name = "fake-name"
fake_item.id = "fake-id"


class GeturlTests(unittest.TestCase):
def test_evaluate_file_name_pdf(self):
Expand Down Expand Up @@ -59,9 +67,79 @@ def test_view_name(self):
"""


@mock.patch("tableauserverclient.ViewItem", fake_item)
class ExportTests(unittest.TestCase):
def test_parse_export_url_to_workbook(self):

mock_logger = mock.MagicMock("logger")
fake_item.csv = mock.MagicMock("bytes[]")
fake_item.pdf = mock.MagicMock("bytes")
fake_item.png = mock.MagicMock("bytes")

def test_parse_export_url_to_workbook_and_view(self):
wb_url = "wb-name/view-name"
view, wb = ExportCommand.parse_export_url_to_workbook_and_view(mock_logger, wb_url)
assert view == "wb-name/sheets/view-name"
assert wb == "wb-name"

def test_parse_export_url_to_workbook_and_view_with_start_slash(self):
wb_url = "/wb-name/view-name"
view, wb = ExportCommand.parse_export_url_to_workbook_and_view(mock_logger, wb_url)
assert view == "wb-name/sheets/view-name"
assert wb == "wb-name"

def test_parse_export_url_to_workbook_and_view_bad_url(self):
wb_url = "wb-name/view-name/kitty"
view, wb = ExportCommand.parse_export_url_to_workbook_and_view(mock_logger, wb_url)
assert view is None
assert wb is None

def test_extract_query_params(self):
url = "wb-name/view-name?param1=value1"
options = TSC.PDFRequestOptions()
assert options.view_filters is not None
assert len(options.view_filters) is 0
ExportCommand.extract_filter_values_from_url_params(options, url)
assert len(options.view_filters) == 1
assert options.view_filters[0] == ("param1", "value1")

@mock.patch("tableauserverclient.Server")
def test_download_csv(self, mock_server):
mock_server.views = mock.MagicMock()
mock_server.views.csv = mock.MagicMock()
mock_view = tableauserverclient.ViewItem()
url = "wb-name/view-name?param1=value1"
ExportCommand.download_csv(mock_server, mock_view, url, mock_logger)

@mock.patch("tableauserverclient.Server")
def test_download_image(self, mock_server):
mock_server.views = mock.MagicMock()
mock_server.views.png = mock.MagicMock()
mock_view = tableauserverclient.ViewItem()
url = "wb-name/view-name?param1=value1"
ExportCommand.download_png(mock_server, mock_view, url, mock_logger)

@mock.patch("tableauserverclient.Server")
def test_download_view_pdf(self, mock_server):
mock_server.views = mock.MagicMock()
mock_server.views.pdf = mock.MagicMock()
mock_view = tableauserverclient.ViewItem()
url = "wb-name/view-name?param1=value1"
ExportCommand.download_view_pdf(mock_server, mock_view, url, mock_logger)

@mock.patch("tableauserverclient.Server")
def test_download_wb_pdf(self, mock_server):
mock_server.workbooks = mock.MagicMock()
mock_server.workbooks.pdf = mock.MagicMock()
mock_view = tableauserverclient.ViewItem()
url = "wb-name/view-name?param1=value1"
ExportCommand.download_wb_pdf(mock_server, mock_view, url, mock_logger)

def test_save_to_binary_file(self):
mock_content = bytes()
filename = "test_out.pdf"
ExportCommand.save_to_file(mock_logger, mock_content, filename)

def test_save_to_data_file(self):
mock_content = mock.MagicMock()
filename = "test_out.csv"
ExportCommand.save_to_data_file(mock_logger, mock_content, filename)
9 changes: 9 additions & 0 deletions tests/commands/test_run_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ def test_export(self, mock_session, mock_server):
mock_args.fullpdf = True
mock_args.filename = "filename.pdf"
mock_args.url = "workbook-name/view-name"
mock_args.csv = None
mock_args.image = None
mock_args.pdf = None
mock_args.pagelayout = None
mock_args.pagesize = None
mock_args.size = None
mock_args.height = None
mock_args.width = None
mock_args.filter = None
export_command.ExportCommand.run_command(mock_args)
mock_session.assert_called()

Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/online_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ def test_export_view(self):
file = os.path.join("tests", "assets", OnlineCommandTest.TWBX_FILE_WITH_EXTRACT)
self._publish_wb(file, name_on_server)
command = "export"
friendly_name = name_on_server + "/" + OnlineCommandTest.TWBX_WITH_EXTRACT_SHEET
friendly_name = name_on_server + "/" + OnlineCommandTest.TWBX_WITH_EXTRACT_SHEET + "?param1=3"
arguments = [command, friendly_name, "--pdf", "-f", "exported_view.pdf"]
_test_command(arguments)

Expand Down