From 1203fc1b4d37454af2fcd6aa6c9dc6bcc9d9eb84 Mon Sep 17 00:00:00 2001 From: Renoy John Date: Mon, 7 Apr 2025 17:04:43 -0700 Subject: [PATCH 1/5] Adding replace & thumbnail options to publish command --- .../export_command.py | 8 ++++ .../publish_command.py | 34 +++++++------- tabcmd/execution/global_options.py | 2 +- tests/commands/test_publish_command.py | 45 ++++++++++++++++++- 4 files changed, 68 insertions(+), 21 deletions(-) diff --git a/tabcmd/commands/datasources_and_workbooks/export_command.py b/tabcmd/commands/datasources_and_workbooks/export_command.py index 37d90ed3..f2e44b67 100644 --- a/tabcmd/commands/datasources_and_workbooks/export_command.py +++ b/tabcmd/commands/datasources_and_workbooks/export_command.py @@ -151,6 +151,8 @@ def apply_filters_from_args(request_options: TSC.RequestOptions, args, logger=No def download_wb_pdf(server, workbook_item, args, logger): logger.debug(args.url) pdf_options = TSC.PDFRequestOptions(maxage=1) + if args.language: + pdf_options.language = args.language ExportCommand.apply_values_from_url_params(logger, pdf_options, args.url) ExportCommand.apply_filters_from_args(pdf_options, args, logger) ExportCommand.apply_pdf_options(logger, pdf_options, args) @@ -162,6 +164,8 @@ def download_wb_pdf(server, workbook_item, args, logger): def download_view_pdf(server_content_type, export_item, args, logger): logger.debug(args.url) pdf_options = TSC.PDFRequestOptions(maxage=1) + if args.language: + pdf_options.language = args.language ExportCommand.apply_values_from_url_params(logger, pdf_options, args.url) ExportCommand.apply_filters_from_args(pdf_options, args, logger) ExportCommand.apply_pdf_options(logger, pdf_options, args) @@ -173,6 +177,8 @@ def download_view_pdf(server_content_type, export_item, args, logger): def download_csv(server_content_type, export_item, args, logger): logger.debug(args.url) csv_options = TSC.CSVRequestOptions(maxage=1) + if args.language: + csv_options.language = args.language ExportCommand.apply_values_from_url_params(logger, csv_options, args.url) ExportCommand.apply_filters_from_args(csv_options, args, logger) logger.debug(csv_options.get_query_params()) @@ -183,6 +189,8 @@ def download_csv(server_content_type, export_item, args, logger): def download_png(server_content_type, export_item, args, logger): logger.debug(args.url) image_options = TSC.ImageRequestOptions(maxage=1) + if args.language: + image_options.language = args.language ExportCommand.apply_values_from_url_params(logger, image_options, args.url) ExportCommand.apply_filters_from_args(image_options, args, logger) DatasourcesAndWorkbooks.apply_png_options(logger, image_options, args) diff --git a/tabcmd/commands/datasources_and_workbooks/publish_command.py b/tabcmd/commands/datasources_and_workbooks/publish_command.py index 98a3e5e9..6388cc38 100644 --- a/tabcmd/commands/datasources_and_workbooks/publish_command.py +++ b/tabcmd/commands/datasources_and_workbooks/publish_command.py @@ -76,12 +76,15 @@ def run_command(args): source = PublishCommand.get_filename_extension_if_tableau_type(logger, args.filename) logger.info(_("publish.status").format(args.filename)) if source in ["twbx", "twb"]: - if args.thumbnail_group: - raise AttributeError("Generating thumbnails for a group is not yet implemented.") if args.thumbnail_username and args.thumbnail_group: raise AttributeError("Cannot specify both a user and group for thumbnails.") new_workbook = TSC.WorkbookItem(project_id, name=args.name, show_tabs=args.tabbed) + if args.thumbnail_username: + new_workbook.thumbnails_user_id = args.thumbnail_username + elif args.thumbnail_group: + new_workbook.thumbnails_group_id = args.thumbnail_group + try: new_workbook = server.workbooks.publish( new_workbook, @@ -114,24 +117,19 @@ def get_publish_mode(args, logger): default_mode = TSC.Server.PublishMode.CreateNew publish_mode = default_mode - if args.replace: - raise AttributeError("Replacing an extract is not yet implemented") - - if args.append: - if publish_mode != default_mode: - publish_mode = None - else: - # only relevant for datasources, but tsc will throw an error for us if necessary - publish_mode = TSC.Server.PublishMode.Append + mode_mapping = { + "replace": TSC.Server.PublishMode.Replace, + "append": TSC.Server.PublishMode.Append, + "overwrite": TSC.Server.PublishMode.Overwrite, + } - if args.overwrite: - if publish_mode != default_mode: - publish_mode = None - else: - # Overwrites the workbook, data source, or data extract if it already exists on the server. - publish_mode = TSC.Server.PublishMode.Overwrite + selected_modes = [mode for mode, mode_value in mode_mapping.items() if getattr(args, mode, False)] - if not publish_mode: + if len(selected_modes) > 1: Errors.exit_with_error(logger, "Invalid combination of publishing options (Append, Overwrite, Replace)") + + if selected_modes: + publish_mode = mode_mapping[selected_modes[0]] + logger.debug("Publish mode selected: " + publish_mode) return publish_mode diff --git a/tabcmd/execution/global_options.py b/tabcmd/execution/global_options.py index 849316ef..f4c85361 100644 --- a/tabcmd/execution/global_options.py +++ b/tabcmd/execution/global_options.py @@ -327,7 +327,7 @@ def set_publish_args(parser): ) thumbnails.add_argument( "--thumbnail-group", - help="[Not yet implemented] If the workbook contains user filters, the thumbnails will be generated based on what the " + help="If the workbook contains user filters, the thumbnails will be generated based on what the " "specified group can see. Cannot be specified when --thumbnail-username option is set.", ) diff --git a/tests/commands/test_publish_command.py b/tests/commands/test_publish_command.py index 800c81fc..ab37646a 100644 --- a/tests/commands/test_publish_command.py +++ b/tests/commands/test_publish_command.py @@ -3,8 +3,7 @@ from unittest.mock import * import tableauserverclient as TSC -from tabcmd.commands.auth import login_command -from tabcmd.commands.datasources_and_workbooks import delete_command, export_command, get_url_command, publish_command +from tabcmd.commands.datasources_and_workbooks import publish_command from typing import List, NamedTuple, TextIO, Union @@ -31,6 +30,8 @@ getter.get = MagicMock("get", return_value=([fake_item], fake_item_pagination)) getter.publish = MagicMock("publish", return_value=fake_item) +mock_logger = MagicMock() + @patch("tableauserverclient.Server") @patch("tabcmd.commands.auth.session.Session.create_session") @@ -95,3 +96,43 @@ def test_publish_with_creds(self, mock_session, mock_server): mock_server.projects = getter publish_command.PublishCommand.run_command(mock_args) mock_session.assert_called() + + def test_default_publish_mode(self, mock_session, mock_server): + mock_args.replace = False + mock_args.append = False + mock_args.overwrite = False + + publish_mode = publish_command.PublishCommand.get_publish_mode(mock_args, mock_logger) + self.assertEqual(publish_mode, TSC.Server.PublishMode.CreateNew) + + def test_replace_publish_mode(self, mock_session, mock_server): + mock_args.replace = True + mock_args.append = False + mock_args.overwrite = False + + publish_mode = publish_command.PublishCommand.get_publish_mode(mock_args, mock_logger) + self.assertEqual(publish_mode, TSC.Server.PublishMode.Replace) + + def test_append_publish_mode(self, mock_session, mock_server): + mock_args.replace = False + mock_args.append = True + mock_args.overwrite = False + + publish_mode = publish_command.PublishCommand.get_publish_mode(mock_args, mock_logger) + self.assertEqual(publish_mode, TSC.Server.PublishMode.Append) + + def test_overwrite_publish_mode(self, mock_session, mock_server): + mock_args.replace = False + mock_args.append = False + mock_args.overwrite = True + + publish_mode = publish_command.PublishCommand.get_publish_mode(mock_args, mock_logger) + self.assertEqual(publish_mode, TSC.Server.PublishMode.Overwrite) + + def test_invalid_combination_of_modes(self, mock_session, mock_server): + mock_args.replace = True + mock_args.append = True + mock_args.overwrite = False + + with self.assertRaises(SystemExit): + publish_command.PublishCommand.get_publish_mode(mock_args, mock_logger) From 71768f9e0a43278298a2958b5b2a73f718be6408 Mon Sep 17 00:00:00 2001 From: Renoy John Date: Tue, 8 Apr 2025 09:56:31 -0700 Subject: [PATCH 2/5] Fixing test failures --- tests/commands/test_geturl_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/commands/test_geturl_utils.py b/tests/commands/test_geturl_utils.py index 92e7c0bb..b5e8dd54 100644 --- a/tests/commands/test_geturl_utils.py +++ b/tests/commands/test_geturl_utils.py @@ -18,6 +18,7 @@ mock_args.filename = None mock_args.filter = None mock_args.resolution = None +mock_args.language = None mock_logger = mock.MagicMock() From dae67acaca23181802173100e2120d5c159847e0 Mon Sep 17 00:00:00 2001 From: Renoy John Date: Tue, 8 Apr 2025 10:00:14 -0700 Subject: [PATCH 3/5] Fixing an export test failure --- tests/commands/test_run_commands.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/commands/test_run_commands.py b/tests/commands/test_run_commands.py index 958b0bfa..2bcd4d35 100644 --- a/tests/commands/test_run_commands.py +++ b/tests/commands/test_run_commands.py @@ -120,6 +120,7 @@ def test_export(self, mock_session, mock_server): mock_args.height = None mock_args.width = None mock_args.filter = None + #mock_args.language = None export_command.ExportCommand.run_command(mock_args) mock_session.assert_called() From d3ec0e801617f419b0af4cf0ad1b990c04dfc4db Mon Sep 17 00:00:00 2001 From: Renoy John Date: Tue, 8 Apr 2025 10:08:20 -0700 Subject: [PATCH 4/5] uncommenting the fix --- tests/commands/test_run_commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/commands/test_run_commands.py b/tests/commands/test_run_commands.py index 2bcd4d35..2a6ff820 100644 --- a/tests/commands/test_run_commands.py +++ b/tests/commands/test_run_commands.py @@ -120,7 +120,7 @@ def test_export(self, mock_session, mock_server): mock_args.height = None mock_args.width = None mock_args.filter = None - #mock_args.language = None + mock_args.language = None export_command.ExportCommand.run_command(mock_args) mock_session.assert_called() From 73fa4961cf0d6aafa98dac5f4c8cacb1d273b799 Mon Sep 17 00:00:00 2001 From: Renoy John Date: Tue, 8 Apr 2025 11:37:41 -0700 Subject: [PATCH 5/5] PR comment refactor --- .../datasources_and_workbooks_command.py | 9 ++++ .../export_command.py | 9 +--- .../test_datasources_and_workbooks_command.py | 41 +++++++++++++++++++ 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py b/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py index 539ae53e..4dc36e64 100644 --- a/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py +++ b/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py @@ -155,6 +155,8 @@ def apply_png_options(logger, request_options: TSC.ImageRequestOptions, args): request_options.image_resolution = None else: request_options.image_resolution = TSC.ImageRequestOptions.Resolution.High.lower() + if args.language: + request_options.language = args.language @staticmethod def apply_pdf_options(logger, request_options: TSC.PDFRequestOptions, args): @@ -162,6 +164,13 @@ def apply_pdf_options(logger, request_options: TSC.PDFRequestOptions, args): request_options.orientation = args.pagelayout if args.pagesize: request_options.page_type = args.pagesize + if args.language: + request_options.language = args.language + + @staticmethod + def apply_csv_options(logger, request_options: TSC.CSVRequestOptions, args): + if args.language: + request_options.language = args.language @staticmethod def save_to_data_file(logger, output, filename): diff --git a/tabcmd/commands/datasources_and_workbooks/export_command.py b/tabcmd/commands/datasources_and_workbooks/export_command.py index f2e44b67..50267c19 100644 --- a/tabcmd/commands/datasources_and_workbooks/export_command.py +++ b/tabcmd/commands/datasources_and_workbooks/export_command.py @@ -151,8 +151,6 @@ def apply_filters_from_args(request_options: TSC.RequestOptions, args, logger=No def download_wb_pdf(server, workbook_item, args, logger): logger.debug(args.url) pdf_options = TSC.PDFRequestOptions(maxage=1) - if args.language: - pdf_options.language = args.language ExportCommand.apply_values_from_url_params(logger, pdf_options, args.url) ExportCommand.apply_filters_from_args(pdf_options, args, logger) ExportCommand.apply_pdf_options(logger, pdf_options, args) @@ -164,8 +162,6 @@ def download_wb_pdf(server, workbook_item, args, logger): def download_view_pdf(server_content_type, export_item, args, logger): logger.debug(args.url) pdf_options = TSC.PDFRequestOptions(maxage=1) - if args.language: - pdf_options.language = args.language ExportCommand.apply_values_from_url_params(logger, pdf_options, args.url) ExportCommand.apply_filters_from_args(pdf_options, args, logger) ExportCommand.apply_pdf_options(logger, pdf_options, args) @@ -177,10 +173,9 @@ def download_view_pdf(server_content_type, export_item, args, logger): def download_csv(server_content_type, export_item, args, logger): logger.debug(args.url) csv_options = TSC.CSVRequestOptions(maxage=1) - if args.language: - csv_options.language = args.language ExportCommand.apply_values_from_url_params(logger, csv_options, args.url) ExportCommand.apply_filters_from_args(csv_options, args, logger) + DatasourcesAndWorkbooks.apply_csv_options(logger, csv_options, args) logger.debug(csv_options.get_query_params()) server_content_type.populate_csv(export_item, csv_options) return export_item.csv @@ -189,8 +184,6 @@ def download_csv(server_content_type, export_item, args, logger): def download_png(server_content_type, export_item, args, logger): logger.debug(args.url) image_options = TSC.ImageRequestOptions(maxage=1) - if args.language: - image_options.language = args.language ExportCommand.apply_values_from_url_params(logger, image_options, args.url) ExportCommand.apply_filters_from_args(image_options, args, logger) DatasourcesAndWorkbooks.apply_png_options(logger, image_options, args) diff --git a/tests/commands/test_datasources_and_workbooks_command.py b/tests/commands/test_datasources_and_workbooks_command.py index 8b837afd..0986cd1a 100644 --- a/tests/commands/test_datasources_and_workbooks_command.py +++ b/tests/commands/test_datasources_and_workbooks_command.py @@ -50,16 +50,30 @@ def test_apply_png_options(self): mock_args.width = "800" mock_args.height = "76" mock_args.resolution = None + mock_args.language = None request_options = tsc.ImageRequestOptions() DatasourcesAndWorkbooks.apply_png_options(mock_logger, request_options, mock_args) assert request_options.image_resolution == "high" assert request_options.viz_width == 800 assert request_options.viz_height == 76 + def test_apply_png_options_with_language(self): + mock_args.width = "800" + mock_args.height = "76" + mock_args.resolution = None + mock_args.language = "de" + request_options = tsc.ImageRequestOptions() + DatasourcesAndWorkbooks.apply_png_options(mock_logger, request_options, mock_args) + assert request_options.image_resolution == "high" + assert request_options.viz_width == 800 + assert request_options.viz_height == 76 + assert request_options.language == "de" + def test_apply_png_options_with_resolution_high(self): mock_args.width = "800" mock_args.height = "76" mock_args.resolution = "high" + mock_args.language = None request_options = tsc.ImageRequestOptions() DatasourcesAndWorkbooks.apply_png_options(mock_logger, request_options, mock_args) assert request_options.image_resolution == "high" @@ -70,6 +84,7 @@ def test_apply_png_options_with_resolution_standard(self): mock_args.width = "800" mock_args.height = "76" mock_args.resolution = "standard" + mock_args.language = None request_options = tsc.ImageRequestOptions() DatasourcesAndWorkbooks.apply_png_options(mock_logger, request_options, mock_args) assert request_options.image_resolution is None @@ -88,11 +103,25 @@ def test_apply_pdf_options(self): expected_layout = tsc.PDFRequestOptions.Orientation.Portrait.__str__() mock_args.pagelayout = expected_layout mock_args.pagesize = expected_page + mock_args.language = None request_options = tsc.PDFRequestOptions() DatasourcesAndWorkbooks.apply_pdf_options(mock_logger, request_options, mock_args) assert request_options.page_type == expected_page assert request_options.orientation == expected_layout + def test_apply_pdf_options_with_language(self): + language = "de" + expected_page = tsc.PDFRequestOptions.PageType.Folio.__str__() + expected_layout = tsc.PDFRequestOptions.Orientation.Portrait.__str__() + mock_args.pagelayout = expected_layout + mock_args.pagesize = expected_page + mock_args.language = language + request_options = tsc.PDFRequestOptions() + DatasourcesAndWorkbooks.apply_pdf_options(mock_logger, request_options, mock_args) + assert request_options.page_type == expected_page + assert request_options.orientation == expected_layout + assert request_options.language == language + def test_apply_options_in_url_with_size(self): request_options = tsc.ImageRequestOptions() value = ":size=800,600" @@ -126,6 +155,18 @@ def test_apply_options_in_url_with_unrecognized_parameter(self): self.assertEqual(request_options.viz_width, None) self.assertEqual(request_options.max_age, default_max_age) + def test_apply_csv_options(self): + mock_args.language = None + request_options = tsc.CSVRequestOptions() + DatasourcesAndWorkbooks.apply_csv_options(mock_logger, request_options, mock_args) + assert request_options.language == None + + def test_apply_csv_options_with_language(self): + mock_args.language = "de" + request_options = tsc.CSVRequestOptions() + DatasourcesAndWorkbooks.apply_csv_options(mock_logger, request_options, mock_args) + assert request_options.language == "de" + @mock.patch("tableauserverclient.Server") class MockedServerTests(unittest.TestCase):