Skip to content

Commit

Permalink
Merge pull request #294 from tableau/development
Browse files Browse the repository at this point in the history
handle case sensitivity in import users
handle spaces in filter parameters for export
fix mac packaging error
fix file handling in export command
add proxy capability
add options to extract commands
  • Loading branch information
jacalata authored Jun 13, 2024
2 parents 19aabf5 + 8a42f75 commit 73a739c
Show file tree
Hide file tree
Showing 26 changed files with 1,232 additions and 80 deletions.
35 changes: 33 additions & 2 deletions .github/workflows/package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,21 @@ jobs:
pyinstaller tabcmd-windows.spec --clean --noconfirm --distpath ./dist/windows
OUT_FILE_NAME: tabcmd.exe
ASSET_MIME: application/vnd.microsoft.portable-executable
- os: macos-13
TARGET: macos
CMD_BUILD: >
pyinstaller tabcmd-mac.spec --clean --noconfirm --distpath ./dist/macos/
BUNDLE_NAME: tabcmd.app
OUT_FILE_NAME: tabcmd.app
APP_BINARY_FILE_NAME: tabcmd
ASSET_MIME: application/zip
- os: macos-latest
TARGET: macos
CMD_BUILD: >
pyinstaller tabcmd-mac.spec --clean --noconfirm --distpath ./dist/macos
pyinstaller tabcmd-mac.spec --clean --noconfirm --distpath ./dist/macos/
BUNDLE_NAME: tabcmd_arm64.app
OUT_FILE_NAME: tabcmd.app
APP_BINARY_FILE_NAME: tabcmd
ASSET_MIME: application/zip
- os: ubuntu-latest
TARGET: ubuntu
Expand Down Expand Up @@ -64,9 +74,30 @@ jobs:
run: ${{matrix.CMD_BUILD}}

- name: Validate package for ${{matrix.TARGET}}
if: matrix.TARGET != 'macos'
run: ./dist/${{ matrix.TARGET }}/${{matrix.OUT_FILE_NAME}}

- uses: actions/upload-artifact@v4
- name: Validate package for Mac
if: matrix.TARGET == 'macos'
run: ./dist/${{ matrix.TARGET }}/${{ matrix.OUT_FILE_NAME }}/Contents/MacOS/${{ matrix.APP_BINARY_FILE_NAME }}

- name: Tar app bundle for Mac
if: matrix.TARGET == 'macos'
run: |
rm -f dist/${{ matrix.TARGET }}/${{ matrix.APP_BINARY_FILE_NAME }}
cd dist/${{ matrix.TARGET }}
tar -cvf ${{ matrix.BUNDLE_NAME }}.tar ${{ matrix.OUT_FILE_NAME }}
- name: Upload artifact
if: matrix.TARGET != 'macos'
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.OUT_FILE_NAME }}
path: ./dist/${{ matrix.TARGET }}/${{ matrix.OUT_FILE_NAME }}

- name: Upload artifact for Mac
if: matrix.TARGET == 'macos'
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.BUNDLE_NAME }}
path: ./dist/${{ matrix.TARGET }}/${{ matrix.BUNDLE_NAME }}.tar
20 changes: 8 additions & 12 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# general dev/ide files
*.log
*.vscode
*.csv
.idea
*.DS_Store

Expand All @@ -24,19 +23,20 @@ workon/
.coverage
pytest.xml

*.txt
# content
# todo probably want to add a workbook and ds in /res for getting started easily
*.pdf
*.png
*.twbx
*.hyper
*.twb
*.twbr
html
*.html
*.twbr
# *.twbx
# *.hyper
# *.twb
# *.twbr
# *.tdsx
**/credentials.py
*.txt
# exceptions
!tests/assets/

# doit
.doit.*
Expand All @@ -48,7 +48,3 @@ site-packages
tabcmd-dev
workon
test.junit.xml

