Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,21 @@ def apply_options_in_url(logger, request_options: RequestOptionsType, value: str
def is_truthy(value: str):
return value.lower() in ["yes", "y", "1", "true"]

@staticmethod
def _resolve_locale(args, logger=None):
# tabcmd Classic accepts --language <code> and --country <code> to control
# export locale. tabcmd 2 exposes both as global flags but only --language
# was reaching the REST API. Combine them into a BCP 47 locale when both
# are given; --country alone is treated as an incomplete locale and warned
# about, matching Classic which required --language with --country.
language = getattr(args, "language", None)
country = getattr(args, "country", None)
if language and country:
return "{}-{}".format(language, country)
if country and not language and logger is not None:
logger.warning(_("export.locale.country_without_language").format(country))
return language

@staticmethod
def apply_png_options(logger, request_options: TSC.ImageRequestOptions, args):
# these are only used in export, not get
Expand All @@ -157,8 +172,9 @@ 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
locale = DatasourcesAndWorkbooks._resolve_locale(args, logger)
if locale:
request_options.language = locale

@staticmethod
def apply_pdf_options(logger, request_options: TSC.PDFRequestOptions, args):
Expand All @@ -170,13 +186,15 @@ def apply_pdf_options(logger, request_options: TSC.PDFRequestOptions, args):
request_options.viz_height = int(args.height)
if args.width:
request_options.viz_width = int(args.width)
if args.language:
request_options.language = args.language
locale = DatasourcesAndWorkbooks._resolve_locale(args, logger)
if locale:
request_options.language = locale

@staticmethod
def apply_csv_options(logger, request_options: TSC.CSVRequestOptions, args):
if args.language:
request_options.language = args.language
locale = DatasourcesAndWorkbooks._resolve_locale(args, logger)
if locale:
request_options.language = locale

@staticmethod
def save_to_data_file(logger, output, filename):
Expand Down
6 changes: 4 additions & 2 deletions tabcmd/execution/parent_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,10 @@ def parent_parser_with_global_options():

parser.add_argument(
"--country",
choices=["de", "en", "es", "fr", "it", "ja", "ko", "pt", "sv", "zh"],
type=str.lower, # coerce input to lowercase to act case insensitive
# ISO 3166-1 alpha-2 country code (case-insensitive). Combined with --language
# to form a locale (e.g. --language en --country GB -> "en-GB"). Left
# unconstrained on choices since Classic accepts any 2-letter country code.
type=str.upper,
help=_("export.options.country"),
)

Expand Down
1 change: 1 addition & 0 deletions tabcmd/locales/en/tabcmd_messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ encryptextracts.status=Scheduling extracts on site {0} to be encrypted...
export.errors.white_space_workbook_view=The name of the workbook or view to export cannot include spaces. Use the normalized name of the workbook or view as it appears in the URL.
export.errors.requires_workbook_view_param=The ''{0}'' command requires a <workbook>/<view> parameter, and there must be at least one slash (/) in this parameter
export.errors.requires_valid_custom_view_uuid=The URL for custom views must contain a valid custom view uuid
export.locale.country_without_language=--country {0} was ignored: --country requires --language (e.g. --language en --country GB). Using the site''s default locale.
export.options.country=If not using user''s default locale, the country abbreviation for locale (find in IANA Language Subtag Registry). Must use with --language
export.options.csv=Export data in CSV format (default)
export.options.fullpdf=Export visual views in PDF format (if workbook was published with tabs)
Expand Down
52 changes: 52 additions & 0 deletions tests/commands/test_datasources_and_workbooks_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,62 @@ def test_apply_csv_options(self):

def test_apply_csv_options_with_language(self):
mock_args.language = "de"
mock_args.country = None
request_options = tsc.CSVRequestOptions()
DatasourcesAndWorkbooks.apply_csv_options(mock_logger, request_options, mock_args)
assert request_options.language == "de"

def test_apply_csv_options_with_language_and_country(self):
# tabcmd Classic combined --language + --country into a locale (issue #109).
local_args = argparse.Namespace(language="en", country="GB")
request_options = tsc.CSVRequestOptions()
DatasourcesAndWorkbooks.apply_csv_options(mock_logger, request_options, local_args)
assert request_options.language == "en-GB"

def test_apply_png_options_with_language_and_country(self):
local_args = argparse.Namespace(language="fr", country="CA", height="600", width="800", resolution=None)
request_options = tsc.ImageRequestOptions()
DatasourcesAndWorkbooks.apply_png_options(mock_logger, request_options, local_args)
assert request_options.language == "fr-CA"

def test_apply_pdf_options_with_language_and_country(self):
local_args = argparse.Namespace(
language="pt",
country="BR",
height=800,
width=600,
pagelayout=tsc.PDFRequestOptions.Orientation.Portrait.__str__(),
pagesize=tsc.PDFRequestOptions.PageType.Folio.__str__(),
)
request_options = tsc.PDFRequestOptions()
DatasourcesAndWorkbooks.apply_pdf_options(mock_logger, request_options, local_args)
assert request_options.language == "pt-BR"

def test_resolve_locale_country_without_language_is_dropped(self):
# --country alone is not a valid locale; drop it and don't send to server.
local_args = argparse.Namespace(language=None, country="US")
assert DatasourcesAndWorkbooks._resolve_locale(local_args) is None

def test_resolve_locale_country_without_language_warns(self):
# The user's --country was silently dropped in prior revisions; make sure
# they get a warning. In tests the gettext catalog isn't loaded so `_()`
# returns the raw key -- assert on the key rather than the English text.
local_args = argparse.Namespace(language=None, country="US")
warn_logger = mock.MagicMock()
assert DatasourcesAndWorkbooks._resolve_locale(local_args, warn_logger) is None
warn_logger.warning.assert_called_once()
assert "country_without_language" in warn_logger.warning.call_args[0][0]

def test_resolve_locale_language_only(self):
local_args = argparse.Namespace(language="ja", country=None)
assert DatasourcesAndWorkbooks._resolve_locale(local_args) == "ja"

def test_resolve_locale_language_only_does_not_warn(self):
local_args = argparse.Namespace(language="ja", country=None)
warn_logger = mock.MagicMock()
DatasourcesAndWorkbooks._resolve_locale(local_args, warn_logger)
warn_logger.warning.assert_not_called()


@mock.patch("tableauserverclient.Server")
class MockedServerTests(unittest.TestCase):
Expand Down