# exceptions
!tests/assets/detailed_users.csv
est
Binary file removed World Indicators.tdsx
Binary file not shown.
Binary file removed WorldIndicators.tdsx
Binary file not shown.
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ packages = ["tabcmd"]
tabcmd = ["tabcmd.locales/**/*.mo"]
[tool.black]
line-length = 120
required-version = 22
target-version = ['py38', 'py39', 'py310', 'py311']
extend-exclude = '^/bin/*'
[tool.mypy]
Expand Down Expand Up @@ -40,7 +41,6 @@ classifiers = [
"Programming Language :: Python :: 3.11"
]
dependencies = [
'argparse',
"appdirs",
"doit",
"ftfy",
Expand All @@ -52,8 +52,8 @@ dependencies = [
"types-mock",
"types-requests",
"types-setuptools",
"tableauserverclient==0.25",
"urllib3>=1.24.3,<2.0",
"tableauserverclient==0.31",
"urllib3",
]
[project.optional-dependencies]
test = [
Expand Down
6 changes: 3 additions & 3 deletions tabcmd-mac.spec
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,20 @@ exe = EXE(
a.zipfiles,
a.datas,
[],
name='tabcmd.app',
name='tabcmd',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=True,
codesign_identity=None,
version='program_metadata.txt',
version='program_metadata.txt'
)

app = BUNDLE(
exe,
name = 'tabcmd-app',
name = 'tabcmd.app',
icon='res/tabcmd.icns',
bundle_identifier = None,
)
32 changes: 16 additions & 16 deletions tabcmd/commands/datasources_and_workbooks/get_url_command.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import inspect
import os

import tableauserverclient as TSC
from tableauserverclient import ServerResponseError
Expand Down Expand Up @@ -80,7 +81,7 @@ def explain_expected_url(logger, url: str, command: str):
def get_file_type_from_filename(logger, url, file_name):
logger.debug("Choosing between {}, {}".format(file_name, url))
file_name = file_name or url
logger.debug(_("get.options.file") + ": {}".format(file_name))
logger.debug(_("get.options.file") + ": {}".format(file_name)) # Name to save the file as
type_of_file = GetUrl.get_file_extension(file_name)

if not type_of_file and file_name is not None:
Expand All @@ -98,25 +99,24 @@ def get_file_type_from_filename(logger, url, file_name):
Errors.exit_with_error(logger, _("tabcmd.get.extension.not_found").format(file_name))

@staticmethod
def get_file_extension(filename):
parts = filename.split(".")
if len(parts) < 2:
return None
extension = parts[1]
def get_file_extension(path):
path_segments = os.path.split(path)
filename = path_segments[-1]
filename_segments = filename.split(".")
extension = filename_segments[-1]
extension = GetUrl.strip_query_params(extension)
return extension

@staticmethod
def strip_query_params(filename):
if filename.find("?") > 0:
filename = filename.split("?")[0]
return filename
if "?" in filename:
return filename.split("?")[0]
else:
return filename

@staticmethod
def get_name_without_possible_extension(filename):
if filename.find(".") > 0:
filename = filename.split(".")[0]
return filename
return filename.split(".")[0]

@staticmethod
def get_resource_name(url: str, logger): # workbooks/wb-name" -> "wb-name", datasource/ds-name -> ds-name
Expand Down Expand Up @@ -181,7 +181,7 @@ def generate_pdf(logger, server, args, view_url):
filename = GetUrl.filename_from_args(args.filename, view_item.name, "pdf")
DatasourcesAndWorkbooks.save_to_file(logger, view_item.pdf, filename)
except Exception as e:
Errors.exit_with_error(logger, e)
Errors.exit_with_error(logger, exception=e)

@staticmethod
def generate_png(logger, server, args, view_url):
Expand All @@ -195,7 +195,7 @@ def generate_png(logger, server, args, view_url):
filename = GetUrl.filename_from_args(args.filename, view_item.name, "png")
DatasourcesAndWorkbooks.save_to_file(logger, view_item.image, filename)
except Exception as e:
Errors.exit_with_error(logger, e)
Errors.exit_with_error(logger, exception=e)

@staticmethod
def generate_csv(logger, server, args, view_url):
Expand Down Expand Up @@ -226,7 +226,7 @@ def generate_twb(logger, server, args, file_extension, url):
server.workbooks.download(target_workbook.id, filepath=file_name_with_path, no_extract=False)
logger.info(_("export.success").format(target_workbook.name, file_name_with_ext))
except Exception as e:
Errors.exit_with_error(logger, e)
Errors.exit_with_error(logger, exception=e)

@staticmethod
def generate_tds(logger, server, args, file_extension):
Expand All @@ -243,4 +243,4 @@ def generate_tds(logger, server, args, file_extension):
server.datasources.download(target_datasource.id, filepath=file_name_with_path, no_extract=False)
logger.info(_("export.success").format(target_datasource.name, file_name_with_ext))
except Exception as e:
Errors.exit_with_error(logger, e)
Errors.exit_with_error(logger, exception=e)
24 changes: 11 additions & 13 deletions tabcmd/commands/datasources_and_workbooks/publish_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,17 @@ def run_command(args):
session = Session()
server = session.create_session(args, logger)

if args.project_name:
try:
dest_project = Server.get_project_by_name_and_parent_path(
logger, server, args.project_name, args.parent_project_path
)
project_id = dest_project.id
except Exception as exc:
logger.error(exc.__str__())
Errors.exit_with_error(logger, _("publish.errors.server_resource_not_found"), exc)
else:
project_id = ""
args.project_name = "default"
if not args.project_name:
args.project_name = "Default"
args.parent_project_path = ""
try:
dest_project = Server.get_project_by_name_and_parent_path(
logger, server, args.project_name, args.parent_project_path
)
project_id = dest_project.id
except Exception as exc:
logger.error(exc.__str__())
Errors.exit_with_error(logger, _("publish.errors.server_resource_not_found"), exc)

publish_mode = PublishCommand.get_publish_mode(args, logger)

Expand Down Expand Up @@ -82,7 +80,7 @@ def run_command(args):
# args.thumbnail_group,
connection_credentials=creds,
as_job=False,
skip_connection_check=False,
skip_connection_check=args.skip_connection_check,
)
except Exception as e:
Errors.exit_with_error(logger, exception=e)
Expand Down
1 change: 0 additions & 1 deletion tabcmd/commands/site/list_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ class ListCommand(Server):
"tabcmd_content_none": "No content found.",
}


name: str = "list"
description: str = "List content items of a specified type"

Expand Down
5 changes: 5 additions & 0 deletions tabcmd/execution/global_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,11 @@ def set_publish_args(parser):
help="Encrypt extracts in the workbook, datasource, or extract being published to the server. "
"[N/a on Tableau Cloud: extract encryption is controlled by Site Admin]",
)
parser.add_argument(
"--skip-connection-check",
action="store_true",
help="Skip connection check: do not validate the workbook/datasource connection during publishing",
)

# These two only apply for a workbook, not a datasource
thumbnails = parser.add_mutually_exclusive_group()
Expand Down
6 changes: 6 additions & 0 deletions tabcmd/execution/localize.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,13 @@ def _load_language(current_locale, domain, logger):


def _get_default_locale():
# c:\dev\tabcmd\tabcmd\execution\localize.py:85: DeprecationWarning 'locale.getdefaultlocale' is deprecated
# see test_localize for details
import logging

logging.captureWarnings(True)
current_locale, encoding = locale.getdefaultlocale()
logging.captureWarnings(False)
current_locale = _validate_lang(current_locale)
return current_locale

Expand Down
6 changes: 4 additions & 2 deletions tabcmd/execution/tabcmd_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,16 @@ def run(parser, user_input=None):
logger.debug(namespace)
if namespace.language:
set_client_locale(namespace.language, logger)

try:
func = namespace.func
# if a subcommand was identified, call the function assigned to it
# this is the functional equivalent of the call by reflection in the previous structure
# https://stackoverflow.com/questions/49038616/argparse-subparsers-with-functions
namespace.func.run_command(namespace)
except AttributeError:
parser.error("No command identified or too few arguments")
except Exception as e:
# todo: use log_stack here for better presentation
# todo: use log_stack here for better presentation?
logger.exception(e)
# if no command was given, argparse will just not create the attribute
sys.exit(2)
Expand Down
Loading

0 comments on commit 73a739c

Please sign in to comment.