From b518881b084d473051467af0327f9b41a3e498d0 Mon Sep 17 00:00:00 2001 From: Jac Date: Fri, 24 Jun 2022 13:45:04 -0700 Subject: [PATCH 01/20] configure black (#133) * configure black * add step to list dependencies for licensing --- .github/workflows/check-coverage.yml | 10 +-- .github/workflows/generate-metadata.yml | 3 + .gitignore | 4 +- bin/black.sh | 3 - bin/e2e.sh | 3 - bin/license-checker.py | 54 +++++++++++++++ contributing.md | 4 +- dodo.py | 70 +++++++++++--------- pyproject.toml | 3 + setup.py | 88 +++++++++++++------------ src/execution/_version.py | 2 +- src/execution/parent_parser.py | 2 + tabcmd.py | 2 +- 13 files changed, 157 insertions(+), 91 deletions(-) delete mode 100644 bin/black.sh delete mode 100644 bin/e2e.sh create mode 100644 bin/license-checker.py diff --git a/.github/workflows/check-coverage.yml b/.github/workflows/check-coverage.yml index 123c3e91..cb83fa36 100644 --- a/.github/workflows/check-coverage.yml +++ b/.github/workflows/check-coverage.yml @@ -26,12 +26,11 @@ jobs: - name: Install dependencies run: | + python --version python -m pip install --upgrade pip - pip install -e .[install] pip install -e .[build] pip install -e .[test] - python setup.py build - python res/versioning.py + doit version python setup.py build # https://github.com/marketplace/actions/pytest-coverage-comment @@ -42,8 +41,9 @@ jobs: uses: MishaKav/pytest-coverage-comment@main with: pytest-coverage-path: ./pytest-coverage.txt - # broken? Error: The head commit for this pull_request event is not ahead of the base commit. - # Please submit an issue on this action's GitHub repo + # TODO: check if this has been fixed + # Error: The head commit for this pull_request event is not ahead of the base commit. + # Please submit an issue on this action's GitHub repo # report-only-changed-files: true # TODO update badge on readme: diff --git a/.github/workflows/generate-metadata.yml b/.github/workflows/generate-metadata.yml index 582c9e0d..12513ace 100644 --- a/.github/workflows/generate-metadata.yml +++ b/.github/workflows/generate-metadata.yml @@ -25,6 +25,9 @@ jobs: doit version python setup.py build + - name: Generate dependencies list + run: python bin/license-checker.py + - name: Type-check run: mypy src tests diff --git a/.gitignore b/.gitignore index aa66e7fb..97a5509d 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,9 @@ __pycache__/ *.pytest_cache *.pyc *.pkl -.coverage +venv/ +tabcmd-dev/ +workon/ # code coverage outputs .coverage diff --git a/bin/black.sh b/bin/black.sh deleted file mode 100644 index 0d34ed7b..00000000 --- a/bin/black.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -set -x -black --line-length 120 tabcmd tests diff --git a/bin/e2e.sh b/bin/e2e.sh deleted file mode 100644 index 8b0142ea..00000000 --- a/bin/e2e.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -# not tested as a script -pytest -q tests\e2e\online_tests.py -r pfE diff --git a/bin/license-checker.py b/bin/license-checker.py new file mode 100644 index 00000000..b9bc00df --- /dev/null +++ b/bin/license-checker.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# https://git.soma.salesforce.com/python-at-sfdc/license_checker +# Modified version of https://stackoverflow.com/a/44090218 +from __future__ import print_function +from collections import defaultdict +import pkg_resources + +def get_pkg_license(pkg): + try: + lines = pkg.get_metadata_lines('METADATA') + except: + lines = pkg.get_metadata_lines('PKG-INFO') + + for line in lines: + if line.startswith('License:'): + return line[9:] + return '(Licence not found)' + exit() + +def print_table(table): + column_index_to_max_width = defaultdict(int) + for row_index, row in enumerate(table): + for cell_index, cell in enumerate(row): + cur_max = column_index_to_max_width[cell_index] + cell_width = len(cell) + if cell_width > cur_max: + column_index_to_max_width[cell_index] = cell_width + for row_index, row in enumerate(table): + line = '' + for cell_index, cell in enumerate(row): + cell_width = column_index_to_max_width[cell_index] + line += cell.ljust(cell_width) + line += " - " + line = line.ljust(25) + line = line.rstrip(" -") + print(line) + if row_index == 0: + print("-" * len(line)) + + +def get_directory(package_name): + return pkg_resources.working_set.find(pkg_resources.Requirement.parse(package_name)).location + + +def print_packages_and_licenses(): + table = [] + table.append(['Package', 'License', 'Location']) + for pkg in sorted(pkg_resources.working_set): + table.append([str(pkg).rjust(25)[:25], get_pkg_license(pkg)[:25], pkg.location]) + print_table(table) + + +if __name__ == "__main__": + print_packages_and_licenses() diff --git a/contributing.md b/contributing.md index ae8dd0ce..c7c77036 100644 --- a/contributing.md +++ b/contributing.md @@ -30,9 +30,9 @@ _(note that running mypy and black is required for code being submitted to the r > pytest - run tests against a live server > python -m tabcmd login {your server info here} -> bin/e2e.sh +> pytest -q tests\e2e\online_tests.py -r pfE - autoformat your code with black (https://pypi.org/project/black/) -> bin/black.sh +> black . - check types > mypy src tests - do test coverage calculation (https://coverage.readthedocs.io/en/6.3.2) diff --git a/dodo.py b/dodo.py index ef2b8b42..3e4272a5 100644 --- a/dodo.py +++ b/dodo.py @@ -4,10 +4,7 @@ import ftfy import setuptools_scm -LOCALES = [ - "en", "de", "es", "fr", "ga", "it", "pt", - "sv", "ja", "ko", - "zh"] +LOCALES = ["en", "de", "es", "fr", "ga", "it", "pt", "sv", "ja", "ko", "zh"] """ https://pydoit.org/ @@ -46,8 +43,8 @@ def process_locales(): outfile.write(ftfy.fixes.decode_escapes(data)) return { - 'actions': [process_locales], - 'verbosity': 2, + "actions": [process_locales], + "verbosity": 2, } @@ -65,40 +62,48 @@ def task_po(): - 3.x, from pip install translate-toolkit: it copies key->comment, value-> msgid, ""->msgstr which is not at all what we want """ + def process_locales(): for current_locale in LOCALES: LOC_PATH = "src/locales/" + current_locale - for file in glob.glob(LOC_PATH+"/*.properties"): + for file in glob.glob(LOC_PATH + "/*.properties"): basename = os.path.basename(file).split(".")[0] print("processing", basename) - result = subprocess.run(["python", - "bin/i18n/prop2po.py", - "--encoding", "utf-8", # for the .po header - "--language", current_locale, # for the .po header - LOC_PATH + "/"+basename+".properties", - LOC_PATH + "/LC_MESSAGES/"+basename+".po"]) + result = subprocess.run( + [ + "python", + "bin/i18n/prop2po.py", + "--encoding", + "utf-8", # for the .po header + "--language", + current_locale, # for the .po header + LOC_PATH + "/" + basename + ".properties", + LOC_PATH + "/LC_MESSAGES/" + basename + ".po", + ] + ) print("\n", result) # print("stdout:", result.stdout) if not result.returncode == 0: print("stderr:", result.stderr) + return { - 'actions': [process_locales], - 'verbosity': 2, + "actions": [process_locales], + "verbosity": 2, } def task_clean_all(): - """For all languages: removes all generated artifacts (.po, .mo) which source from properties files. """ + """For all languages: removes all generated artifacts (.po, .mo) which source from properties files.""" def process_locales(): for current_locale in LOCALES: LOC_PATH = "src/locales/" + current_locale - for file in glob.glob(LOC_PATH+"/*.properties"): + for file in glob.glob(LOC_PATH + "/*.properties"): basename = os.path.basename(file).split(".")[0] - print("deleting",basename + ".*") + print("deleting", basename + ".*") try: - os.remove(LOC_PATH + "/LC_MESSAGES/"+basename+".po") + os.remove(LOC_PATH + "/LC_MESSAGES/" + basename + ".po") except OSError: pass try: @@ -111,10 +116,9 @@ def process_locales(): except OSError: pass - return { - 'actions': [process_locales], - 'verbosity': 2, + "actions": [process_locales], + "verbosity": 2, } @@ -128,7 +132,7 @@ def process_locales(): LOC_PATH = "src/locales/" + current_locale + "/LC_MESSAGES" - with open(LOC_PATH + "/tabcmd.po", 'w+', encoding="utf-8") as outfile: + with open(LOC_PATH + "/tabcmd.po", "w+", encoding="utf-8") as outfile: for file in glob.glob(LOC_PATH + "/*.po"): if file.endswith("tabcmd.po"): pass @@ -139,8 +143,8 @@ def process_locales(): outfile.write("\n") return { - 'actions': [process_locales], - 'verbosity': 2, + "actions": [process_locales], + "verbosity": 2, } @@ -164,14 +168,15 @@ def process_locales(): print("stderr:", result.stderr) return { - 'actions': [process_locales], - 'verbosity': 2, + "actions": [process_locales], + "verbosity": 2, } def task_version(): - """ Generates a metadata info file with current version to be bundled by pyinstaller""" + """Generates a metadata info file with current version to be bundled by pyinstaller""" + def write_for_pyinstaller(): import pyinstaller_versionfile import os @@ -183,12 +188,13 @@ def write_for_pyinstaller(): output_file = os.path.join(".", "program_metadata.txt") input_file = os.path.join("res", "metadata.yml") pyinstaller_versionfile.create_versionfile_from_input_file( - output_file, input_file, + output_file, + input_file, # optional, can be set to overwrite version information (equivalent to --version when using the CLI) - version=numeric_version + version=numeric_version, ) return { - 'actions': [write_for_pyinstaller], - 'verbosity': 2, + "actions": [write_for_pyinstaller], + "verbosity": 2, } diff --git a/pyproject.toml b/pyproject.toml index 4b7830a6..485decd9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,3 +2,6 @@ requires = ["build", "setuptools>=62", "wheel", "setuptools_scm>=6.2"] [tool.setuptools_scm] "local_scheme"= "no-local-version" # require pypi supported versions always +[tool.black] +line-length = 120 +extend-exclude = '^/bin/*' diff --git a/setup.py b/setup.py index 9955108f..57466d7d 100644 --- a/setup.py +++ b/setup.py @@ -1,61 +1,63 @@ from setuptools import setup, find_packages setup( - name='tabcmd', - author='Tableau', - author_email='github@tableau.com', - description='A command line client for working with Tableau Server.', - long_description='A command line client for working with Tableau Server.', - license='MIT', - url='https://github.com/tableau/tabcmd', - - python_requires='>=3.7', + name="tabcmd", + author="Tableau", + author_email="github@tableau.com", + description="A command line client for working with Tableau Server.", + long_description="A command line client for working with Tableau Server.", + license="MIT", + url="https://github.com/tableau/tabcmd", + python_requires=">=3.7", packages=find_packages(), - package_data={'tabcmd': ['src.locales/**/*.mo']}, + package_data={"tabcmd": ["src.locales/**/*.mo"]}, include_package_data=True, - entry_points={ - 'console_scripts': [ - 'tabcmd = src.tabcmd:main' - ] - }, + entry_points={"console_scripts": ["tabcmd = src.tabcmd:main"]}, setup_requires=[ # copy of pyproject.toml for back compat - "setuptools>=62", "wheel", "setuptools_scm>=6.2" + "build", + "setuptools>=62", + "setuptools_scm>=6.2", + "wheel", ], install_requires=[ - 'polling2', - 'requests>=2.11,<3.0', - 'tableauserverclient>=0.19', - 'urllib3>=1.24.3,<2.0', + "polling2", + "requests>=2.11,<3.0", + "tableauserverclient>=0.19", + "urllib3>=1.24.3,<2.0", ], extras_require={ - 'localize': [ - 'doit', - 'ftfy', + "localize": [ + "doit", + "ftfy", ], - 'build': [ - 'appdirs', - 'black', - 'doit', - 'ftfy', - 'mypy', - 'pyinstaller_versionfile', - 'setuptools>=62', - 'setuptools_scm', - 'types-appdirs', - 'types-mock', - 'types-requests', + "build": [ + "appdirs", + "black", + "doit", + "ftfy", + "mypy", + "pyinstaller_versionfile", + "setuptools>=62", + "setuptools_scm", + "types-appdirs", + "types-mock", + "types-requests", + "types-setuptools", ], - 'package': [ - 'pyinstaller>=5.1', - 'pyinstaller-versionfile', + "package": [ + "pyinstaller>=5.1", + "pyinstaller-versionfile", ], - 'test': [ - 'mock', - 'pytest', 'pytest-cov', 'pytest-order', 'pytest-runner', - 'requests-mock>=1.0,<2.0', + "test": [ + "mock", + "pytest", + "pytest-cov", + "pytest-order", + "pytest-runner", + "requests-mock>=1.0,<2.0", ], }, - test_suite='tests', + test_suite="tests", zip_safe=False, ) diff --git a/src/execution/_version.py b/src/execution/_version.py index fcf69b5e..1d50c3ef 100644 --- a/src/execution/_version.py +++ b/src/execution/_version.py @@ -1,5 +1,5 @@ # coding: utf-8 # file generated by setuptools_scm # don't change, don't track in version control -version = '38' +version = "38" version_tuple = (38,) diff --git a/src/execution/parent_parser.py b/src/execution/parent_parser.py index b04b6168..d7018031 100644 --- a/src/execution/parent_parser.py +++ b/src/execution/parent_parser.py @@ -1,8 +1,10 @@ import argparse from .localize import _ + # when we drop python 3.8, this could be replaced with this lighter weight option # from importlib.metadata import version, PackageNotFoundError from pkg_resources import get_distribution, DistributionNotFound + try: version = get_distribution("tabcmd").version except DistributionNotFound: diff --git a/tabcmd.py b/tabcmd.py index 981ca265..1c987d62 100644 --- a/tabcmd.py +++ b/tabcmd.py @@ -1,4 +1,4 @@ from src import tabcmd -if __name__ == '__main__': +if __name__ == "__main__": tabcmd.main() From 648d7b26ddf6dc7192ed5de0f607998a76d46d7d Mon Sep 17 00:00:00 2001 From: Jac Date: Fri, 8 Jul 2022 13:04:58 -0700 Subject: [PATCH 02/20] Jac/args (#134) * Defect 1427376: [Tabcmd WAM] --save-db-password flag thinks it should get an argument -updated several other publishing arguments that had the same problem - change argument from "token" to "token-value" so it is not a substring of "token-name", remove the 2-letter short arguments * fix reencrypt_extracts help string * Hand format a nice help output --- src/commands/auth/session.py | 16 ++--- .../extracts/reencrypt_extracts_command.py | 2 +- src/commands/help/help_command.py | 67 ++++++++++++++++--- src/commands/server.py | 4 +- src/execution/global_options.py | 25 ++++--- src/execution/logger_config.py | 2 +- src/execution/parent_parser.py | 25 +++---- src/execution/tabcmd_controller.py | 3 +- tests/commands/test_run_commands.py | 1 + tests/commands/test_session.py | 16 ++--- tests/parsers/test_login_parser.py | 4 +- tests/parsers/test_parser_publish.py | 13 ++++ tests/parsers/test_parser_publish_samples.py | 2 +- tests/parsers/test_parser_refresh_extracts.py | 3 +- 14 files changed, 128 insertions(+), 55 deletions(-) diff --git a/src/commands/auth/session.py b/src/commands/auth/session.py index 812d294c..c3fc2687 100644 --- a/src/commands/auth/session.py +++ b/src/commands/auth/session.py @@ -28,7 +28,7 @@ def __init__(self): self.user_id = None self.auth_token = None self.token_name = None - self.token = None + self.token_value = None self.password_file = None self.site_name = None # The site name, e.g 'alpodev' self.site_id = None # The site id, e.g 'abcd-1234-1234-1244-1234' @@ -63,7 +63,7 @@ def _update_session_data(self, args): self.logging_level = args.logging_level or self.logging_level self.password_file = args.password_file self.token_name = args.token_name or self.token_name - self.token = args.token or self.token + self.token_value = args.token_value or self.token_value self.no_prompt = args.no_prompt or self.no_prompt self.certificate = args.certificate or self.certificate @@ -107,8 +107,8 @@ def _create_new_credential(self, password, credential_type): Errors.exit_with_error(self.logger, "Couldn't find credentials") def _create_new_token_credential(self): - if self.token: - token = self.token + if self.token_value: + token = self.token_value elif self.password_file: token = Session._read_password_from_file(self.password_file) elif self._allow_prompt(): @@ -220,7 +220,7 @@ def create_session(self, args): credentials = self._create_new_credential(args.password, Session.PASSWORD_CRED_TYPE) else: credentials = self._create_new_credential(args.password, Session.TOKEN_CRED_TYPE) - elif args.token: + elif args.token_value: self._end_session() credentials = self._create_new_token_credential() else: # no login arguments given - look for saved info @@ -262,7 +262,7 @@ def _clear_data(self): self.user_id = None self.auth_token = None self.token_name = None - self.token = None + self.token_value = None self.site_name = None self.site_id = None self.server = None @@ -300,7 +300,7 @@ def _read_from_json(self): self.username = auth["username"] self.user_id = auth["user_id"] self.token_name = auth["personal_access_token_name"] - self.token = auth["personal_access_token"] + self.token_value = auth["personal_access_token"] self.last_login_using = auth["last_login_using"] self.password_file = auth["password_file"] self.no_prompt = auth["no_prompt"] @@ -341,7 +341,7 @@ def _serialize_for_save(self): "site_name": self.site_name, "site_id": self.site_id, "personal_access_token_name": self.token_name, - "personal_access_token": self.token, + "personal_access_token": self.token_value, "last_login_using": self.last_login_using, "password_file": self.password_file, "no_prompt": self.no_prompt, diff --git a/src/commands/extracts/reencrypt_extracts_command.py b/src/commands/extracts/reencrypt_extracts_command.py index 98ea8c34..3071757b 100644 --- a/src/commands/extracts/reencrypt_extracts_command.py +++ b/src/commands/extracts/reencrypt_extracts_command.py @@ -14,7 +14,7 @@ class ReencryptExtracts(Server): """ name: str = "reencryptextracts" - description: str = _("reencryptextracts.short_description=") + description: str = _("reencryptextracts.short_description") @staticmethod def define_args(reencrypt_extract_parser): diff --git a/src/commands/help/help_command.py b/src/commands/help/help_command.py index 30aacc3b..2436cd21 100644 --- a/src/commands/help/help_command.py +++ b/src/commands/help/help_command.py @@ -1,4 +1,8 @@ -import sys +import argparse +from typing import Any, List + +from src.execution.localize import _ +from src.execution.logger_config import log class HelpCommand: @@ -11,15 +15,60 @@ class HelpCommand: @staticmethod def define_args(parser): - # takes no args - pass + parser.add_argument("help_option", nargs="?") @staticmethod - def run_command(args): - description = ( + def run_command(args: argparse.Namespace): + + # whaddya mean, '__class__' is not defined ??!?!!? + logger = log(__class__.__name__, args.logging_level) # type: ignore[name-defined] + logger.debug(_("tabcmd.launching")) + + # delayed import, TODO fix cyclic imports + from src.execution.map_of_commands import CommandsMap + + all_commands: List[Any] = CommandsMap.commands_hash_map + + description: str = ( "tabcmd - Tableau Server Command Line Utility 2.0 \n \n" - "tabcmd help -- Help for tabcmd commands \n" - "tabcmd help -- Show Help for a specific command\n" - "tabcmd help commands -- List all available commands\n\n" + "tabcmd help -- List all available commands and global options \n" + "tabcmd help -- Show Help for a specific command\n\n" ) - sys.stdout.write(description) + + if args.help_option: + + if args.help_option in map(lambda command: command.name, all_commands): + command_objects = filter(lambda command: command.name == args.help_option, all_commands) + cli_cmd = list(command_objects)[0] + logger.info(cli_cmd.name.ljust(25) + cli_cmd.description + "\n") + command_parser = argparse.ArgumentParser(parents=[]) + cli_cmd.define_args(command_parser) + + positionals = [] + optionals = [] + for option in command_parser._actions: + if option.option_strings: + optionals.append(option) + else: + positionals.append(option) + + if positionals: + logger.info("Required arguments") + for option in positionals: + logger.info("{0} {1}{2}{3}".format(option.dest.ljust(25), "{", option.help, "}")) + if optionals: + logger.info("\nOptional arguments") + for option in optionals: + logger.info("{0} {1} ".format(option.option_strings, option.help)) + + logger.info("\nUsage") + usage = cli_cmd.name + " " + for opt in positionals: + usage = usage + opt.dest + if len(positionals) < len(command_parser._actions): + usage = usage + " [--optional arguments]" + logger.info(usage) + + else: + for cmd in all_commands: + logger.info(cmd.name + ": " + cmd.description) diff --git a/src/commands/server.py b/src/commands/server.py index 9ba01b62..3f171234 100644 --- a/src/commands/server.py +++ b/src/commands/server.py @@ -1,8 +1,8 @@ import os - import tableauserverclient as TSC from src.commands.constants import Errors +from src.execution.localize import _ class Server: @@ -74,7 +74,7 @@ def get_site_for_command_or_throw(logger, server, args): Errors.exit_with_error(logger, exception=e) else: logger.debug("Use logged in site") - # site_item = server.sites.get_by_id(server.site_id) + site_item = server.sites.get_by_id(server.site_id) if not site_item: raise ResourceWarning("Could not get site from server") return site_item diff --git a/src/execution/global_options.py b/src/execution/global_options.py index 9d0b834b..8c259028 100644 --- a/src/execution/global_options.py +++ b/src/execution/global_options.py @@ -65,6 +65,7 @@ def set_no_wait_option(parser): return parser +# TODO make this lower case? def set_role_arg(parser): parser.add_argument( "-r", @@ -88,7 +89,9 @@ def set_role_arg(parser): def set_silent_option(parser): - parser.add_argument("--silent-progress", help="Do not display progress messages for the command.") + parser.add_argument( + "--silent-progress", action="store_true", help="Do not display progress messages for the command." + ) return parser @@ -301,6 +304,7 @@ def set_publish_args(parser): ) parser.add_argument( "--save-db-password", + action="store_true", help="Stores the provided database password on the server.", ) parser.add_argument( @@ -309,25 +313,29 @@ def set_publish_args(parser): help="When a workbook with tabbed views is published, each sheet becomes a tab that viewers can use to \ navigate through the workbook", ) - parser.add_argument("--replace", help="Use the extract file to replace the existing data source.") - parser.add_argument("--disable-uploader", help="Disable the incremental file uploader.") + parser.add_argument( + "--replace", action="store_true", help="Use the extract file to replace the existing data source." + ) + parser.add_argument("--disable-uploader", action="store_true", help="Disable the incremental file uploader.") parser.add_argument("--restart", help="Restart the file upload.") parser.add_argument( "--encrypt-extracts", + action="store_true", help="Encrypt extracts in the workbook, datasource, or extract being published to the server", ) parser.add_argument("--oauth-username", help="The email address of a preconfigured OAuth connection") - parser.add_argument("--save-oauth") - parser.add_argument("--thumbnail-username") - parser.add_argument("--thumbnail-group") # not implemented in the REST API + parser.add_argument("--save-oauth", action="store_true", help="Save embedded OAuth credentials in the datasource") + parser.add_argument("--thumbnail-username", help="Not yet implemented") + parser.add_argument("--thumbnail-group", help="Not yet implemented") # not implemented in the REST API # refresh-extracts def set_incremental_options(parser): sync_group = parser.add_mutually_exclusive_group() - sync_group.add_argument("--incremental", help="Runs the incremental refresh operation.") + sync_group.add_argument("--incremental", action="store_true", help="Runs the incremental refresh operation.") sync_group.add_argument( "--synchronous", + action="store_true", help="Adds the full refresh operation to the queue used by the Backgrounder process, to be run as soon as a \ Backgrounder process is available.", ) @@ -368,7 +376,7 @@ def set_domain_arguments(parser): def set_target_users_arg(parser): target_users_group = parser.add_mutually_exclusive_group() target_users_group.add_argument("--target-username", help="Clears sub value for the specified individual user.") - target_users_group.add_argument("--all", help="Clears sub values for all users.") + target_users_group.add_argument("--all", action="store_true", help="Clears sub values for all users.") return parser @@ -383,6 +391,7 @@ def set_update_group_args(parser): parser.add_argument( "--grant-license-mode", choices=["on-login", "on-sync"], + type=str.lower, help="Specifies whether a role should be granted on sign in. ", ) parser.add_argument( diff --git a/src/execution/logger_config.py b/src/execution/logger_config.py index eee39ba1..1837e951 100644 --- a/src/execution/logger_config.py +++ b/src/execution/logger_config.py @@ -7,7 +7,7 @@ logging.ERROR: "ERROR: %(name)-10s: %(lineno)d: %(message)s", logging.WARN: "WARN: %(message)s", logging.DEBUG: "DEBUG: %(name)-10s: %(lineno)d: %(message)-10s", - logging.INFO: "INFO: %(message)s", + logging.INFO: "%(message)s", "TRACE": "TRACE: %(asctime)-12s %(name)-10s: %(lineno)d: %(message)-10s", "DEFAULT": "%(message)s", } diff --git a/src/execution/parent_parser.py b/src/execution/parent_parser.py index d7018031..82261f28 100644 --- a/src/execution/parent_parser.py +++ b/src/execution/parent_parser.py @@ -66,8 +66,8 @@ def parent_parser_with_global_options(self): "-l", "--logging-level", choices=["DEBUG", "INFO", "ERROR"], + type=str.upper, # coerce input to uppercase to act case insensitive default="info", - metavar="", help="Use the specified logging level. The default level is INFO.", ) @@ -75,7 +75,6 @@ def parent_parser_with_global_options(self): auth_options = parser.add_mutually_exclusive_group() auth_options.add_argument( - "-tn", "--token-name", default=None, metavar="", @@ -88,8 +87,7 @@ def parent_parser_with_global_options(self): secret_values = parser.add_mutually_exclusive_group() secret_values.add_argument( - "-to", - "--token", + "--token-value", default=None, metavar="", help="Use the specified Tableau Server Personal Access Token. Requires --token-name to be set.", @@ -129,14 +127,6 @@ def parent_parser_with_global_options(self): help=_("session.options.timeout"), ) - parser.add_argument( - "-v", - "--version", - action="version", - version="tabcmd.exe - Tableau Server Command Line Utility v" + version + "\n \n", - help="Show version information and exit.", - ) - # TODO get the list of choices dynamically? parser.add_argument( "--language", @@ -150,4 +140,15 @@ def parent_parser_with_global_options(self): choices=["de", "en", "es", "fr", "it", "ja", "ko", "pt", "sv", "zh"], help=_("export.options.country"), ) + + # -h goes to argparse default help + + parser.add_argument( + "-v", + "--version", + action="version", + version="Tableau Server Command Line Utility v" + version + "\n \n", + help="Show version information and exit.", + ) + return parser diff --git a/src/execution/tabcmd_controller.py b/src/execution/tabcmd_controller.py index 7b8b2346..f54f6b02 100644 --- a/src/execution/tabcmd_controller.py +++ b/src/execution/tabcmd_controller.py @@ -1,4 +1,5 @@ import logging +import sys from .localize import set_client_locale from .map_of_commands import * @@ -26,7 +27,7 @@ def run(parser, user_input=None): namespace = parser.parse_args(user_input) logger = log(__name__, namespace.logging_level or logging.INFO) - logger.debug(namespace) + # logger.debug(namespace) if namespace.language: set_client_locale(namespace.language, logger) diff --git a/tests/commands/test_run_commands.py b/tests/commands/test_run_commands.py index 10915a3e..67c032c3 100644 --- a/tests/commands/test_run_commands.py +++ b/tests/commands/test_run_commands.py @@ -243,6 +243,7 @@ def test_delete_group(self, mock_session, mock_server): # help def test_help(self, mock_session, mock_server): RunCommandsTest._set_up_session(mock_session, mock_server) + mock_args.help_option = "boo" help_command.HelpCommand.run_command(mock_args) mock_session.assert_not_called() diff --git a/tests/commands/test_session.py b/tests/commands/test_session.py index 76580e03..64ea26a9 100644 --- a/tests/commands/test_session.py +++ b/tests/commands/test_session.py @@ -13,7 +13,7 @@ password_file=None, server=None, token_name=None, - token=None, + token_value=None, logging_level=None, no_certcheck=None, no_prompt=False, @@ -117,9 +117,9 @@ def test__create_new_username_credential_fails_no_args(self, mock_pass): # These two already have a username saved and pass in a password as argument def test__create_new_token_credential_succeeds_new_token(self, mock_pass): test_args = Namespace(**vars(args_to_mock)) - test_args.token = "gibberish" + test_args.token_value = "gibberish" active_session = Session() - assert active_session.token is None, active_session.token + assert active_session.token_value is None, active_session.token_value active_session.token_name = "readable" auth = active_session._create_new_token_credential() assert auth is not None @@ -136,7 +136,7 @@ def test__create_new_username_credential_succeeds_new_password(self, mock_pass): # this one has a token saved def test__create_new_token_credential_succeeds_from_self(self, mock_pass): active_session = Session() - active_session.token = "gibberish2" + active_session.token_value = "gibberish2" active_session.token_name = "readable2" auth = active_session._create_new_token_credential() assert mock_pass.is_not_called() @@ -157,7 +157,7 @@ def test__create_new_username_credential_succeeds_from_self(self, mock_pass): def test__create_new_token_credential_succeeds_from_args(self, mock_pass): test_args = Namespace(**vars(args_to_mock)) - test_args.token = "gibberish" + test_args.token_value = "gibberish" test_args.token_name = "readable" active_session = Session() active_session._update_session_data(test_args) @@ -220,13 +220,13 @@ def test_create_session_first_time_with_token_arg( assert mock_path.exists("anything") is False test_args = Namespace(**vars(args_to_mock)) test_args.token_name = "tn" - test_args.token = "foo" + test_args.token_value = "foo" new_session = Session() auth = new_session.create_session(test_args) assert auth is not None, auth assert auth.auth_token is not None, auth.auth_token assert auth.auth_token.name is not None, auth.auth_token - assert new_session.token == "foo", new_session.token + assert new_session.token_value == "foo", new_session.token_value assert new_session.token_name == "tn", new_session @mock.patch("tableauserverclient.Server") @@ -302,7 +302,7 @@ def test_create_session_with_active_session_saved( _set_mocks_for_json_file_exists(mock_path, True) _set_mocks_for_json_file_saved_username(mock_json_load, "auth_token", None) test_args = Namespace(**vars(args_to_mock)) - test_args.token = "tn" + test_args.token_value = "tn" test_args.token_name = "tnnnn" test_args.no_prompt = False new_session = Session() diff --git a/tests/parsers/test_login_parser.py b/tests/parsers/test_login_parser.py index 0c0f0413..7369ed37 100644 --- a/tests/parsers/test_login_parser.py +++ b/tests/parsers/test_login_parser.py @@ -93,10 +93,10 @@ def test_login_parser_test_username_password(self): assert args.password == "pw", args def test_login_parser_test_token(self): - mock_args = [commandname, "--token", "token", "--token-name", "tn"] + mock_args = [commandname, "--token-value", "token", "--token-name", "tn"] args = self.parser_under_test.parse_args(mock_args) assert args.token_name == "tn", args - assert args.token == "token", args + assert args.token_value == "token", args def test_login_token_and_username(self): mock_args = [commandname, "--token-name", "to", "--username", "u"] diff --git a/tests/parsers/test_parser_publish.py b/tests/parsers/test_parser_publish.py index f93dc748..356bfe60 100644 --- a/tests/parsers/test_parser_publish.py +++ b/tests/parsers/test_parser_publish.py @@ -25,3 +25,16 @@ def test_publish_parser_tabbed(self): mock_args = [commandname, "filename.twbx", "--tabbed"] args = self.parser_under_test.parse_args(mock_args) assert args.tabbed is True, args + + def test_publish_parser_save_password(self): + mock_args = [ + commandname, + "filename.twbx", + "--db-username", + "user", + "--db-password", + "somepassword", + "--save-db-password", + ] + args = self.parser_under_test.parse_args(mock_args) + assert args.save_db_password is True, args diff --git a/tests/parsers/test_parser_publish_samples.py b/tests/parsers/test_parser_publish_samples.py index b8500e8f..52e04c6d 100644 --- a/tests/parsers/test_parser_publish_samples.py +++ b/tests/parsers/test_parser_publish_samples.py @@ -6,7 +6,7 @@ commandname = "publishsamples" -class PublishParserParserTest(unittest.TestCase): +class PublishSamplesParserTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, PublishSamplesCommand) diff --git a/tests/parsers/test_parser_refresh_extracts.py b/tests/parsers/test_parser_refresh_extracts.py index ed2c035d..942182d3 100644 --- a/tests/parsers/test_parser_refresh_extracts.py +++ b/tests/parsers/test_parser_refresh_extracts.py @@ -22,13 +22,12 @@ def test_refresh_extract_parser_optional_arguments(self): "--datasource", "hello", "--incremental", - "True", "--removecalculations", "--project", "testproject", ] args = self.parser_under_test.parse_args(mock_args) - assert args.incremental == "True", args + assert args.incremental, args def test_refresh_extract_parser_missing_all_args(self): mock_args = [commandname] From 9f21a456a72617b4ec53ce18bc28aa4b821c7d5a Mon Sep 17 00:00:00 2001 From: Brian Cantoni Date: Fri, 8 Jul 2022 13:06:11 -0700 Subject: [PATCH 03/20] Fix exit_with_error handling (#137) * Fix exit_with_error handling --- src/commands/datasources_and_workbooks/get_url_command.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/commands/datasources_and_workbooks/get_url_command.py b/src/commands/datasources_and_workbooks/get_url_command.py index 924c1a2d..52989ad5 100644 --- a/src/commands/datasources_and_workbooks/get_url_command.py +++ b/src/commands/datasources_and_workbooks/get_url_command.py @@ -144,7 +144,7 @@ def generate_pdf(logger, server, args): f.write(view_item.pdf) logger.info(_("export.success").format(view_item.name, filename)) except TSC.ServerResponseError as e: - GetUrl.exit_with_error(logger, _("publish.errors.unexpected_server_response"), e) + Errors.exit_with_error(logger, _("publish.errors.unexpected_server_response"), e) @staticmethod def generate_png(logger, server, args): @@ -159,7 +159,7 @@ def generate_png(logger, server, args): f.write(view_item.png) logger.info(_("export.success").format(view_item.name, filename)) except TSC.ServerResponseError as e: - GetUrl.exit_with_error(logger, _("publish.errors.unexpected_server_response"), e) + Errors.exit_with_error(logger, _("publish.errors.unexpected_server_response"), e) @staticmethod def generate_csv(logger, server, args): @@ -174,7 +174,7 @@ def generate_csv(logger, server, args): f.write(view_item.csv) logger.info(_("export.success"), views_from_list.name, formatted_file_name) except TSC.ServerResponseError as e: - GetUrl.exit_with_error(logger, _("publish.errors.unexpected_server_response"), e) + Errors.exit_with_error(logger, _("publish.errors.unexpected_server_response"), e) except Exception as e: Errors.exit_with_error(logger, exception=e) @@ -188,4 +188,4 @@ def generate_twb(logger, server, args, file_extension): 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_path)) except TSC.ServerResponseError as e: - GetUrl.exit_with_error(logger, _("publish.errors.unexpected_server_response"), e) + Errors.exit_with_error(logger, _("publish.errors.unexpected_server_response"), e) From d493cddfc662593b050f998068c94833d48f110e Mon Sep 17 00:00:00 2001 From: Jac Date: Sun, 10 Jul 2022 13:16:10 -0700 Subject: [PATCH 04/20] TFS 1428581 publish project (#139) --- .gitignore | 6 ++++++ src/commands/datasources_and_workbooks/publish_command.py | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 97a5509d..d586d56e 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,9 @@ html # doit .doit.* +# venv +site-packages + +# local +tabcmd-dev +workon diff --git a/src/commands/datasources_and_workbooks/publish_command.py b/src/commands/datasources_and_workbooks/publish_command.py index 5ad5b87c..b360b775 100644 --- a/src/commands/datasources_and_workbooks/publish_command.py +++ b/src/commands/datasources_and_workbooks/publish_command.py @@ -38,9 +38,10 @@ def run_command(args): if args.project_name: try: - project_id = Server.get_project_by_name_and_parent_path( + 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: Errors.exit_with_error(logger, _("publish.errors.server_resource_not_found"), exc) else: @@ -49,6 +50,7 @@ def run_command(args): args.parent_project_path = "" publish_mode = PublishCommand.get_publish_mode(args) + logger.info("Publishing as " + publish_mode) source = PublishCommand.get_filename_extension_if_tableau_type(logger, args.filename) logger.info(_("publish.status").format(args.filename)) From e099732ffa4fb0771a3682cfa49325c3a011148e Mon Sep 17 00:00:00 2001 From: Jac Date: Tue, 12 Jul 2022 13:57:41 -0700 Subject: [PATCH 05/20] Jac/groups (#140) * Defect 1426885: [Tabcmd WAM] addusers and removeusers error * fix deletegroup * update unlocalized strings --- src/commands/auth/session.py | 13 ++++++++++--- .../runschedule_command.py | 3 --- src/commands/group/create_group_command.py | 5 +++-- src/commands/group/delete_group_command.py | 5 +++-- src/commands/server.py | 13 ++++++++++--- src/commands/user/user_data.py | 9 +++++---- tests/commands/test_run_commands.py | 2 +- 7 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/commands/auth/session.py b/src/commands/auth/session.py index c3fc2687..3deecb49 100644 --- a/src/commands/auth/session.py +++ b/src/commands/auth/session.py @@ -4,6 +4,7 @@ import requests import tableauserverclient as TSC +import tableauserverclient.server.endpoint.exceptions from urllib3.exceptions import InsecureRequestWarning from src.commands.constants import Errors @@ -132,7 +133,10 @@ def _set_connection_options(self): if self.no_certcheck: http_options = {"verify": False} requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) - tableau_server = TSC.Server(self.server_url, use_server_version=True, http_options=http_options) + try: + tableau_server = TSC.Server(self.server_url, use_server_version=True, http_options=http_options) + except Exception as e: + Errors.exit_with_error(self.logger, "Failed to connect to server", e) return tableau_server @@ -141,7 +145,10 @@ def _create_new_connection(self): self.tableau_server = self._set_connection_options() self._print_server_info() self.logger.info(_("session.connecting")) - self.tableau_server.use_server_version() # this will attempt to contact the server + try: + self.tableau_server.use_server_version() # this will attempt to contact the server + except Exception as e: + Errors.exit_with_error(self.logger, "Failed to connect to server", e) def _read_existing_state(self): if self._check_json(): @@ -175,7 +182,7 @@ def _sign_in(self, tableau_auth): self.logger.debug(_("listsites.output").format("", self.username or self.token_name, self.site_name)) try: self.tableau_server.auth.sign_in(tableau_auth) # it's the same call for token or user-pass - except TSC.ServerResponseError as e: + except Exception as e: Errors.exit_with_error(self.logger, exception=e) try: self.site_id = self.tableau_server.site_id diff --git a/src/commands/datasources_and_workbooks/runschedule_command.py b/src/commands/datasources_and_workbooks/runschedule_command.py index 4a1a8911..0b58e396 100644 --- a/src/commands/datasources_and_workbooks/runschedule_command.py +++ b/src/commands/datasources_and_workbooks/runschedule_command.py @@ -23,10 +23,7 @@ def run_command(args): logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args) - logger.info(_("export.status").format(args.schedule)) schedule = DatasourcesAndWorkbooks.get_items_by_name(logger, server.schedules, args.schedule)[0] - if not schedule: - Errors.exit_with_error(logger, _("publish.errors.server_resource_not_found")) logger.info(_("runschedule.status")) Errors.exit_with_error(logger, "Not yet implemented") diff --git a/src/commands/group/create_group_command.py b/src/commands/group/create_group_command.py index 9344ea2a..9220b5b5 100644 --- a/src/commands/group/create_group_command.py +++ b/src/commands/group/create_group_command.py @@ -29,9 +29,10 @@ def run_command(args): logger.info(_("creategroup.status").format(args.name)) new_group = TSC.GroupItem(args.name) server.groups.create(new_group) - logger.info(_("tabcmd.result.succeeded")) + logger.info(_("common.output.succeeded")) except TSC.ServerResponseError as e: + # quite likely a 403 because you must be server/site admin to call this if args.continue_if_exists and Errors.is_resource_conflict(e): logger.info(_("tabcmd.result.already_exists.group").format(args.name)) return - Errors.exit_with_error(logger, "tabcmd.result.failed.create_group") + Errors.exit_with_error(logger, _("tabcmd.result.failed.create_group")) diff --git a/src/commands/group/delete_group_command.py b/src/commands/group/delete_group_command.py index 1e94b910..ef76584b 100644 --- a/src/commands/group/delete_group_command.py +++ b/src/commands/group/delete_group_command.py @@ -30,6 +30,7 @@ def run_command(args): group_id = Server.find_group_id(logger, server, args.name) logger.info(_("deletegroup.status").format(group_id)) server.groups.delete(group_id) - logger.info(_("tabcmd.result.succeeded")) + logger.info(_("common.output.succeeded")) except TSC.ServerResponseError as e: - Errors.exit_with_error(logger, "tabcmd.result.failed.delete.group", e) + # quite likely a 403 because you must be server/site admin to call this + Errors.exit_with_error(logger, _("tabcmd.result.failed.delete.group"), e) diff --git a/src/commands/server.py b/src/commands/server.py index 3f171234..dee5dd0b 100644 --- a/src/commands/server.py +++ b/src/commands/server.py @@ -38,7 +38,7 @@ def find_group(logger, server, group_name): @staticmethod def find_group_id(logger, server, group_name): - return Server.find_group(logger, server, group_name)[0].id + return Server.find_group(logger, server, group_name).id @staticmethod def find_user_id(logger, server, username): @@ -49,7 +49,12 @@ def find_user_id(logger, server, username): @staticmethod def get_items_by_name(logger, item_endpoint, item_name, container=None): - logger.debug(_("export.status").format(item_name)) + item_type = type(item_endpoint).__name__ + item_log_name = item_name + if container: + item_log_name = container + "/" + item_log_name + item_log_name = "[" + item_type + "] " + item_log_name + logger.debug(_("export.status").format(item_log_name)) req_option = TSC.RequestOptions() req_option.filter.add(TSC.Filter(TSC.RequestOptions.Field.Name, TSC.RequestOptions.Operator.Equals, item_name)) if container: @@ -59,9 +64,11 @@ def get_items_by_name(logger, item_endpoint, item_name, container=None): ) all_items, pagination_item = item_endpoint.get(req_option) if all_items is None or all_items == []: - raise ValueError(_("publish.errors.server_resource_not_found")) + raise ValueError("[" + item_type + "] " + _("errors.xmlapi.not_found")) if len(all_items) > 1: logger.debug("{}+ items of this name were found. Returning first page.".format(len(all_items))) + logger.debug(all_items[0].name + ", " + all_items[1].name + ", " + all_items[2].name) + return all_items # Get site by name or get currently logged in site diff --git a/src/commands/user/user_data.py b/src/commands/user/user_data.py index c6fcafd1..dd5d9c53 100644 --- a/src/commands/user/user_data.py +++ b/src/commands/user/user_data.py @@ -214,10 +214,6 @@ def evaluate_site_role(license_level, admin_level, publisher): def act_on_users( logger: logging.Logger, server: object, action_name: str, server_method: Callable, args: argparse.Namespace ) -> None: - n_users_handled: int = 0 - number_of_errors: int = 0 - n_users_listed: int = UserCommand.validate_file_for_import(args.users, logger, strict=args.require_all_valid) - logger.debug(_("importcsvsummary.line.processed").format(n_users_listed)) group = None try: @@ -227,6 +223,11 @@ def act_on_users( logger, _("errors.reportable.impersonation.group_not_found").format(args.name), exception=e ) + n_users_handled: int = 0 + number_of_errors: int = 0 + n_users_listed: int = UserCommand.validate_file_for_import(args.users, logger, strict=args.require_all_valid) + logger.debug(_("importcsvsummary.line.processed").format(n_users_listed)) + error_list = [] user_obj_list: List[TSC.UserItem] = UserCommand.get_users_from_file(args.users) logger.debug(_("tabcmd.result.success.parsed_users").format(len(user_obj_list))) diff --git a/tests/commands/test_run_commands.py b/tests/commands/test_run_commands.py index 67c032c3..7ffd9e2c 100644 --- a/tests/commands/test_run_commands.py +++ b/tests/commands/test_run_commands.py @@ -162,7 +162,7 @@ def test_decrypt(self, mock_session, mock_server): def test_delete_extract(self, mock_session, mock_server): RunCommandsTest._set_up_session(mock_session, mock_server) mock_server.datasources = getter - mock_args.datasource = True + mock_args.datasource = "datasource-name" delete_extracts_command.DeleteExtracts.run_command(mock_args) mock_session.assert_called() From e62bc12f272d0e300bfa7ef1d478a9c917233b8a Mon Sep 17 00:00:00 2001 From: Jac Date: Tue, 12 Jul 2022 22:36:34 -0700 Subject: [PATCH 06/20] Update session.py (#141) clearly indicate when using the default site --- src/commands/auth/session.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/commands/auth/session.py b/src/commands/auth/session.py index 3deecb49..827dac98 100644 --- a/src/commands/auth/session.py +++ b/src/commands/auth/session.py @@ -160,7 +160,8 @@ def _print_server_info(self): self.logger.info("===== Username: {}".format(self.username)) else: self.logger.info("===== Token Name: {}".format(self.token_name)) - self.logger.info(_("dataconnections.classes.tableau_server_site") + ": {}".format(self.site_name)) + site_display_name = self.site_name or "Default Site" + self.logger.info(_("dataconnections.classes.tableau_server_site") + ": {}".format(site_display_name)) def _validate_existing_signin(self): self.logger.info(_("session.continuing_session")) From 2ad6352b7eb41777000664201a24a986cfd9bc94 Mon Sep 17 00:00:00 2001 From: Brian Cantoni Date: Wed, 13 Jul 2022 01:30:08 -0700 Subject: [PATCH 07/20] Fix export success log message params (#142) --- src/commands/datasources_and_workbooks/get_url_command.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/datasources_and_workbooks/get_url_command.py b/src/commands/datasources_and_workbooks/get_url_command.py index 52989ad5..557cfd1c 100644 --- a/src/commands/datasources_and_workbooks/get_url_command.py +++ b/src/commands/datasources_and_workbooks/get_url_command.py @@ -172,7 +172,7 @@ def generate_csv(logger, server, args): file_name_with_path = GetUrl.filename_from_args(args.filename, view_item.name, "csv") with open(file_name_with_path, "wb") as f: f.write(view_item.csv) - logger.info(_("export.success"), views_from_list.name, formatted_file_name) + logger.info(_("export.success").format(view_item.name, file_name_with_path)) except TSC.ServerResponseError as e: Errors.exit_with_error(logger, _("publish.errors.unexpected_server_response"), e) except Exception as e: From 313e486eb94ffba7dc12ab2238fa0470cfce523b Mon Sep 17 00:00:00 2001 From: Jac Date: Fri, 15 Jul 2022 11:50:13 -0700 Subject: [PATCH 08/20] Jac/tfs 1428582 create edit site (#146) --- src/commands/auth/session.py | 2 +- .../extracts/decrypt_extracts_command.py | 2 +- .../extracts/encrypt_extracts_command.py | 2 +- .../extracts/reencrypt_extracts_command.py | 2 +- src/commands/server.py | 17 +++++++++++------ src/commands/site/create_site_command.py | 15 +++++++++------ src/commands/site/delete_site_command.py | 8 +++----- src/commands/site/edit_site_command.py | 2 +- tests/commands/test_run_commands.py | 10 +++++----- tests/parsers/test_parser_create_site.py | 4 ++-- tests/parsers/test_parser_delete_site.py | 2 +- 11 files changed, 36 insertions(+), 30 deletions(-) diff --git a/src/commands/auth/session.py b/src/commands/auth/session.py index 827dac98..e224d35d 100644 --- a/src/commands/auth/session.py +++ b/src/commands/auth/session.py @@ -191,10 +191,10 @@ def _sign_in(self, tableau_auth): self.auth_token = self.tableau_server._auth_token if not self.username: self.username = self.tableau_server.users.get_by_id(self.user_id).name - self.logger.debug("Signed into {0}{1} as {2}".format(self.server_url, self.site_name, self.username)) self.logger.info(_("common.output.succeeded")) except TSC.ServerResponseError as e: Errors.exit_with_error(self.logger, _("publish.errors.unexpected_server_response"), e) + self.logger.debug("Signed into {0}{1} as {2}".format(self.server_url, self.site_name, self.username)) return self.tableau_server diff --git a/src/commands/extracts/decrypt_extracts_command.py b/src/commands/extracts/decrypt_extracts_command.py index 61605bc5..2f02ec7a 100644 --- a/src/commands/extracts/decrypt_extracts_command.py +++ b/src/commands/extracts/decrypt_extracts_command.py @@ -24,7 +24,7 @@ def run_command(args): logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args) - site_item = Server.get_site_for_command_or_throw(logger, server, args) + site_item = Server.get_site_for_command_or_throw(logger, server, args.site_name) try: logger.info(_("decryptextracts.status").format(args.site_name)) job = server.sites.decrypt_extracts(site_item.id) diff --git a/src/commands/extracts/encrypt_extracts_command.py b/src/commands/extracts/encrypt_extracts_command.py index 8cf32057..96d18422 100644 --- a/src/commands/extracts/encrypt_extracts_command.py +++ b/src/commands/extracts/encrypt_extracts_command.py @@ -26,7 +26,7 @@ def run_command(args): logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args) - site_item = Server.get_site_for_command_or_throw(logger, server, args) + site_item = Server.get_site_for_command_or_throw(logger, server, args.site_name) try: logger.info(_("encryptextracts.status").format(site_item.name)) job = server.sites.encrypt_extracts(site_item.id) diff --git a/src/commands/extracts/reencrypt_extracts_command.py b/src/commands/extracts/reencrypt_extracts_command.py index 3071757b..42021a10 100644 --- a/src/commands/extracts/reencrypt_extracts_command.py +++ b/src/commands/extracts/reencrypt_extracts_command.py @@ -26,7 +26,7 @@ def run_command(args): logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args) - site_item = Server.get_site_for_command_or_throw(logger, server, args) + site_item = Server.get_site_for_command_or_throw(logger, server, args.site_name) try: logger.info(_("reencryptextracts.status").format(site_item.name)) job = server.sites.encrypt_extracts(site_item.id) diff --git a/src/commands/server.py b/src/commands/server.py index dee5dd0b..3bec44ec 100644 --- a/src/commands/server.py +++ b/src/commands/server.py @@ -73,12 +73,9 @@ def get_items_by_name(logger, item_endpoint, item_name, container=None): # Get site by name or get currently logged in site @staticmethod - def get_site_for_command_or_throw(logger, server, args): - if args.site_name: - try: - site_item = Server.get_items_by_name(logger, server.sites, args.site_name)[0] - except Exception as e: - Errors.exit_with_error(logger, exception=e) + def get_site_for_command_or_throw(logger, server, site_name): + if site_name: + site_item = Server.get_site_by_name(logger, server, site_name) else: logger.debug("Use logged in site") site_item = server.sites.get_by_id(server.site_id) @@ -86,6 +83,14 @@ def get_site_for_command_or_throw(logger, server, args): raise ResourceWarning("Could not get site from server") return site_item + @staticmethod + def get_site_by_name(logger, server, site_name): + try: + site_item = Server.get_items_by_name(logger, server.sites, site_name)[0] + except Exception as e: + Errors.exit_with_error(logger, exception=e) + return site_item + @staticmethod def get_filename_extension_if_tableau_type(logger, filename): logger.debug("Filename given: {}".format(filename)) diff --git a/src/commands/site/create_site_command.py b/src/commands/site/create_site_command.py index 1e7489e8..ef44b56d 100644 --- a/src/commands/site/create_site_command.py +++ b/src/commands/site/create_site_command.py @@ -18,7 +18,7 @@ class CreateSiteCommand(Server): @staticmethod def define_args(create_site_parser): - create_site_parser.add_argument("site_name", metavar="site-name", help=_("editsite.options.site-name")) + create_site_parser.add_argument("new_site_name", metavar="site-name", help=_("editsite.options.site-name")) set_common_site_args(create_site_parser) @staticmethod @@ -27,21 +27,24 @@ def run_command(args): logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args) + admin_mode = "ContentAndUsers" # default: allow site admins to manage users + if not args.site_admin_user_management: + admin_mode = "ContentOnly" new_site = TSC.SiteItem( - name=args.site_name, - content_url=args.url, - admin_mode=args.admin_mode, + name=args.new_site_name, + content_url=args.url or args.new_site_name, + admin_mode=admin_mode, user_quota=args.user_quota, storage_quota=args.storage_quota, ) try: - logger.info(_("createsite.status").format(args.site_name)) + logger.info(_("createsite.status").format(args.new_site_name)) server.sites.create(new_site) logger.info(_("common.output.succeeded")) except TSC.ServerResponseError as e: if Errors.is_resource_conflict(e): if args.continue_if_exists: - logger.info(_("createsite.errors.site_name_already_exists").format(args.site_name)) + logger.info(_("createsite.errors.site_name_already_exists").format(args.new_site_name)) return else: Errors.exit_with_error( diff --git a/src/commands/site/delete_site_command.py b/src/commands/site/delete_site_command.py index 1e6622c0..2f9ea640 100644 --- a/src/commands/site/delete_site_command.py +++ b/src/commands/site/delete_site_command.py @@ -17,7 +17,7 @@ class DeleteSiteCommand(Server): @staticmethod def define_args(delete_site_parser): - delete_site_parser.add_argument("site_name", help="name of site to delete") + delete_site_parser.add_argument("site_name_to_delete", metavar="site-name", help="name of site to delete") @staticmethod def run_command(args): @@ -25,11 +25,9 @@ def run_command(args): logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args) - site_id = server.sites.get_by_name(args.site_name) - if site_id == session.site_id: - Errors.exit_with_error(logger, "Cannot delete the site you are logged in to") + site_url = Server.get_site_by_name(logger, server, args.site_name_to_delete).content_url try: - server.sites.delete(site_id) + server.sites.delete(site_url) logger.info("Successfully deleted the site") except TSC.ServerResponseError as e: Errors.exit_with_error(logger, "Error deleting site", e) diff --git a/src/commands/site/edit_site_command.py b/src/commands/site/edit_site_command.py index d384f34d..a055a45f 100644 --- a/src/commands/site/edit_site_command.py +++ b/src/commands/site/edit_site_command.py @@ -34,7 +34,7 @@ def run_command(args): session = Session() server = session.create_session(args) - site_item = Server.get_site_for_command_or_throw(logger, server, args) + site_item = Server.get_site_for_command_or_throw(logger, server, args.site_name) if args.url: site_item.content_url = args.url if args.user_quota: diff --git a/tests/commands/test_run_commands.py b/tests/commands/test_run_commands.py index 7ffd9e2c..f654598b 100644 --- a/tests/commands/test_run_commands.py +++ b/tests/commands/test_run_commands.py @@ -224,9 +224,9 @@ def test_create_project_already_exists(self, mock_session, mock_server): def test_create_site_already_exists(self, mock_session, mock_server): RunCommandsTest._set_up_session(mock_session, mock_server) mock_args.continue_if_exists = True - mock_args.site_name = "duplicate" + mock_args.new_site_name = "duplicate" mock_args.url = "dplct" - mock_args.admin_mode = None + mock_args.site_admin_user_management = None mock_args.user_quota = None mock_args.storage_quota = None mock_server.sites.create.return_value = TSC.ServerResponseError(409, "already exists", "detail") @@ -278,9 +278,9 @@ def test_publish_samples(self, mock_session, mock_server): # site def test_create_site(self, mock_session, mock_server): RunCommandsTest._set_up_session(mock_session, mock_server) - mock_args.site_name = "site-name" + mock_args.new_site_name = "site-name" mock_args.url = "site-content-url" - mock_args.admin_mode = None + mock_args.site_admin_user_management = None mock_args.user_quota = (None,) mock_args.storage_quota = None create_site_command.CreateSiteCommand.run_command(mock_args) @@ -289,7 +289,7 @@ def test_create_site(self, mock_session, mock_server): def test_delete_site(self, mock_session, mock_server): RunCommandsTest._set_up_session(mock_session, mock_server) mock_server.sites = getter - mock_args.site_name = "site-name" + mock_args.site_name_to_delete = "site-name" delete_site_command.DeleteSiteCommand.run_command(mock_args) mock_session.assert_called() diff --git a/tests/parsers/test_parser_create_site.py b/tests/parsers/test_parser_create_site.py index 1cffbb88..68f40b7d 100644 --- a/tests/parsers/test_parser_create_site.py +++ b/tests/parsers/test_parser_create_site.py @@ -14,7 +14,7 @@ def setUpClass(cls): def test_create_site_parser_just_a_name(self): mock_args = [commandname, "site-name"] args = self.parser_under_test.parse_args(mock_args) - assert args.site_name == "site-name", args + assert args.new_site_name == "site-name", args def test_create_site_parser_missing_required_name(self): mock_args = [commandname] @@ -37,6 +37,6 @@ def test_create_site_parser_with_all_args(self): ] # what else? args = self.parser_under_test.parse_args(mock_args) print(args) - assert args.site_name == "site-name", args + assert args.new_site_name == "site-name", args assert args.user_quota == 12, args assert args.storage_quota == 12, args diff --git a/tests/parsers/test_parser_delete_site.py b/tests/parsers/test_parser_delete_site.py index 00332236..843e9c82 100644 --- a/tests/parsers/test_parser_delete_site.py +++ b/tests/parsers/test_parser_delete_site.py @@ -14,7 +14,7 @@ def setUpClass(cls): def test_delete_site(self): mock_args = [commandname, "site-name"] args = self.parser_under_test.parse_args(mock_args) - assert args.site_name == "site-name", args + assert args.site_name_to_delete == "site-name", args def test_delete_site_required_name_none(self): mock_args = [commandname] From c1f3941158a773d3967eca312296146aeec76bbe Mon Sep 17 00:00:00 2001 From: Bhuvnesh Singh Date: Mon, 18 Jul 2022 10:18:00 -0700 Subject: [PATCH 09/20] Getting workbook works as expected, but appends extra file extensions --- src/commands/datasources_and_workbooks/get_url_command.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/datasources_and_workbooks/get_url_command.py b/src/commands/datasources_and_workbooks/get_url_command.py index 557cfd1c..36bc8f24 100644 --- a/src/commands/datasources_and_workbooks/get_url_command.py +++ b/src/commands/datasources_and_workbooks/get_url_command.py @@ -185,7 +185,7 @@ def generate_twb(logger, server, args, file_extension): target_workbook = GetUrl.get_wb_by_content_url(logger, server, workbook_name) logger.debug(_("content_type.workbook") + ": {}".format(workbook_name)) file_name_with_path = GetUrl.filename_from_args(args.filename, workbook_name, file_extension) - server.workbooks.download(target_workbook.id, filepath=file_name_with_path, no_extract=False) + server.workbooks.download(target_workbook.id, filepath=None, no_extract=False) logger.info(_("export.success").format(target_workbook.name, file_name_with_path)) except TSC.ServerResponseError as e: Errors.exit_with_error(logger, _("publish.errors.unexpected_server_response"), e) From 52a479352f2ad72ad2020dc9dec259ecdc65d3f0 Mon Sep 17 00:00:00 2001 From: Jac Date: Mon, 18 Jul 2022 16:06:09 -0700 Subject: [PATCH 10/20] Fix get view, get csv (#143) --- .../datasources_and_workbooks/get_url_command.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/commands/datasources_and_workbooks/get_url_command.py b/src/commands/datasources_and_workbooks/get_url_command.py index 557cfd1c..7718ad4b 100644 --- a/src/commands/datasources_and_workbooks/get_url_command.py +++ b/src/commands/datasources_and_workbooks/get_url_command.py @@ -65,7 +65,9 @@ def evaluate_content_type(logger, url): elif url.find("/workbooks/") == 0: return "workbook" else: - Errors.exit_with_error(logger, message=_("export.errors.requires_workbook_view_param").format(GetUrl.name)) + Errors.exit_with_error( + logger, message=_("export.errors.requires_workbook_view_param").format(__class__.__name__) + ) @staticmethod def get_file_type_from_filename(logger, file_name, url): @@ -77,7 +79,7 @@ def get_file_type_from_filename(logger, file_name, url): if not type_of_file: Errors.exit_with_error(logger, _("tabcmd.get.extension.not_found").format(file_name)) else: - logger.debug(_("get.options.file") + ": {}".format(type_of_file)) + logger.debug("filetype: {}".format(type_of_file)) if type_of_file in ["pdf", "csv", "png", "twb", "twbx"]: return type_of_file @@ -152,11 +154,11 @@ def generate_png(logger, server, args): try: view_item: TSC.ViewItem = GetUrl.get_view_by_content_url(logger, server, view) logger.debug(_("content_type.view") + ": {}".format(view_item.name)) - req_option_csv = TSC.CSVRequestOptions(maxage=1) - server.views.populate_csv(view_item, req_option_csv) + req_option_csv = TSC.CSVRequestOptions(maxage=1) # same as png + server.views.populate_image(view_item, req_option_csv) filename = GetUrl.filename_from_args(args.filename, view_item.name, "png") with open(filename, "wb") as f: - f.write(view_item.png) + f.write(view_item.image) logger.info(_("export.success").format(view_item.name, filename)) except TSC.ServerResponseError as e: Errors.exit_with_error(logger, _("publish.errors.unexpected_server_response"), e) @@ -171,7 +173,7 @@ def generate_csv(logger, server, args): server.views.populate_csv(view_item, req_option_csv) file_name_with_path = GetUrl.filename_from_args(args.filename, view_item.name, "csv") with open(file_name_with_path, "wb") as f: - f.write(view_item.csv) + f.writelines(view_item.csv) logger.info(_("export.success").format(view_item.name, file_name_with_path)) except TSC.ServerResponseError as e: Errors.exit_with_error(logger, _("publish.errors.unexpected_server_response"), e) @@ -181,10 +183,12 @@ def generate_csv(logger, server, args): @staticmethod def generate_twb(logger, server, args, file_extension): workbook_name = GetUrl.get_workbook_name(logger, args.url) + try: target_workbook = GetUrl.get_wb_by_content_url(logger, server, workbook_name) logger.debug(_("content_type.workbook") + ": {}".format(workbook_name)) file_name_with_path = GetUrl.filename_from_args(args.filename, workbook_name, file_extension) + logger.debug("Saving as {}".format(file_name_with_path)) 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_path)) except TSC.ServerResponseError as e: From 76964342053302f0999e337c67bde8709e224d9b Mon Sep 17 00:00:00 2001 From: Brian Cantoni Date: Tue, 19 Jul 2022 10:42:14 -0700 Subject: [PATCH 11/20] Change install destination (#150) * Rename src dir to tabcmd so that setup.py install will go to the 'tabcmd' destination directory * Update references in scripts and code to reflect src-->tabcmd --- .github/workflows/generate-metadata.yml | 2 +- README.md | 6 +- contributing.md | 2 +- dodo.py | 12 ++-- setup.py | 4 +- src/execution/map_of_commands.py | 61 ------------------ tabcmd-linux.spec | 4 +- tabcmd-mac.spec | 4 +- tabcmd-windows.spec | 4 +- tabcmd.spec | 2 +- {src => tabcmd}/__init__.py | 0 {src => tabcmd}/__main__.py | 2 +- {src => tabcmd}/commands/__init__.py | 0 {src => tabcmd}/commands/auth/__init__.py | 0 .../commands/auth/login_command.py | 6 +- .../commands/auth/logout_command.py | 8 +-- {src => tabcmd}/commands/auth/session.py | 6 +- {src => tabcmd}/commands/commands.py | 0 {src => tabcmd}/commands/constants.py | 2 +- .../datasources_and_workbooks/__init__.py | 0 .../datasources_and_workbooks_command.py | 6 +- .../delete_command.py | 10 +-- .../export_command.py | 8 +-- .../get_url_command.py | 10 +-- .../publish_command.py | 12 ++-- .../runschedule_command.py | 8 +-- {src => tabcmd}/commands/extracts/__init__.py | 0 .../extracts/create_extracts_command.py | 12 ++-- .../extracts/decrypt_extracts_command.py | 10 +-- .../extracts/delete_extracts_command.py | 12 ++-- .../extracts/encrypt_extracts_command.py | 10 +-- .../extracts/reencrypt_extracts_command.py | 10 +-- .../extracts/refresh_extracts_command.py | 12 ++-- {src => tabcmd}/commands/group/__init__.py | 0 .../commands/group/create_group_command.py | 10 +-- .../commands/group/delete_group_command.py | 10 +-- {src => tabcmd}/commands/help/__init__.py | 0 {src => tabcmd}/commands/help/help_command.py | 6 +- {src => tabcmd}/commands/project/__init__.py | 0 .../project/create_project_command.py | 12 ++-- .../project/delete_project_command.py | 12 ++-- .../project/publish_samples_command.py | 12 ++-- {src => tabcmd}/commands/server.py | 4 +- {src => tabcmd}/commands/site/__init__.py | 0 .../commands/site/create_site_command.py | 12 ++-- .../commands/site/delete_site_command.py | 10 +-- .../commands/site/edit_site_command.py | 12 ++-- .../commands/site/list_sites_command.py | 12 ++-- {src => tabcmd}/commands/user/__init__.py | 0 .../commands/user/add_users_command.py | 8 +-- .../commands/user/create_site_users.py | 8 +-- .../commands/user/create_users_command.py | 10 +-- .../user/delete_site_users_command.py | 14 ++-- .../commands/user/remove_users_command.py | 8 +-- {src => tabcmd}/commands/user/user_data.py | 6 +- {src => tabcmd}/execution/__init__.py | 0 {src => tabcmd}/execution/_version.py | 0 {src => tabcmd}/execution/global_options.py | 0 {src => tabcmd}/execution/localize.py | 0 {src => tabcmd}/execution/logger_config.py | 0 tabcmd/execution/map_of_commands.py | 61 ++++++++++++++++++ {src => tabcmd}/execution/parent_parser.py | 0 .../execution/tabcmd_controller.py | 0 .../locales/de/LC_MESSAGES/.gitkeep | 0 {src => tabcmd}/locales/de/LC_MESSAGES/de.po | 0 .../locales/de/LC_MESSAGES/shared_wg_de.po | 0 .../locales/de/LC_MESSAGES/tabcmd.mo | Bin .../locales/de/LC_MESSAGES/tabcmd.po | 0 .../de/LC_MESSAGES/tabcmd_messages_de.po | 0 .../locales/de/shared_wg_de.properties | 0 .../locales/de/tabcmd_messages_de.properties | 0 .../locales/en/LC_MESSAGES/en-US.po | 0 .../locales/en/LC_MESSAGES/shared_wg_en.po | 0 .../locales/en/LC_MESSAGES/tabcmd.mo | Bin .../locales/en/LC_MESSAGES/tabcmd.po | 0 .../en/LC_MESSAGES/tabcmd_messages_en.po | 0 .../locales/en/shared_wg_en.properties | 0 .../locales/en/tabcmd_messages_en.properties | 0 .../locales/es/LC_MESSAGES/.gitkeep | 0 {src => tabcmd}/locales/es/LC_MESSAGES/es.po | 0 .../locales/es/LC_MESSAGES/shared_wg_es.po | 0 .../locales/es/LC_MESSAGES/tabcmd.mo | Bin .../locales/es/LC_MESSAGES/tabcmd.po | 0 .../es/LC_MESSAGES/tabcmd_messages_es.po | 0 .../locales/es/shared_wg_es.properties | 0 .../locales/es/tabcmd_messages_es.properties | 0 .../locales/fr/LC_MESSAGES/.gitkeep | 0 {src => tabcmd}/locales/fr/LC_MESSAGES/fr.po | 0 .../locales/fr/LC_MESSAGES/shared_wg_fr.po | 0 .../locales/fr/LC_MESSAGES/tabcmd.mo | Bin .../locales/fr/LC_MESSAGES/tabcmd.po | 0 .../fr/LC_MESSAGES/tabcmd_messages_fr.po | 0 .../locales/fr/shared_wg_fr.properties | 0 .../locales/fr/tabcmd_messages_fr.properties | 0 .../locales/ga/LC_MESSAGES/.gitkeep | 0 {src => tabcmd}/locales/ga/LC_MESSAGES/ga.po | 0 .../locales/ga/LC_MESSAGES/shared_wg_ga.po | 0 .../locales/ga/LC_MESSAGES/tabcmd.mo | Bin .../locales/ga/LC_MESSAGES/tabcmd.po | 0 .../ga/LC_MESSAGES/tabcmd_messages_ga.po | 0 .../locales/ga/shared_wg_ga.properties | 0 .../locales/ga/tabcmd_messages_ga.properties | 0 .../locales/it/LC_MESSAGES/.gitkeep | 0 {src => tabcmd}/locales/it/LC_MESSAGES/it.po | 0 .../locales/it/LC_MESSAGES/shared_wg_it.po | 0 .../locales/it/LC_MESSAGES/tabcmd.mo | Bin .../locales/it/LC_MESSAGES/tabcmd.po | 0 .../it/LC_MESSAGES/tabcmd_messages_it.po | 0 .../locales/it/shared_wg_it.properties | 0 .../locales/it/tabcmd_messages_it.properties | 0 .../locales/ja/LC_MESSAGES/.gitkeep | 0 {src => tabcmd}/locales/ja/LC_MESSAGES/ja.po | 0 .../locales/ja/LC_MESSAGES/shared_wg_ja.po | 0 .../locales/ja/LC_MESSAGES/tabcmd.mo | Bin .../locales/ja/LC_MESSAGES/tabcmd.po | 0 .../ja/LC_MESSAGES/tabcmd_messages_ja.po | 0 .../locales/ja/shared_wg_ja.properties | 0 .../locales/ja/tabcmd_messages_ja.properties | 0 .../locales/ko/LC_MESSAGES/.gitkeep | 0 {src => tabcmd}/locales/ko/LC_MESSAGES/ko.po | 0 .../locales/ko/LC_MESSAGES/shared_wg_ko.po | 0 .../locales/ko/LC_MESSAGES/tabcmd.mo | Bin .../locales/ko/LC_MESSAGES/tabcmd.po | 0 .../ko/LC_MESSAGES/tabcmd_messages_ko.po | 0 .../locales/ko/shared_wg_ko.properties | 0 .../locales/ko/tabcmd_messages_ko.properties | 0 .../locales/pt/LC_MESSAGES/.gitkeep | 0 {src => tabcmd}/locales/pt/LC_MESSAGES/pt.po | 0 .../locales/pt/LC_MESSAGES/shared_wg_pt.po | 0 .../locales/pt/LC_MESSAGES/tabcmd.mo | Bin .../locales/pt/LC_MESSAGES/tabcmd.po | 0 .../pt/LC_MESSAGES/tabcmd_messages_pt.po | 0 .../locales/pt/shared_wg_pt.properties | 0 .../locales/pt/tabcmd_messages_pt.properties | 0 .../locales/sv/LC_MESSAGES/.gitkeep | 0 .../locales/sv/LC_MESSAGES/shared_wg_sv.po | 0 {src => tabcmd}/locales/sv/LC_MESSAGES/sv.po | 0 .../locales/sv/LC_MESSAGES/tabcmd.mo | Bin .../locales/sv/LC_MESSAGES/tabcmd.po | 0 .../sv/LC_MESSAGES/tabcmd_messages_sv.po | 0 .../locales/sv/shared_wg_sv.properties | 0 .../locales/sv/tabcmd_messages_sv.properties | 0 .../locales/zh/LC_MESSAGES/.gitkeep | 0 .../locales/zh/LC_MESSAGES/shared_wg_zh.po | 0 .../locales/zh/LC_MESSAGES/tabcmd.mo | Bin .../locales/zh/LC_MESSAGES/tabcmd.po | 0 .../zh/LC_MESSAGES/tabcmd_messages_zh.po | 0 {src => tabcmd}/locales/zh/LC_MESSAGES/zh.po | 0 .../locales/zh/shared_wg_zh.properties | 0 .../locales/zh/tabcmd_messages_zh.properties | 0 {src => tabcmd}/tabcmd.py | 2 +- tests/commands/test_execution.py | 4 +- tests/commands/test_geturl_utils.py | 6 +- tests/commands/test_localize.py | 2 +- tests/commands/test_projects_utils.py | 4 +- tests/commands/test_run_commands.py | 20 +++--- tests/commands/test_server_handler.py | 2 +- tests/commands/test_session.py | 2 +- tests/commands/test_user_utils.py | 4 +- tests/e2e/tests_integration.py | 6 +- tests/parsers/common_setup.py | 2 +- tests/parsers/test_login_parser.py | 2 +- tests/parsers/test_logout_parser.py | 2 +- tests/parsers/test_parser_add_user.py | 2 +- tests/parsers/test_parser_create_extracts.py | 2 +- tests/parsers/test_parser_create_group.py | 2 +- tests/parsers/test_parser_create_project.py | 2 +- tests/parsers/test_parser_create_site.py | 2 +- .../parsers/test_parser_create_site_users.py | 2 +- tests/parsers/test_parser_create_user.py | 2 +- tests/parsers/test_parser_decrypt_extracts.py | 2 +- tests/parsers/test_parser_delete.py | 2 +- tests/parsers/test_parser_delete_extracts.py | 2 +- tests/parsers/test_parser_delete_group.py | 2 +- tests/parsers/test_parser_delete_project.py | 2 +- tests/parsers/test_parser_delete_site.py | 2 +- tests/parsers/test_parser_delete_site_user.py | 2 +- tests/parsers/test_parser_edit_site.py | 2 +- tests/parsers/test_parser_encrypt_extracts.py | 2 +- tests/parsers/test_parser_export.py | 2 +- tests/parsers/test_parser_get_url.py | 2 +- tests/parsers/test_parser_list_sites.py | 2 +- tests/parsers/test_parser_publish.py | 2 +- tests/parsers/test_parser_publish_samples.py | 2 +- .../parsers/test_parser_reencrypt_extracts.py | 2 +- tests/parsers/test_parser_refresh_extracts.py | 2 +- tests/parsers/test_parser_remove_user.py | 2 +- tests/parsers/test_parser_runschedule.py | 2 +- 188 files changed, 290 insertions(+), 290 deletions(-) delete mode 100644 src/execution/map_of_commands.py rename {src => tabcmd}/__init__.py (100%) rename {src => tabcmd}/__main__.py (85%) rename {src => tabcmd}/commands/__init__.py (100%) rename {src => tabcmd}/commands/auth/__init__.py (100%) rename {src => tabcmd}/commands/auth/login_command.py (79%) rename {src => tabcmd}/commands/auth/logout_command.py (72%) rename {src => tabcmd}/commands/auth/session.py (99%) rename {src => tabcmd}/commands/commands.py (100%) rename {src => tabcmd}/commands/constants.py (97%) rename {src => tabcmd}/commands/datasources_and_workbooks/__init__.py (100%) rename {src => tabcmd}/commands/datasources_and_workbooks/datasources_and_workbooks_command.py (93%) rename {src => tabcmd}/commands/datasources_and_workbooks/delete_command.py (89%) rename {src => tabcmd}/commands/datasources_and_workbooks/export_command.py (97%) rename {src => tabcmd}/commands/datasources_and_workbooks/get_url_command.py (97%) rename {src => tabcmd}/commands/datasources_and_workbooks/publish_command.py (91%) rename {src => tabcmd}/commands/datasources_and_workbooks/runschedule_command.py (83%) rename {src => tabcmd}/commands/extracts/__init__.py (100%) rename {src => tabcmd}/commands/extracts/create_extracts_command.py (88%) rename {src => tabcmd}/commands/extracts/decrypt_extracts_command.py (84%) rename {src => tabcmd}/commands/extracts/delete_extracts_command.py (86%) rename {src => tabcmd}/commands/extracts/encrypt_extracts_command.py (84%) rename {src => tabcmd}/commands/extracts/reencrypt_extracts_command.py (84%) rename {src => tabcmd}/commands/extracts/refresh_extracts_command.py (94%) rename {src => tabcmd}/commands/group/__init__.py (100%) rename {src => tabcmd}/commands/group/create_group_command.py (84%) rename {src => tabcmd}/commands/group/delete_group_command.py (83%) rename {src => tabcmd}/commands/help/__init__.py (100%) rename {src => tabcmd}/commands/help/help_command.py (94%) rename {src => tabcmd}/commands/project/__init__.py (100%) rename {src => tabcmd}/commands/project/create_project_command.py (89%) rename {src => tabcmd}/commands/project/delete_project_command.py (85%) rename {src => tabcmd}/commands/project/publish_samples_command.py (85%) rename {src => tabcmd}/commands/server.py (98%) rename {src => tabcmd}/commands/site/__init__.py (100%) rename {src => tabcmd}/commands/site/create_site_command.py (87%) rename {src => tabcmd}/commands/site/delete_site_command.py (80%) rename {src => tabcmd}/commands/site/edit_site_command.py (86%) rename {src => tabcmd}/commands/site/list_sites_command.py (80%) rename {src => tabcmd}/commands/user/__init__.py (100%) rename {src => tabcmd}/commands/user/add_users_command.py (82%) rename {src => tabcmd}/commands/user/create_site_users.py (92%) rename {src => tabcmd}/commands/user/create_users_command.py (91%) rename {src => tabcmd}/commands/user/delete_site_users_command.py (86%) rename {src => tabcmd}/commands/user/remove_users_command.py (83%) rename {src => tabcmd}/commands/user/user_data.py (98%) rename {src => tabcmd}/execution/__init__.py (100%) rename {src => tabcmd}/execution/_version.py (100%) rename {src => tabcmd}/execution/global_options.py (100%) rename {src => tabcmd}/execution/localize.py (100%) rename {src => tabcmd}/execution/logger_config.py (100%) create mode 100644 tabcmd/execution/map_of_commands.py rename {src => tabcmd}/execution/parent_parser.py (100%) rename {src => tabcmd}/execution/tabcmd_controller.py (100%) rename {src => tabcmd}/locales/de/LC_MESSAGES/.gitkeep (100%) rename {src => tabcmd}/locales/de/LC_MESSAGES/de.po (100%) rename {src => tabcmd}/locales/de/LC_MESSAGES/shared_wg_de.po (100%) rename {src => tabcmd}/locales/de/LC_MESSAGES/tabcmd.mo (100%) rename {src => tabcmd}/locales/de/LC_MESSAGES/tabcmd.po (100%) rename {src => tabcmd}/locales/de/LC_MESSAGES/tabcmd_messages_de.po (100%) rename {src => tabcmd}/locales/de/shared_wg_de.properties (100%) rename {src => tabcmd}/locales/de/tabcmd_messages_de.properties (100%) rename {src => tabcmd}/locales/en/LC_MESSAGES/en-US.po (100%) rename {src => tabcmd}/locales/en/LC_MESSAGES/shared_wg_en.po (100%) rename {src => tabcmd}/locales/en/LC_MESSAGES/tabcmd.mo (100%) rename {src => tabcmd}/locales/en/LC_MESSAGES/tabcmd.po (100%) rename {src => tabcmd}/locales/en/LC_MESSAGES/tabcmd_messages_en.po (100%) rename {src => tabcmd}/locales/en/shared_wg_en.properties (100%) rename {src => tabcmd}/locales/en/tabcmd_messages_en.properties (100%) rename {src => tabcmd}/locales/es/LC_MESSAGES/.gitkeep (100%) rename {src => tabcmd}/locales/es/LC_MESSAGES/es.po (100%) rename {src => tabcmd}/locales/es/LC_MESSAGES/shared_wg_es.po (100%) rename {src => tabcmd}/locales/es/LC_MESSAGES/tabcmd.mo (100%) rename {src => tabcmd}/locales/es/LC_MESSAGES/tabcmd.po (100%) rename {src => tabcmd}/locales/es/LC_MESSAGES/tabcmd_messages_es.po (100%) rename {src => tabcmd}/locales/es/shared_wg_es.properties (100%) rename {src => tabcmd}/locales/es/tabcmd_messages_es.properties (100%) rename {src => tabcmd}/locales/fr/LC_MESSAGES/.gitkeep (100%) rename {src => tabcmd}/locales/fr/LC_MESSAGES/fr.po (100%) rename {src => tabcmd}/locales/fr/LC_MESSAGES/shared_wg_fr.po (100%) rename {src => tabcmd}/locales/fr/LC_MESSAGES/tabcmd.mo (100%) rename {src => tabcmd}/locales/fr/LC_MESSAGES/tabcmd.po (100%) rename {src => tabcmd}/locales/fr/LC_MESSAGES/tabcmd_messages_fr.po (100%) rename {src => tabcmd}/locales/fr/shared_wg_fr.properties (100%) rename {src => tabcmd}/locales/fr/tabcmd_messages_fr.properties (100%) rename {src => tabcmd}/locales/ga/LC_MESSAGES/.gitkeep (100%) rename {src => tabcmd}/locales/ga/LC_MESSAGES/ga.po (100%) rename {src => tabcmd}/locales/ga/LC_MESSAGES/shared_wg_ga.po (100%) rename {src => tabcmd}/locales/ga/LC_MESSAGES/tabcmd.mo (100%) rename {src => tabcmd}/locales/ga/LC_MESSAGES/tabcmd.po (100%) rename {src => tabcmd}/locales/ga/LC_MESSAGES/tabcmd_messages_ga.po (100%) rename {src => tabcmd}/locales/ga/shared_wg_ga.properties (100%) rename {src => tabcmd}/locales/ga/tabcmd_messages_ga.properties (100%) rename {src => tabcmd}/locales/it/LC_MESSAGES/.gitkeep (100%) rename {src => tabcmd}/locales/it/LC_MESSAGES/it.po (100%) rename {src => tabcmd}/locales/it/LC_MESSAGES/shared_wg_it.po (100%) rename {src => tabcmd}/locales/it/LC_MESSAGES/tabcmd.mo (100%) rename {src => tabcmd}/locales/it/LC_MESSAGES/tabcmd.po (100%) rename {src => tabcmd}/locales/it/LC_MESSAGES/tabcmd_messages_it.po (100%) rename {src => tabcmd}/locales/it/shared_wg_it.properties (100%) rename {src => tabcmd}/locales/it/tabcmd_messages_it.properties (100%) rename {src => tabcmd}/locales/ja/LC_MESSAGES/.gitkeep (100%) rename {src => tabcmd}/locales/ja/LC_MESSAGES/ja.po (100%) rename {src => tabcmd}/locales/ja/LC_MESSAGES/shared_wg_ja.po (100%) rename {src => tabcmd}/locales/ja/LC_MESSAGES/tabcmd.mo (100%) rename {src => tabcmd}/locales/ja/LC_MESSAGES/tabcmd.po (100%) rename {src => tabcmd}/locales/ja/LC_MESSAGES/tabcmd_messages_ja.po (100%) rename {src => tabcmd}/locales/ja/shared_wg_ja.properties (100%) rename {src => tabcmd}/locales/ja/tabcmd_messages_ja.properties (100%) rename {src => tabcmd}/locales/ko/LC_MESSAGES/.gitkeep (100%) rename {src => tabcmd}/locales/ko/LC_MESSAGES/ko.po (100%) rename {src => tabcmd}/locales/ko/LC_MESSAGES/shared_wg_ko.po (100%) rename {src => tabcmd}/locales/ko/LC_MESSAGES/tabcmd.mo (100%) rename {src => tabcmd}/locales/ko/LC_MESSAGES/tabcmd.po (100%) rename {src => tabcmd}/locales/ko/LC_MESSAGES/tabcmd_messages_ko.po (100%) rename {src => tabcmd}/locales/ko/shared_wg_ko.properties (100%) rename {src => tabcmd}/locales/ko/tabcmd_messages_ko.properties (100%) rename {src => tabcmd}/locales/pt/LC_MESSAGES/.gitkeep (100%) rename {src => tabcmd}/locales/pt/LC_MESSAGES/pt.po (100%) rename {src => tabcmd}/locales/pt/LC_MESSAGES/shared_wg_pt.po (100%) rename {src => tabcmd}/locales/pt/LC_MESSAGES/tabcmd.mo (100%) rename {src => tabcmd}/locales/pt/LC_MESSAGES/tabcmd.po (100%) rename {src => tabcmd}/locales/pt/LC_MESSAGES/tabcmd_messages_pt.po (100%) rename {src => tabcmd}/locales/pt/shared_wg_pt.properties (100%) rename {src => tabcmd}/locales/pt/tabcmd_messages_pt.properties (100%) rename {src => tabcmd}/locales/sv/LC_MESSAGES/.gitkeep (100%) rename {src => tabcmd}/locales/sv/LC_MESSAGES/shared_wg_sv.po (100%) rename {src => tabcmd}/locales/sv/LC_MESSAGES/sv.po (100%) rename {src => tabcmd}/locales/sv/LC_MESSAGES/tabcmd.mo (100%) rename {src => tabcmd}/locales/sv/LC_MESSAGES/tabcmd.po (100%) rename {src => tabcmd}/locales/sv/LC_MESSAGES/tabcmd_messages_sv.po (100%) rename {src => tabcmd}/locales/sv/shared_wg_sv.properties (100%) rename {src => tabcmd}/locales/sv/tabcmd_messages_sv.properties (100%) rename {src => tabcmd}/locales/zh/LC_MESSAGES/.gitkeep (100%) rename {src => tabcmd}/locales/zh/LC_MESSAGES/shared_wg_zh.po (100%) rename {src => tabcmd}/locales/zh/LC_MESSAGES/tabcmd.mo (100%) rename {src => tabcmd}/locales/zh/LC_MESSAGES/tabcmd.po (100%) rename {src => tabcmd}/locales/zh/LC_MESSAGES/tabcmd_messages_zh.po (100%) rename {src => tabcmd}/locales/zh/LC_MESSAGES/zh.po (100%) rename {src => tabcmd}/locales/zh/shared_wg_zh.properties (100%) rename {src => tabcmd}/locales/zh/tabcmd_messages_zh.properties (100%) rename {src => tabcmd}/tabcmd.py (90%) diff --git a/.github/workflows/generate-metadata.yml b/.github/workflows/generate-metadata.yml index 12513ace..213b564a 100644 --- a/.github/workflows/generate-metadata.yml +++ b/.github/workflows/generate-metadata.yml @@ -29,7 +29,7 @@ jobs: run: python bin/license-checker.py - name: Type-check - run: mypy src tests + run: mypy tabcmd tests - name: Check formatting with black run: black . --check diff --git a/README.md b/README.md index 5ec40a8a..c22552aa 100644 --- a/README.md +++ b/README.md @@ -58,13 +58,13 @@ To run tabcmd from your local copy, from a console window in the same directory > coverage run -m pytest && coverage report -m - autoformat your code with black (https://pypi.org/project/black/) -> black --line-length 120 src tests [--check] +> black --line-length 120 tabcmd tests [--check] - type check with mypy -> mypy src tests +> mypy tabcmd tests - packaging is done with pyinstaller. You can only build an executable for the platform you build on. -> pyinstaller src\tabcmd.py --clean --noconfirm +> pyinstaller tabcmd\tabcmd.py --clean --noconfirm produces dist/tabcmd.exe To run tabcmd during development, from a console window in the same directory as the file tabcmd.py: diff --git a/contributing.md b/contributing.md index c7c77036..723e8f86 100644 --- a/contributing.md +++ b/contributing.md @@ -34,7 +34,7 @@ _(note that running mypy and black is required for code being submitted to the r - autoformat your code with black (https://pypi.org/project/black/) > black . - check types -> mypy src tests +> mypy tabcmd tests - do test coverage calculation (https://coverage.readthedocs.io/en/6.3.2) > bin/coverage.sh diff --git a/dodo.py b/dodo.py index 3e4272a5..c24d9506 100644 --- a/dodo.py +++ b/dodo.py @@ -33,7 +33,7 @@ def process_locales(): else: encoding = "cp1252" - for file in glob.glob("src/locales/" + current_locale + "/*.properties"): + for file in glob.glob("tabcmd/locales/" + current_locale + "/*.properties"): basename = os.path.basename(file).split(".")[0] print("transcoding", basename) with open(file, encoding=encoding) as infile: @@ -66,7 +66,7 @@ def task_po(): def process_locales(): for current_locale in LOCALES: - LOC_PATH = "src/locales/" + current_locale + LOC_PATH = "tabcmd/locales/" + current_locale for file in glob.glob(LOC_PATH + "/*.properties"): basename = os.path.basename(file).split(".")[0] print("processing", basename) @@ -98,7 +98,7 @@ def task_clean_all(): def process_locales(): for current_locale in LOCALES: - LOC_PATH = "src/locales/" + current_locale + LOC_PATH = "tabcmd/locales/" + current_locale for file in glob.glob(LOC_PATH + "/*.properties"): basename = os.path.basename(file).split(".")[0] print("deleting", basename + ".*") @@ -130,7 +130,7 @@ def task_merge(): def process_locales(): for current_locale in LOCALES: - LOC_PATH = "src/locales/" + current_locale + "/LC_MESSAGES" + LOC_PATH = "tabcmd/locales/" + current_locale + "/LC_MESSAGES" with open(LOC_PATH + "/tabcmd.po", "w+", encoding="utf-8") as outfile: for file in glob.glob(LOC_PATH + "/*.po"): @@ -157,11 +157,11 @@ def task_mo(): def process_locales(): for current_locale in LOCALES: - LOC_PATH = "src/locales/" + current_locale + "/LC_MESSAGES" + LOC_PATH = "tabcmd/locales/" + current_locale + "/LC_MESSAGES" print("writing final tabcmd.mo file") # build the single binary file from the .po file - result = subprocess.run(["python", "bin/i18n/msgfmt.py", LOC_PATH + "/src"]) + result = subprocess.run(["python", "bin/i18n/msgfmt.py", LOC_PATH + "/tabcmd"]) print("\n", result) # print("stdout:", result.stdout) if not result.returncode == 0: diff --git a/setup.py b/setup.py index 57466d7d..8d834b82 100644 --- a/setup.py +++ b/setup.py @@ -10,9 +10,9 @@ url="https://github.com/tableau/tabcmd", python_requires=">=3.7", packages=find_packages(), - package_data={"tabcmd": ["src.locales/**/*.mo"]}, + package_data={"tabcmd": ["tabcmd.locales/**/*.mo"]}, include_package_data=True, - entry_points={"console_scripts": ["tabcmd = src.tabcmd:main"]}, + entry_points={"console_scripts": ["tabcmd = tabcmd.tabcmd:main"]}, setup_requires=[ # copy of pyproject.toml for back compat "build", diff --git a/src/execution/map_of_commands.py b/src/execution/map_of_commands.py deleted file mode 100644 index 628349d2..00000000 --- a/src/execution/map_of_commands.py +++ /dev/null @@ -1,61 +0,0 @@ -from src.commands.auth.login_command import * -from src.commands.auth.logout_command import * -from src.commands.datasources_and_workbooks.delete_command import * -from src.commands.datasources_and_workbooks.export_command import * -from src.commands.datasources_and_workbooks.get_url_command import * -from src.commands.datasources_and_workbooks.publish_command import * -from src.commands.extracts.create_extracts_command import * -from src.commands.extracts.decrypt_extracts_command import * -from src.commands.extracts.delete_extracts_command import * -from src.commands.extracts.encrypt_extracts_command import * -from src.commands.extracts.reencrypt_extracts_command import * -from src.commands.extracts.refresh_extracts_command import * -from src.commands.group.create_group_command import * -from src.commands.group.delete_group_command import * -from src.commands.help.help_command import * -from src.commands.project.create_project_command import * -from src.commands.project.delete_project_command import * -from src.commands.project.publish_samples_command import * -from src.commands.site.create_site_command import * -from src.commands.site.delete_site_command import * -from src.commands.site.edit_site_command import * -from src.commands.site.list_sites_command import * -from src.commands.user.add_users_command import * -from src.commands.user.create_site_users import * -from src.commands.user.delete_site_users_command import * - -# from src.commands.user.create_users import * -from src.commands.user.remove_users_command import * - - -class CommandsMap: - commands_hash_map = [ - # not yet implemented "createusers": ("createusers", CreateUserCommand, "Create users on the server"), - # run schedule - AddUserCommand, - CreateExtracts, - CreateGroupCommand, - CreateProjectCommand, - CreateSiteCommand, - CreateSiteUsersCommand, - DecryptExtracts, - DeleteCommand, - DeleteExtracts, - DeleteGroupCommand, - DeleteProjectCommand, - DeleteSiteCommand, - DeleteSiteUsersCommand, - EditSiteCommand, - EncryptExtracts, - ExportCommand, - GetUrl, - HelpCommand, - ListSiteCommand, - LoginCommand, - LogoutCommand, - PublishCommand, - PublishSamplesCommand, - ReencryptExtracts, - RefreshExtracts, - RemoveUserCommand, - ] diff --git a/tabcmd-linux.spec b/tabcmd-linux.spec index 530baf62..d71fd83e 100644 --- a/tabcmd-linux.spec +++ b/tabcmd-linux.spec @@ -2,13 +2,13 @@ from PyInstaller.utils.hooks import collect_data_files datas = [] -datas += collect_data_files('src.locales') +datas += collect_data_files('tabcmd.locales') print(datas) block_cipher = None a = Analysis( - ['src\\tabcmd.py'], + ['tabcmd\\tabcmd.py'], pathex=[], binaries=[], datas=datas, diff --git a/tabcmd-mac.spec b/tabcmd-mac.spec index 5f56d46c..fcce4bfe 100644 --- a/tabcmd-mac.spec +++ b/tabcmd-mac.spec @@ -2,14 +2,14 @@ from PyInstaller.utils.hooks import collect_data_files datas = [] -datas += collect_data_files('src.locales') +datas += collect_data_files('tabcmd.locales') block_cipher = None a = Analysis( - ['src/tabcmd.py'], + ['tabcmd/tabcmd.py'], pathex=[], binaries=[], datas=datas, diff --git a/tabcmd-windows.spec b/tabcmd-windows.spec index 530baf62..d71fd83e 100644 --- a/tabcmd-windows.spec +++ b/tabcmd-windows.spec @@ -2,13 +2,13 @@ from PyInstaller.utils.hooks import collect_data_files datas = [] -datas += collect_data_files('src.locales') +datas += collect_data_files('tabcmd.locales') print(datas) block_cipher = None a = Analysis( - ['src\\tabcmd.py'], + ['tabcmd\\tabcmd.py'], pathex=[], binaries=[], datas=datas, diff --git a/tabcmd.spec b/tabcmd.spec index 4475458a..650b541a 100644 --- a/tabcmd.spec +++ b/tabcmd.spec @@ -5,7 +5,7 @@ block_cipher = None a = Analysis( - ['src\\tabcmd.py'], + ['tabcmd\\tabcmd.py'], pathex=[], binaries=[], datas=[], diff --git a/src/__init__.py b/tabcmd/__init__.py similarity index 100% rename from src/__init__.py rename to tabcmd/__init__.py diff --git a/src/__main__.py b/tabcmd/__main__.py similarity index 85% rename from src/__main__.py rename to tabcmd/__main__.py index 29ea8617..29886963 100644 --- a/src/__main__.py +++ b/tabcmd/__main__.py @@ -1,7 +1,7 @@ import sys try: - from src.tabcmd import main + from tabcmd.tabcmd import main except ImportError: print("Tabcmd needs to be run as a module, it cannot be run as a script") print("Try running python -m tabcmd") diff --git a/src/commands/__init__.py b/tabcmd/commands/__init__.py similarity index 100% rename from src/commands/__init__.py rename to tabcmd/commands/__init__.py diff --git a/src/commands/auth/__init__.py b/tabcmd/commands/auth/__init__.py similarity index 100% rename from src/commands/auth/__init__.py rename to tabcmd/commands/auth/__init__.py diff --git a/src/commands/auth/login_command.py b/tabcmd/commands/auth/login_command.py similarity index 79% rename from src/commands/auth/login_command.py rename to tabcmd/commands/auth/login_command.py index fd636369..647fee3f 100644 --- a/src/commands/auth/login_command.py +++ b/tabcmd/commands/auth/login_command.py @@ -1,6 +1,6 @@ -from src.commands.server import Server -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.server import Server +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log from .session import Session diff --git a/src/commands/auth/logout_command.py b/tabcmd/commands/auth/logout_command.py similarity index 72% rename from src/commands/auth/logout_command.py rename to tabcmd/commands/auth/logout_command.py index 49eb72e7..b15f8246 100644 --- a/src/commands/auth/logout_command.py +++ b/tabcmd/commands/auth/logout_command.py @@ -1,7 +1,7 @@ -from src.commands.auth.session import Session -from src.commands.server import Server -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.server import Server +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class LogoutCommand(Server): diff --git a/src/commands/auth/session.py b/tabcmd/commands/auth/session.py similarity index 99% rename from src/commands/auth/session.py rename to tabcmd/commands/auth/session.py index e224d35d..ba98fd2d 100644 --- a/src/commands/auth/session.py +++ b/tabcmd/commands/auth/session.py @@ -7,9 +7,9 @@ import tableauserverclient.server.endpoint.exceptions from urllib3.exceptions import InsecureRequestWarning -from src.commands.constants import Errors -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.constants import Errors +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class Session: diff --git a/src/commands/commands.py b/tabcmd/commands/commands.py similarity index 100% rename from src/commands/commands.py rename to tabcmd/commands/commands.py diff --git a/src/commands/constants.py b/tabcmd/commands/constants.py similarity index 97% rename from src/commands/constants.py rename to tabcmd/commands/constants.py index 66cc0db2..fe642bbb 100644 --- a/src/commands/constants.py +++ b/tabcmd/commands/constants.py @@ -1,6 +1,6 @@ import sys -from src.execution.localize import _ +from tabcmd.execution.localize import _ class Constants: diff --git a/src/commands/datasources_and_workbooks/__init__.py b/tabcmd/commands/datasources_and_workbooks/__init__.py similarity index 100% rename from src/commands/datasources_and_workbooks/__init__.py rename to tabcmd/commands/datasources_and_workbooks/__init__.py diff --git a/src/commands/datasources_and_workbooks/datasources_and_workbooks_command.py b/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py similarity index 93% rename from src/commands/datasources_and_workbooks/datasources_and_workbooks_command.py rename to tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py index 1948914b..af54d65a 100644 --- a/src/commands/datasources_and_workbooks/datasources_and_workbooks_command.py +++ b/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py @@ -1,8 +1,8 @@ import tableauserverclient as TSC -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.localize import _ +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.localize import _ class DatasourcesAndWorkbooks(Server): diff --git a/src/commands/datasources_and_workbooks/delete_command.py b/tabcmd/commands/datasources_and_workbooks/delete_command.py similarity index 89% rename from src/commands/datasources_and_workbooks/delete_command.py rename to tabcmd/commands/datasources_and_workbooks/delete_command.py index 223eefbd..e9750a93 100644 --- a/src/commands/datasources_and_workbooks/delete_command.py +++ b/tabcmd/commands/datasources_and_workbooks/delete_command.py @@ -1,10 +1,10 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.execution.global_options import * -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log from .datasources_and_workbooks_command import DatasourcesAndWorkbooks diff --git a/src/commands/datasources_and_workbooks/export_command.py b/tabcmd/commands/datasources_and_workbooks/export_command.py similarity index 97% rename from src/commands/datasources_and_workbooks/export_command.py rename to tabcmd/commands/datasources_and_workbooks/export_command.py index ff0b8da9..72f7375a 100644 --- a/src/commands/datasources_and_workbooks/export_command.py +++ b/tabcmd/commands/datasources_and_workbooks/export_command.py @@ -1,9 +1,9 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log from .datasources_and_workbooks_command import DatasourcesAndWorkbooks diff --git a/src/commands/datasources_and_workbooks/get_url_command.py b/tabcmd/commands/datasources_and_workbooks/get_url_command.py similarity index 97% rename from src/commands/datasources_and_workbooks/get_url_command.py rename to tabcmd/commands/datasources_and_workbooks/get_url_command.py index 643b7e80..5b23dc4e 100644 --- a/src/commands/datasources_and_workbooks/get_url_command.py +++ b/tabcmd/commands/datasources_and_workbooks/get_url_command.py @@ -1,10 +1,10 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.execution.global_options import * -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log from .datasources_and_workbooks_command import DatasourcesAndWorkbooks diff --git a/src/commands/datasources_and_workbooks/publish_command.py b/tabcmd/commands/datasources_and_workbooks/publish_command.py similarity index 91% rename from src/commands/datasources_and_workbooks/publish_command.py rename to tabcmd/commands/datasources_and_workbooks/publish_command.py index b360b775..c81c0091 100644 --- a/src/commands/datasources_and_workbooks/publish_command.py +++ b/tabcmd/commands/datasources_and_workbooks/publish_command.py @@ -1,11 +1,11 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.global_options import * -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log from .datasources_and_workbooks_command import DatasourcesAndWorkbooks diff --git a/src/commands/datasources_and_workbooks/runschedule_command.py b/tabcmd/commands/datasources_and_workbooks/runschedule_command.py similarity index 83% rename from src/commands/datasources_and_workbooks/runschedule_command.py rename to tabcmd/commands/datasources_and_workbooks/runschedule_command.py index 0b58e396..f4eceb29 100644 --- a/src/commands/datasources_and_workbooks/runschedule_command.py +++ b/tabcmd/commands/datasources_and_workbooks/runschedule_command.py @@ -1,7 +1,7 @@ -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log from .datasources_and_workbooks_command import DatasourcesAndWorkbooks diff --git a/src/commands/extracts/__init__.py b/tabcmd/commands/extracts/__init__.py similarity index 100% rename from src/commands/extracts/__init__.py rename to tabcmd/commands/extracts/__init__.py diff --git a/src/commands/extracts/create_extracts_command.py b/tabcmd/commands/extracts/create_extracts_command.py similarity index 88% rename from src/commands/extracts/create_extracts_command.py rename to tabcmd/commands/extracts/create_extracts_command.py index da9a0b71..f7f93b72 100644 --- a/src/commands/extracts/create_extracts_command.py +++ b/tabcmd/commands/extracts/create_extracts_command.py @@ -1,11 +1,11 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.global_options import * -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class CreateExtracts(Server): diff --git a/src/commands/extracts/decrypt_extracts_command.py b/tabcmd/commands/extracts/decrypt_extracts_command.py similarity index 84% rename from src/commands/extracts/decrypt_extracts_command.py rename to tabcmd/commands/extracts/decrypt_extracts_command.py index 2f02ec7a..6a983a06 100644 --- a/src/commands/extracts/decrypt_extracts_command.py +++ b/tabcmd/commands/extracts/decrypt_extracts_command.py @@ -1,10 +1,10 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class DecryptExtracts(Server): diff --git a/src/commands/extracts/delete_extracts_command.py b/tabcmd/commands/extracts/delete_extracts_command.py similarity index 86% rename from src/commands/extracts/delete_extracts_command.py rename to tabcmd/commands/extracts/delete_extracts_command.py index 27a35aa6..400d7c7a 100644 --- a/src/commands/extracts/delete_extracts_command.py +++ b/tabcmd/commands/extracts/delete_extracts_command.py @@ -1,11 +1,11 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.global_options import * -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class DeleteExtracts(Server): diff --git a/src/commands/extracts/encrypt_extracts_command.py b/tabcmd/commands/extracts/encrypt_extracts_command.py similarity index 84% rename from src/commands/extracts/encrypt_extracts_command.py rename to tabcmd/commands/extracts/encrypt_extracts_command.py index 96d18422..2d3b7ae4 100644 --- a/src/commands/extracts/encrypt_extracts_command.py +++ b/tabcmd/commands/extracts/encrypt_extracts_command.py @@ -1,10 +1,10 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class EncryptExtracts(Server): diff --git a/src/commands/extracts/reencrypt_extracts_command.py b/tabcmd/commands/extracts/reencrypt_extracts_command.py similarity index 84% rename from src/commands/extracts/reencrypt_extracts_command.py rename to tabcmd/commands/extracts/reencrypt_extracts_command.py index 42021a10..66785bfe 100644 --- a/src/commands/extracts/reencrypt_extracts_command.py +++ b/tabcmd/commands/extracts/reencrypt_extracts_command.py @@ -1,10 +1,10 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class ReencryptExtracts(Server): diff --git a/src/commands/extracts/refresh_extracts_command.py b/tabcmd/commands/extracts/refresh_extracts_command.py similarity index 94% rename from src/commands/extracts/refresh_extracts_command.py rename to tabcmd/commands/extracts/refresh_extracts_command.py index 15245255..562554ba 100644 --- a/src/commands/extracts/refresh_extracts_command.py +++ b/tabcmd/commands/extracts/refresh_extracts_command.py @@ -1,12 +1,12 @@ import polling2 # type: ignore import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.global_options import * -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class RefreshExtracts(Server): diff --git a/src/commands/group/__init__.py b/tabcmd/commands/group/__init__.py similarity index 100% rename from src/commands/group/__init__.py rename to tabcmd/commands/group/__init__.py diff --git a/src/commands/group/create_group_command.py b/tabcmd/commands/group/create_group_command.py similarity index 84% rename from src/commands/group/create_group_command.py rename to tabcmd/commands/group/create_group_command.py index 9220b5b5..489bf4db 100644 --- a/src/commands/group/create_group_command.py +++ b/tabcmd/commands/group/create_group_command.py @@ -1,10 +1,10 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class CreateGroupCommand(Server): diff --git a/src/commands/group/delete_group_command.py b/tabcmd/commands/group/delete_group_command.py similarity index 83% rename from src/commands/group/delete_group_command.py rename to tabcmd/commands/group/delete_group_command.py index ef76584b..01a8a4b5 100644 --- a/src/commands/group/delete_group_command.py +++ b/tabcmd/commands/group/delete_group_command.py @@ -1,10 +1,10 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class DeleteGroupCommand(Server): diff --git a/src/commands/help/__init__.py b/tabcmd/commands/help/__init__.py similarity index 100% rename from src/commands/help/__init__.py rename to tabcmd/commands/help/__init__.py diff --git a/src/commands/help/help_command.py b/tabcmd/commands/help/help_command.py similarity index 94% rename from src/commands/help/help_command.py rename to tabcmd/commands/help/help_command.py index 2436cd21..6c5409d8 100644 --- a/src/commands/help/help_command.py +++ b/tabcmd/commands/help/help_command.py @@ -1,8 +1,8 @@ import argparse from typing import Any, List -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class HelpCommand: @@ -25,7 +25,7 @@ def run_command(args: argparse.Namespace): logger.debug(_("tabcmd.launching")) # delayed import, TODO fix cyclic imports - from src.execution.map_of_commands import CommandsMap + from tabcmd.execution.map_of_commands import CommandsMap all_commands: List[Any] = CommandsMap.commands_hash_map diff --git a/src/commands/project/__init__.py b/tabcmd/commands/project/__init__.py similarity index 100% rename from src/commands/project/__init__.py rename to tabcmd/commands/project/__init__.py diff --git a/src/commands/project/create_project_command.py b/tabcmd/commands/project/create_project_command.py similarity index 89% rename from src/commands/project/create_project_command.py rename to tabcmd/commands/project/create_project_command.py index 09697526..b1838b48 100644 --- a/src/commands/project/create_project_command.py +++ b/tabcmd/commands/project/create_project_command.py @@ -1,11 +1,11 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.global_options import * -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class CreateProjectCommand(Server): diff --git a/src/commands/project/delete_project_command.py b/tabcmd/commands/project/delete_project_command.py similarity index 85% rename from src/commands/project/delete_project_command.py rename to tabcmd/commands/project/delete_project_command.py index 7e05ae29..9ea30d8d 100644 --- a/src/commands/project/delete_project_command.py +++ b/tabcmd/commands/project/delete_project_command.py @@ -1,11 +1,11 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.global_options import * -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class DeleteProjectCommand(Server): diff --git a/src/commands/project/publish_samples_command.py b/tabcmd/commands/project/publish_samples_command.py similarity index 85% rename from src/commands/project/publish_samples_command.py rename to tabcmd/commands/project/publish_samples_command.py index ebf9cf35..907f8a5c 100644 --- a/src/commands/project/publish_samples_command.py +++ b/tabcmd/commands/project/publish_samples_command.py @@ -1,9 +1,9 @@ -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.global_options import * -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class PublishSamplesCommand(Server): diff --git a/src/commands/server.py b/tabcmd/commands/server.py similarity index 98% rename from src/commands/server.py rename to tabcmd/commands/server.py index 3bec44ec..38aac8ef 100644 --- a/src/commands/server.py +++ b/tabcmd/commands/server.py @@ -1,8 +1,8 @@ import os import tableauserverclient as TSC -from src.commands.constants import Errors -from src.execution.localize import _ +from tabcmd.commands.constants import Errors +from tabcmd.execution.localize import _ class Server: diff --git a/src/commands/site/__init__.py b/tabcmd/commands/site/__init__.py similarity index 100% rename from src/commands/site/__init__.py rename to tabcmd/commands/site/__init__.py diff --git a/src/commands/site/create_site_command.py b/tabcmd/commands/site/create_site_command.py similarity index 87% rename from src/commands/site/create_site_command.py rename to tabcmd/commands/site/create_site_command.py index ef44b56d..e8ca8c03 100644 --- a/src/commands/site/create_site_command.py +++ b/tabcmd/commands/site/create_site_command.py @@ -1,11 +1,11 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.global_options import * -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class CreateSiteCommand(Server): diff --git a/src/commands/site/delete_site_command.py b/tabcmd/commands/site/delete_site_command.py similarity index 80% rename from src/commands/site/delete_site_command.py rename to tabcmd/commands/site/delete_site_command.py index 2f9ea640..2d210c86 100644 --- a/src/commands/site/delete_site_command.py +++ b/tabcmd/commands/site/delete_site_command.py @@ -1,10 +1,10 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class DeleteSiteCommand(Server): diff --git a/src/commands/site/edit_site_command.py b/tabcmd/commands/site/edit_site_command.py similarity index 86% rename from src/commands/site/edit_site_command.py rename to tabcmd/commands/site/edit_site_command.py index a055a45f..cbd58f31 100644 --- a/src/commands/site/edit_site_command.py +++ b/tabcmd/commands/site/edit_site_command.py @@ -1,11 +1,11 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.global_options import * -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class EditSiteCommand(Server): diff --git a/src/commands/site/list_sites_command.py b/tabcmd/commands/site/list_sites_command.py similarity index 80% rename from src/commands/site/list_sites_command.py rename to tabcmd/commands/site/list_sites_command.py index 6709c488..418f6caf 100644 --- a/src/commands/site/list_sites_command.py +++ b/tabcmd/commands/site/list_sites_command.py @@ -1,11 +1,11 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.global_options import * -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class ListSiteCommand(Server): diff --git a/src/commands/user/__init__.py b/tabcmd/commands/user/__init__.py similarity index 100% rename from src/commands/user/__init__.py rename to tabcmd/commands/user/__init__.py diff --git a/src/commands/user/add_users_command.py b/tabcmd/commands/user/add_users_command.py similarity index 82% rename from src/commands/user/add_users_command.py rename to tabcmd/commands/user/add_users_command.py index ac2431d7..2d580ac3 100644 --- a/src/commands/user/add_users_command.py +++ b/tabcmd/commands/user/add_users_command.py @@ -1,7 +1,7 @@ -from src.commands.auth.session import Session -from src.execution.global_options import * -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log from .user_data import UserCommand diff --git a/src/commands/user/create_site_users.py b/tabcmd/commands/user/create_site_users.py similarity index 92% rename from src/commands/user/create_site_users.py rename to tabcmd/commands/user/create_site_users.py index b068da51..37b7eaaa 100644 --- a/src/commands/user/create_site_users.py +++ b/tabcmd/commands/user/create_site_users.py @@ -1,9 +1,9 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.execution.global_options import * -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log from .user_data import UserCommand diff --git a/src/commands/user/create_users_command.py b/tabcmd/commands/user/create_users_command.py similarity index 91% rename from src/commands/user/create_users_command.py rename to tabcmd/commands/user/create_users_command.py index 8de37544..2a44854f 100644 --- a/src/commands/user/create_users_command.py +++ b/tabcmd/commands/user/create_users_command.py @@ -1,10 +1,10 @@ import tableauserverclient as TSC -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.execution.global_options import * -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log from .user_data import UserCommand diff --git a/src/commands/user/delete_site_users_command.py b/tabcmd/commands/user/delete_site_users_command.py similarity index 86% rename from src/commands/user/delete_site_users_command.py rename to tabcmd/commands/user/delete_site_users_command.py index 4ef94bad..7f46a311 100644 --- a/src/commands/user/delete_site_users_command.py +++ b/tabcmd/commands/user/delete_site_users_command.py @@ -1,10 +1,10 @@ -from src.commands.auth.session import Session -from src.commands.constants import Errors -from src.commands.server import Server -from src.commands.user.user_data import UserCommand -from src.execution.global_options import * -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.commands.user.user_data import UserCommand +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log class DeleteSiteUsersCommand(Server): diff --git a/src/commands/user/remove_users_command.py b/tabcmd/commands/user/remove_users_command.py similarity index 83% rename from src/commands/user/remove_users_command.py rename to tabcmd/commands/user/remove_users_command.py index b4f102c2..bedbd394 100644 --- a/src/commands/user/remove_users_command.py +++ b/tabcmd/commands/user/remove_users_command.py @@ -1,7 +1,7 @@ -from src.commands.auth.session import Session -from src.execution.global_options import * -from src.execution.localize import _ -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log from .user_data import UserCommand diff --git a/src/commands/user/user_data.py b/tabcmd/commands/user/user_data.py similarity index 98% rename from src/commands/user/user_data.py rename to tabcmd/commands/user/user_data.py index dd5d9c53..ed7082c4 100644 --- a/src/commands/user/user_data.py +++ b/tabcmd/commands/user/user_data.py @@ -6,9 +6,9 @@ import tableauserverclient as TSC -from src.commands.constants import Errors -from src.commands.server import Server -from src.execution.localize import _ +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.localize import _ class Userdata: diff --git a/src/execution/__init__.py b/tabcmd/execution/__init__.py similarity index 100% rename from src/execution/__init__.py rename to tabcmd/execution/__init__.py diff --git a/src/execution/_version.py b/tabcmd/execution/_version.py similarity index 100% rename from src/execution/_version.py rename to tabcmd/execution/_version.py diff --git a/src/execution/global_options.py b/tabcmd/execution/global_options.py similarity index 100% rename from src/execution/global_options.py rename to tabcmd/execution/global_options.py diff --git a/src/execution/localize.py b/tabcmd/execution/localize.py similarity index 100% rename from src/execution/localize.py rename to tabcmd/execution/localize.py diff --git a/src/execution/logger_config.py b/tabcmd/execution/logger_config.py similarity index 100% rename from src/execution/logger_config.py rename to tabcmd/execution/logger_config.py diff --git a/tabcmd/execution/map_of_commands.py b/tabcmd/execution/map_of_commands.py new file mode 100644 index 00000000..87ff0a1d --- /dev/null +++ b/tabcmd/execution/map_of_commands.py @@ -0,0 +1,61 @@ +from tabcmd.commands.auth.login_command import * +from tabcmd.commands.auth.logout_command import * +from tabcmd.commands.datasources_and_workbooks.delete_command import * +from tabcmd.commands.datasources_and_workbooks.export_command import * +from tabcmd.commands.datasources_and_workbooks.get_url_command import * +from tabcmd.commands.datasources_and_workbooks.publish_command import * +from tabcmd.commands.extracts.create_extracts_command import * +from tabcmd.commands.extracts.decrypt_extracts_command import * +from tabcmd.commands.extracts.delete_extracts_command import * +from tabcmd.commands.extracts.encrypt_extracts_command import * +from tabcmd.commands.extracts.reencrypt_extracts_command import * +from tabcmd.commands.extracts.refresh_extracts_command import * +from tabcmd.commands.group.create_group_command import * +from tabcmd.commands.group.delete_group_command import * +from tabcmd.commands.help.help_command import * +from tabcmd.commands.project.create_project_command import * +from tabcmd.commands.project.delete_project_command import * +from tabcmd.commands.project.publish_samples_command import * +from tabcmd.commands.site.create_site_command import * +from tabcmd.commands.site.delete_site_command import * +from tabcmd.commands.site.edit_site_command import * +from tabcmd.commands.site.list_sites_command import * +from tabcmd.commands.user.add_users_command import * +from tabcmd.commands.user.create_site_users import * +from tabcmd.commands.user.delete_site_users_command import * + +# from tabcmd.commands.user.create_users import * +from tabcmd.commands.user.remove_users_command import * + + +class CommandsMap: + commands_hash_map = [ + # not yet implemented "createusers": ("createusers", CreateUserCommand, "Create users on the server"), + # run schedule + AddUserCommand, + CreateExtracts, + CreateGroupCommand, + CreateProjectCommand, + CreateSiteCommand, + CreateSiteUsersCommand, + DecryptExtracts, + DeleteCommand, + DeleteExtracts, + DeleteGroupCommand, + DeleteProjectCommand, + DeleteSiteCommand, + DeleteSiteUsersCommand, + EditSiteCommand, + EncryptExtracts, + ExportCommand, + GetUrl, + HelpCommand, + ListSiteCommand, + LoginCommand, + LogoutCommand, + PublishCommand, + PublishSamplesCommand, + ReencryptExtracts, + RefreshExtracts, + RemoveUserCommand, + ] diff --git a/src/execution/parent_parser.py b/tabcmd/execution/parent_parser.py similarity index 100% rename from src/execution/parent_parser.py rename to tabcmd/execution/parent_parser.py diff --git a/src/execution/tabcmd_controller.py b/tabcmd/execution/tabcmd_controller.py similarity index 100% rename from src/execution/tabcmd_controller.py rename to tabcmd/execution/tabcmd_controller.py diff --git a/src/locales/de/LC_MESSAGES/.gitkeep b/tabcmd/locales/de/LC_MESSAGES/.gitkeep similarity index 100% rename from src/locales/de/LC_MESSAGES/.gitkeep rename to tabcmd/locales/de/LC_MESSAGES/.gitkeep diff --git a/src/locales/de/LC_MESSAGES/de.po b/tabcmd/locales/de/LC_MESSAGES/de.po similarity index 100% rename from src/locales/de/LC_MESSAGES/de.po rename to tabcmd/locales/de/LC_MESSAGES/de.po diff --git a/src/locales/de/LC_MESSAGES/shared_wg_de.po b/tabcmd/locales/de/LC_MESSAGES/shared_wg_de.po similarity index 100% rename from src/locales/de/LC_MESSAGES/shared_wg_de.po rename to tabcmd/locales/de/LC_MESSAGES/shared_wg_de.po diff --git a/src/locales/de/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/de/LC_MESSAGES/tabcmd.mo similarity index 100% rename from src/locales/de/LC_MESSAGES/tabcmd.mo rename to tabcmd/locales/de/LC_MESSAGES/tabcmd.mo diff --git a/src/locales/de/LC_MESSAGES/tabcmd.po b/tabcmd/locales/de/LC_MESSAGES/tabcmd.po similarity index 100% rename from src/locales/de/LC_MESSAGES/tabcmd.po rename to tabcmd/locales/de/LC_MESSAGES/tabcmd.po diff --git a/src/locales/de/LC_MESSAGES/tabcmd_messages_de.po b/tabcmd/locales/de/LC_MESSAGES/tabcmd_messages_de.po similarity index 100% rename from src/locales/de/LC_MESSAGES/tabcmd_messages_de.po rename to tabcmd/locales/de/LC_MESSAGES/tabcmd_messages_de.po diff --git a/src/locales/de/shared_wg_de.properties b/tabcmd/locales/de/shared_wg_de.properties similarity index 100% rename from src/locales/de/shared_wg_de.properties rename to tabcmd/locales/de/shared_wg_de.properties diff --git a/src/locales/de/tabcmd_messages_de.properties b/tabcmd/locales/de/tabcmd_messages_de.properties similarity index 100% rename from src/locales/de/tabcmd_messages_de.properties rename to tabcmd/locales/de/tabcmd_messages_de.properties diff --git a/src/locales/en/LC_MESSAGES/en-US.po b/tabcmd/locales/en/LC_MESSAGES/en-US.po similarity index 100% rename from src/locales/en/LC_MESSAGES/en-US.po rename to tabcmd/locales/en/LC_MESSAGES/en-US.po diff --git a/src/locales/en/LC_MESSAGES/shared_wg_en.po b/tabcmd/locales/en/LC_MESSAGES/shared_wg_en.po similarity index 100% rename from src/locales/en/LC_MESSAGES/shared_wg_en.po rename to tabcmd/locales/en/LC_MESSAGES/shared_wg_en.po diff --git a/src/locales/en/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/en/LC_MESSAGES/tabcmd.mo similarity index 100% rename from src/locales/en/LC_MESSAGES/tabcmd.mo rename to tabcmd/locales/en/LC_MESSAGES/tabcmd.mo diff --git a/src/locales/en/LC_MESSAGES/tabcmd.po b/tabcmd/locales/en/LC_MESSAGES/tabcmd.po similarity index 100% rename from src/locales/en/LC_MESSAGES/tabcmd.po rename to tabcmd/locales/en/LC_MESSAGES/tabcmd.po diff --git a/src/locales/en/LC_MESSAGES/tabcmd_messages_en.po b/tabcmd/locales/en/LC_MESSAGES/tabcmd_messages_en.po similarity index 100% rename from src/locales/en/LC_MESSAGES/tabcmd_messages_en.po rename to tabcmd/locales/en/LC_MESSAGES/tabcmd_messages_en.po diff --git a/src/locales/en/shared_wg_en.properties b/tabcmd/locales/en/shared_wg_en.properties similarity index 100% rename from src/locales/en/shared_wg_en.properties rename to tabcmd/locales/en/shared_wg_en.properties diff --git a/src/locales/en/tabcmd_messages_en.properties b/tabcmd/locales/en/tabcmd_messages_en.properties similarity index 100% rename from src/locales/en/tabcmd_messages_en.properties rename to tabcmd/locales/en/tabcmd_messages_en.properties diff --git a/src/locales/es/LC_MESSAGES/.gitkeep b/tabcmd/locales/es/LC_MESSAGES/.gitkeep similarity index 100% rename from src/locales/es/LC_MESSAGES/.gitkeep rename to tabcmd/locales/es/LC_MESSAGES/.gitkeep diff --git a/src/locales/es/LC_MESSAGES/es.po b/tabcmd/locales/es/LC_MESSAGES/es.po similarity index 100% rename from src/locales/es/LC_MESSAGES/es.po rename to tabcmd/locales/es/LC_MESSAGES/es.po diff --git a/src/locales/es/LC_MESSAGES/shared_wg_es.po b/tabcmd/locales/es/LC_MESSAGES/shared_wg_es.po similarity index 100% rename from src/locales/es/LC_MESSAGES/shared_wg_es.po rename to tabcmd/locales/es/LC_MESSAGES/shared_wg_es.po diff --git a/src/locales/es/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/es/LC_MESSAGES/tabcmd.mo similarity index 100% rename from src/locales/es/LC_MESSAGES/tabcmd.mo rename to tabcmd/locales/es/LC_MESSAGES/tabcmd.mo diff --git a/src/locales/es/LC_MESSAGES/tabcmd.po b/tabcmd/locales/es/LC_MESSAGES/tabcmd.po similarity index 100% rename from src/locales/es/LC_MESSAGES/tabcmd.po rename to tabcmd/locales/es/LC_MESSAGES/tabcmd.po diff --git a/src/locales/es/LC_MESSAGES/tabcmd_messages_es.po b/tabcmd/locales/es/LC_MESSAGES/tabcmd_messages_es.po similarity index 100% rename from src/locales/es/LC_MESSAGES/tabcmd_messages_es.po rename to tabcmd/locales/es/LC_MESSAGES/tabcmd_messages_es.po diff --git a/src/locales/es/shared_wg_es.properties b/tabcmd/locales/es/shared_wg_es.properties similarity index 100% rename from src/locales/es/shared_wg_es.properties rename to tabcmd/locales/es/shared_wg_es.properties diff --git a/src/locales/es/tabcmd_messages_es.properties b/tabcmd/locales/es/tabcmd_messages_es.properties similarity index 100% rename from src/locales/es/tabcmd_messages_es.properties rename to tabcmd/locales/es/tabcmd_messages_es.properties diff --git a/src/locales/fr/LC_MESSAGES/.gitkeep b/tabcmd/locales/fr/LC_MESSAGES/.gitkeep similarity index 100% rename from src/locales/fr/LC_MESSAGES/.gitkeep rename to tabcmd/locales/fr/LC_MESSAGES/.gitkeep diff --git a/src/locales/fr/LC_MESSAGES/fr.po b/tabcmd/locales/fr/LC_MESSAGES/fr.po similarity index 100% rename from src/locales/fr/LC_MESSAGES/fr.po rename to tabcmd/locales/fr/LC_MESSAGES/fr.po diff --git a/src/locales/fr/LC_MESSAGES/shared_wg_fr.po b/tabcmd/locales/fr/LC_MESSAGES/shared_wg_fr.po similarity index 100% rename from src/locales/fr/LC_MESSAGES/shared_wg_fr.po rename to tabcmd/locales/fr/LC_MESSAGES/shared_wg_fr.po diff --git a/src/locales/fr/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/fr/LC_MESSAGES/tabcmd.mo similarity index 100% rename from src/locales/fr/LC_MESSAGES/tabcmd.mo rename to tabcmd/locales/fr/LC_MESSAGES/tabcmd.mo diff --git a/src/locales/fr/LC_MESSAGES/tabcmd.po b/tabcmd/locales/fr/LC_MESSAGES/tabcmd.po similarity index 100% rename from src/locales/fr/LC_MESSAGES/tabcmd.po rename to tabcmd/locales/fr/LC_MESSAGES/tabcmd.po diff --git a/src/locales/fr/LC_MESSAGES/tabcmd_messages_fr.po b/tabcmd/locales/fr/LC_MESSAGES/tabcmd_messages_fr.po similarity index 100% rename from src/locales/fr/LC_MESSAGES/tabcmd_messages_fr.po rename to tabcmd/locales/fr/LC_MESSAGES/tabcmd_messages_fr.po diff --git a/src/locales/fr/shared_wg_fr.properties b/tabcmd/locales/fr/shared_wg_fr.properties similarity index 100% rename from src/locales/fr/shared_wg_fr.properties rename to tabcmd/locales/fr/shared_wg_fr.properties diff --git a/src/locales/fr/tabcmd_messages_fr.properties b/tabcmd/locales/fr/tabcmd_messages_fr.properties similarity index 100% rename from src/locales/fr/tabcmd_messages_fr.properties rename to tabcmd/locales/fr/tabcmd_messages_fr.properties diff --git a/src/locales/ga/LC_MESSAGES/.gitkeep b/tabcmd/locales/ga/LC_MESSAGES/.gitkeep similarity index 100% rename from src/locales/ga/LC_MESSAGES/.gitkeep rename to tabcmd/locales/ga/LC_MESSAGES/.gitkeep diff --git a/src/locales/ga/LC_MESSAGES/ga.po b/tabcmd/locales/ga/LC_MESSAGES/ga.po similarity index 100% rename from src/locales/ga/LC_MESSAGES/ga.po rename to tabcmd/locales/ga/LC_MESSAGES/ga.po diff --git a/src/locales/ga/LC_MESSAGES/shared_wg_ga.po b/tabcmd/locales/ga/LC_MESSAGES/shared_wg_ga.po similarity index 100% rename from src/locales/ga/LC_MESSAGES/shared_wg_ga.po rename to tabcmd/locales/ga/LC_MESSAGES/shared_wg_ga.po diff --git a/src/locales/ga/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/ga/LC_MESSAGES/tabcmd.mo similarity index 100% rename from src/locales/ga/LC_MESSAGES/tabcmd.mo rename to tabcmd/locales/ga/LC_MESSAGES/tabcmd.mo diff --git a/src/locales/ga/LC_MESSAGES/tabcmd.po b/tabcmd/locales/ga/LC_MESSAGES/tabcmd.po similarity index 100% rename from src/locales/ga/LC_MESSAGES/tabcmd.po rename to tabcmd/locales/ga/LC_MESSAGES/tabcmd.po diff --git a/src/locales/ga/LC_MESSAGES/tabcmd_messages_ga.po b/tabcmd/locales/ga/LC_MESSAGES/tabcmd_messages_ga.po similarity index 100% rename from src/locales/ga/LC_MESSAGES/tabcmd_messages_ga.po rename to tabcmd/locales/ga/LC_MESSAGES/tabcmd_messages_ga.po diff --git a/src/locales/ga/shared_wg_ga.properties b/tabcmd/locales/ga/shared_wg_ga.properties similarity index 100% rename from src/locales/ga/shared_wg_ga.properties rename to tabcmd/locales/ga/shared_wg_ga.properties diff --git a/src/locales/ga/tabcmd_messages_ga.properties b/tabcmd/locales/ga/tabcmd_messages_ga.properties similarity index 100% rename from src/locales/ga/tabcmd_messages_ga.properties rename to tabcmd/locales/ga/tabcmd_messages_ga.properties diff --git a/src/locales/it/LC_MESSAGES/.gitkeep b/tabcmd/locales/it/LC_MESSAGES/.gitkeep similarity index 100% rename from src/locales/it/LC_MESSAGES/.gitkeep rename to tabcmd/locales/it/LC_MESSAGES/.gitkeep diff --git a/src/locales/it/LC_MESSAGES/it.po b/tabcmd/locales/it/LC_MESSAGES/it.po similarity index 100% rename from src/locales/it/LC_MESSAGES/it.po rename to tabcmd/locales/it/LC_MESSAGES/it.po diff --git a/src/locales/it/LC_MESSAGES/shared_wg_it.po b/tabcmd/locales/it/LC_MESSAGES/shared_wg_it.po similarity index 100% rename from src/locales/it/LC_MESSAGES/shared_wg_it.po rename to tabcmd/locales/it/LC_MESSAGES/shared_wg_it.po diff --git a/src/locales/it/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/it/LC_MESSAGES/tabcmd.mo similarity index 100% rename from src/locales/it/LC_MESSAGES/tabcmd.mo rename to tabcmd/locales/it/LC_MESSAGES/tabcmd.mo diff --git a/src/locales/it/LC_MESSAGES/tabcmd.po b/tabcmd/locales/it/LC_MESSAGES/tabcmd.po similarity index 100% rename from src/locales/it/LC_MESSAGES/tabcmd.po rename to tabcmd/locales/it/LC_MESSAGES/tabcmd.po diff --git a/src/locales/it/LC_MESSAGES/tabcmd_messages_it.po b/tabcmd/locales/it/LC_MESSAGES/tabcmd_messages_it.po similarity index 100% rename from src/locales/it/LC_MESSAGES/tabcmd_messages_it.po rename to tabcmd/locales/it/LC_MESSAGES/tabcmd_messages_it.po diff --git a/src/locales/it/shared_wg_it.properties b/tabcmd/locales/it/shared_wg_it.properties similarity index 100% rename from src/locales/it/shared_wg_it.properties rename to tabcmd/locales/it/shared_wg_it.properties diff --git a/src/locales/it/tabcmd_messages_it.properties b/tabcmd/locales/it/tabcmd_messages_it.properties similarity index 100% rename from src/locales/it/tabcmd_messages_it.properties rename to tabcmd/locales/it/tabcmd_messages_it.properties diff --git a/src/locales/ja/LC_MESSAGES/.gitkeep b/tabcmd/locales/ja/LC_MESSAGES/.gitkeep similarity index 100% rename from src/locales/ja/LC_MESSAGES/.gitkeep rename to tabcmd/locales/ja/LC_MESSAGES/.gitkeep diff --git a/src/locales/ja/LC_MESSAGES/ja.po b/tabcmd/locales/ja/LC_MESSAGES/ja.po similarity index 100% rename from src/locales/ja/LC_MESSAGES/ja.po rename to tabcmd/locales/ja/LC_MESSAGES/ja.po diff --git a/src/locales/ja/LC_MESSAGES/shared_wg_ja.po b/tabcmd/locales/ja/LC_MESSAGES/shared_wg_ja.po similarity index 100% rename from src/locales/ja/LC_MESSAGES/shared_wg_ja.po rename to tabcmd/locales/ja/LC_MESSAGES/shared_wg_ja.po diff --git a/src/locales/ja/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/ja/LC_MESSAGES/tabcmd.mo similarity index 100% rename from src/locales/ja/LC_MESSAGES/tabcmd.mo rename to tabcmd/locales/ja/LC_MESSAGES/tabcmd.mo diff --git a/src/locales/ja/LC_MESSAGES/tabcmd.po b/tabcmd/locales/ja/LC_MESSAGES/tabcmd.po similarity index 100% rename from src/locales/ja/LC_MESSAGES/tabcmd.po rename to tabcmd/locales/ja/LC_MESSAGES/tabcmd.po diff --git a/src/locales/ja/LC_MESSAGES/tabcmd_messages_ja.po b/tabcmd/locales/ja/LC_MESSAGES/tabcmd_messages_ja.po similarity index 100% rename from src/locales/ja/LC_MESSAGES/tabcmd_messages_ja.po rename to tabcmd/locales/ja/LC_MESSAGES/tabcmd_messages_ja.po diff --git a/src/locales/ja/shared_wg_ja.properties b/tabcmd/locales/ja/shared_wg_ja.properties similarity index 100% rename from src/locales/ja/shared_wg_ja.properties rename to tabcmd/locales/ja/shared_wg_ja.properties diff --git a/src/locales/ja/tabcmd_messages_ja.properties b/tabcmd/locales/ja/tabcmd_messages_ja.properties similarity index 100% rename from src/locales/ja/tabcmd_messages_ja.properties rename to tabcmd/locales/ja/tabcmd_messages_ja.properties diff --git a/src/locales/ko/LC_MESSAGES/.gitkeep b/tabcmd/locales/ko/LC_MESSAGES/.gitkeep similarity index 100% rename from src/locales/ko/LC_MESSAGES/.gitkeep rename to tabcmd/locales/ko/LC_MESSAGES/.gitkeep diff --git a/src/locales/ko/LC_MESSAGES/ko.po b/tabcmd/locales/ko/LC_MESSAGES/ko.po similarity index 100% rename from src/locales/ko/LC_MESSAGES/ko.po rename to tabcmd/locales/ko/LC_MESSAGES/ko.po diff --git a/src/locales/ko/LC_MESSAGES/shared_wg_ko.po b/tabcmd/locales/ko/LC_MESSAGES/shared_wg_ko.po similarity index 100% rename from src/locales/ko/LC_MESSAGES/shared_wg_ko.po rename to tabcmd/locales/ko/LC_MESSAGES/shared_wg_ko.po diff --git a/src/locales/ko/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/ko/LC_MESSAGES/tabcmd.mo similarity index 100% rename from src/locales/ko/LC_MESSAGES/tabcmd.mo rename to tabcmd/locales/ko/LC_MESSAGES/tabcmd.mo diff --git a/src/locales/ko/LC_MESSAGES/tabcmd.po b/tabcmd/locales/ko/LC_MESSAGES/tabcmd.po similarity index 100% rename from src/locales/ko/LC_MESSAGES/tabcmd.po rename to tabcmd/locales/ko/LC_MESSAGES/tabcmd.po diff --git a/src/locales/ko/LC_MESSAGES/tabcmd_messages_ko.po b/tabcmd/locales/ko/LC_MESSAGES/tabcmd_messages_ko.po similarity index 100% rename from src/locales/ko/LC_MESSAGES/tabcmd_messages_ko.po rename to tabcmd/locales/ko/LC_MESSAGES/tabcmd_messages_ko.po diff --git a/src/locales/ko/shared_wg_ko.properties b/tabcmd/locales/ko/shared_wg_ko.properties similarity index 100% rename from src/locales/ko/shared_wg_ko.properties rename to tabcmd/locales/ko/shared_wg_ko.properties diff --git a/src/locales/ko/tabcmd_messages_ko.properties b/tabcmd/locales/ko/tabcmd_messages_ko.properties similarity index 100% rename from src/locales/ko/tabcmd_messages_ko.properties rename to tabcmd/locales/ko/tabcmd_messages_ko.properties diff --git a/src/locales/pt/LC_MESSAGES/.gitkeep b/tabcmd/locales/pt/LC_MESSAGES/.gitkeep similarity index 100% rename from src/locales/pt/LC_MESSAGES/.gitkeep rename to tabcmd/locales/pt/LC_MESSAGES/.gitkeep diff --git a/src/locales/pt/LC_MESSAGES/pt.po b/tabcmd/locales/pt/LC_MESSAGES/pt.po similarity index 100% rename from src/locales/pt/LC_MESSAGES/pt.po rename to tabcmd/locales/pt/LC_MESSAGES/pt.po diff --git a/src/locales/pt/LC_MESSAGES/shared_wg_pt.po b/tabcmd/locales/pt/LC_MESSAGES/shared_wg_pt.po similarity index 100% rename from src/locales/pt/LC_MESSAGES/shared_wg_pt.po rename to tabcmd/locales/pt/LC_MESSAGES/shared_wg_pt.po diff --git a/src/locales/pt/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/pt/LC_MESSAGES/tabcmd.mo similarity index 100% rename from src/locales/pt/LC_MESSAGES/tabcmd.mo rename to tabcmd/locales/pt/LC_MESSAGES/tabcmd.mo diff --git a/src/locales/pt/LC_MESSAGES/tabcmd.po b/tabcmd/locales/pt/LC_MESSAGES/tabcmd.po similarity index 100% rename from src/locales/pt/LC_MESSAGES/tabcmd.po rename to tabcmd/locales/pt/LC_MESSAGES/tabcmd.po diff --git a/src/locales/pt/LC_MESSAGES/tabcmd_messages_pt.po b/tabcmd/locales/pt/LC_MESSAGES/tabcmd_messages_pt.po similarity index 100% rename from src/locales/pt/LC_MESSAGES/tabcmd_messages_pt.po rename to tabcmd/locales/pt/LC_MESSAGES/tabcmd_messages_pt.po diff --git a/src/locales/pt/shared_wg_pt.properties b/tabcmd/locales/pt/shared_wg_pt.properties similarity index 100% rename from src/locales/pt/shared_wg_pt.properties rename to tabcmd/locales/pt/shared_wg_pt.properties diff --git a/src/locales/pt/tabcmd_messages_pt.properties b/tabcmd/locales/pt/tabcmd_messages_pt.properties similarity index 100% rename from src/locales/pt/tabcmd_messages_pt.properties rename to tabcmd/locales/pt/tabcmd_messages_pt.properties diff --git a/src/locales/sv/LC_MESSAGES/.gitkeep b/tabcmd/locales/sv/LC_MESSAGES/.gitkeep similarity index 100% rename from src/locales/sv/LC_MESSAGES/.gitkeep rename to tabcmd/locales/sv/LC_MESSAGES/.gitkeep diff --git a/src/locales/sv/LC_MESSAGES/shared_wg_sv.po b/tabcmd/locales/sv/LC_MESSAGES/shared_wg_sv.po similarity index 100% rename from src/locales/sv/LC_MESSAGES/shared_wg_sv.po rename to tabcmd/locales/sv/LC_MESSAGES/shared_wg_sv.po diff --git a/src/locales/sv/LC_MESSAGES/sv.po b/tabcmd/locales/sv/LC_MESSAGES/sv.po similarity index 100% rename from src/locales/sv/LC_MESSAGES/sv.po rename to tabcmd/locales/sv/LC_MESSAGES/sv.po diff --git a/src/locales/sv/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/sv/LC_MESSAGES/tabcmd.mo similarity index 100% rename from src/locales/sv/LC_MESSAGES/tabcmd.mo rename to tabcmd/locales/sv/LC_MESSAGES/tabcmd.mo diff --git a/src/locales/sv/LC_MESSAGES/tabcmd.po b/tabcmd/locales/sv/LC_MESSAGES/tabcmd.po similarity index 100% rename from src/locales/sv/LC_MESSAGES/tabcmd.po rename to tabcmd/locales/sv/LC_MESSAGES/tabcmd.po diff --git a/src/locales/sv/LC_MESSAGES/tabcmd_messages_sv.po b/tabcmd/locales/sv/LC_MESSAGES/tabcmd_messages_sv.po similarity index 100% rename from src/locales/sv/LC_MESSAGES/tabcmd_messages_sv.po rename to tabcmd/locales/sv/LC_MESSAGES/tabcmd_messages_sv.po diff --git a/src/locales/sv/shared_wg_sv.properties b/tabcmd/locales/sv/shared_wg_sv.properties similarity index 100% rename from src/locales/sv/shared_wg_sv.properties rename to tabcmd/locales/sv/shared_wg_sv.properties diff --git a/src/locales/sv/tabcmd_messages_sv.properties b/tabcmd/locales/sv/tabcmd_messages_sv.properties similarity index 100% rename from src/locales/sv/tabcmd_messages_sv.properties rename to tabcmd/locales/sv/tabcmd_messages_sv.properties diff --git a/src/locales/zh/LC_MESSAGES/.gitkeep b/tabcmd/locales/zh/LC_MESSAGES/.gitkeep similarity index 100% rename from src/locales/zh/LC_MESSAGES/.gitkeep rename to tabcmd/locales/zh/LC_MESSAGES/.gitkeep diff --git a/src/locales/zh/LC_MESSAGES/shared_wg_zh.po b/tabcmd/locales/zh/LC_MESSAGES/shared_wg_zh.po similarity index 100% rename from src/locales/zh/LC_MESSAGES/shared_wg_zh.po rename to tabcmd/locales/zh/LC_MESSAGES/shared_wg_zh.po diff --git a/src/locales/zh/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/zh/LC_MESSAGES/tabcmd.mo similarity index 100% rename from src/locales/zh/LC_MESSAGES/tabcmd.mo rename to tabcmd/locales/zh/LC_MESSAGES/tabcmd.mo diff --git a/src/locales/zh/LC_MESSAGES/tabcmd.po b/tabcmd/locales/zh/LC_MESSAGES/tabcmd.po similarity index 100% rename from src/locales/zh/LC_MESSAGES/tabcmd.po rename to tabcmd/locales/zh/LC_MESSAGES/tabcmd.po diff --git a/src/locales/zh/LC_MESSAGES/tabcmd_messages_zh.po b/tabcmd/locales/zh/LC_MESSAGES/tabcmd_messages_zh.po similarity index 100% rename from src/locales/zh/LC_MESSAGES/tabcmd_messages_zh.po rename to tabcmd/locales/zh/LC_MESSAGES/tabcmd_messages_zh.po diff --git a/src/locales/zh/LC_MESSAGES/zh.po b/tabcmd/locales/zh/LC_MESSAGES/zh.po similarity index 100% rename from src/locales/zh/LC_MESSAGES/zh.po rename to tabcmd/locales/zh/LC_MESSAGES/zh.po diff --git a/src/locales/zh/shared_wg_zh.properties b/tabcmd/locales/zh/shared_wg_zh.properties similarity index 100% rename from src/locales/zh/shared_wg_zh.properties rename to tabcmd/locales/zh/shared_wg_zh.properties diff --git a/src/locales/zh/tabcmd_messages_zh.properties b/tabcmd/locales/zh/tabcmd_messages_zh.properties similarity index 100% rename from src/locales/zh/tabcmd_messages_zh.properties rename to tabcmd/locales/zh/tabcmd_messages_zh.properties diff --git a/src/tabcmd.py b/tabcmd/tabcmd.py similarity index 90% rename from src/tabcmd.py rename to tabcmd/tabcmd.py index 72e53d40..75951897 100644 --- a/src/tabcmd.py +++ b/tabcmd/tabcmd.py @@ -1,6 +1,6 @@ import sys -from src.execution.tabcmd_controller import TabcmdController +from tabcmd.execution.tabcmd_controller import TabcmdController def main(): diff --git a/tests/commands/test_execution.py b/tests/commands/test_execution.py index 7ed589f2..f737829c 100644 --- a/tests/commands/test_execution.py +++ b/tests/commands/test_execution.py @@ -2,8 +2,8 @@ import sys import unittest import mock -from src.execution.logger_config import * -from src.execution.tabcmd_controller import TabcmdController +from tabcmd.execution.logger_config import * +from tabcmd.execution.tabcmd_controller import TabcmdController class ExecutionTests(unittest.TestCase): diff --git a/tests/commands/test_geturl_utils.py b/tests/commands/test_geturl_utils.py index 3f006aa7..2430bf28 100644 --- a/tests/commands/test_geturl_utils.py +++ b/tests/commands/test_geturl_utils.py @@ -1,8 +1,8 @@ import unittest from unittest import mock -from src.commands.datasources_and_workbooks.get_url_command import * -from src.commands.datasources_and_workbooks.export_command import * -from src.commands.server import Server +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() diff --git a/tests/commands/test_localize.py b/tests/commands/test_localize.py index 2e316ed3..90bb4851 100644 --- a/tests/commands/test_localize.py +++ b/tests/commands/test_localize.py @@ -1,6 +1,6 @@ import gettext import unittest -from src.execution.localize import set_client_locale +from tabcmd.execution.localize import set_client_locale class LocaleTests(unittest.TestCase): diff --git a/tests/commands/test_projects_utils.py b/tests/commands/test_projects_utils.py index 3a36ff3d..61799bd7 100644 --- a/tests/commands/test_projects_utils.py +++ b/tests/commands/test_projects_utils.py @@ -1,8 +1,8 @@ import unittest from unittest import mock -from src.commands.server import Server -from src.execution.logger_config import log +from tabcmd.commands.server import Server +from tabcmd.execution.logger_config import log fake_item = mock.MagicMock() fake_item.name = "fake-name" diff --git a/tests/commands/test_run_commands.py b/tests/commands/test_run_commands.py index f654598b..4ab980c3 100644 --- a/tests/commands/test_run_commands.py +++ b/tests/commands/test_run_commands.py @@ -3,15 +3,15 @@ from unittest.mock import * import tableauserverclient as TSC -from src.commands.auth import login_command, logout_command -from src.commands.datasources_and_workbooks import ( +from tabcmd.commands.auth import login_command, logout_command +from tabcmd.commands.datasources_and_workbooks import ( delete_command, export_command, get_url_command, publish_command, runschedule_command, ) -from src.commands.extracts import ( +from tabcmd.commands.extracts import ( create_extracts_command, delete_extracts_command, decrypt_extracts_command, @@ -19,16 +19,16 @@ reencrypt_extracts_command, refresh_extracts_command, ) -from src.commands.group import create_group_command, delete_group_command -from src.commands.help import help_command -from src.commands.project import create_project_command, delete_project_command, publish_samples_command -from src.commands.site import ( +from tabcmd.commands.group import create_group_command, delete_group_command +from tabcmd.commands.help import help_command +from tabcmd.commands.project import create_project_command, delete_project_command, publish_samples_command +from tabcmd.commands.site import ( create_site_command, delete_site_command, edit_site_command, list_sites_command, ) -from src.commands.user import ( +from tabcmd.commands.user import ( add_users_command, create_site_users, create_users_command, @@ -63,7 +63,7 @@ @patch("tableauserverclient.Server") -@patch("src.commands.auth.session.Session.create_session") +@patch("tabcmd.commands.auth.session.Session.create_session") class RunCommandsTest(unittest.TestCase): @staticmethod def _set_up_session(mock_session, mock_server): @@ -79,7 +79,7 @@ def test_login(self, mock_session, mock_server): login_command.LoginCommand.run_command(mock_args) mock_session.assert_called_with(mock_args) - @patch("src.commands.auth.session.Session.end_session_and_clear_data") + @patch("tabcmd.commands.auth.session.Session.end_session_and_clear_data") def test_logout(self, mock_end_session, mock_create_session, mock_server): logout_command.LogoutCommand.run_command(mock_args) mock_create_session.assert_not_called() diff --git a/tests/commands/test_server_handler.py b/tests/commands/test_server_handler.py index b7ca74c5..5b4088e4 100644 --- a/tests/commands/test_server_handler.py +++ b/tests/commands/test_server_handler.py @@ -1,6 +1,6 @@ import unittest import logging -from src.commands.constants import Errors +from tabcmd.commands.constants import Errors # TODO add checks that the logger was called? diff --git a/tests/commands/test_session.py b/tests/commands/test_session.py index 64ea26a9..ebcf770b 100644 --- a/tests/commands/test_session.py +++ b/tests/commands/test_session.py @@ -3,7 +3,7 @@ from unittest import mock from unittest.mock import patch, mock_open -from src.commands.auth.session import Session +from tabcmd.commands.auth.session import Session import os args_to_mock = Namespace( diff --git a/tests/commands/test_user_utils.py b/tests/commands/test_user_utils.py index c6f7e76e..b22f7ff1 100644 --- a/tests/commands/test_user_utils.py +++ b/tests/commands/test_user_utils.py @@ -1,7 +1,7 @@ import unittest from unittest.mock import * -from src.commands.user.user_data import UserCommand, Userdata -from src.execution.logger_config import log +from tabcmd.commands.user.user_data import UserCommand, Userdata +from tabcmd.execution.logger_config import log from typing import List import io diff --git a/tests/e2e/tests_integration.py b/tests/e2e/tests_integration.py index 8ad6ee3e..d41070a1 100644 --- a/tests/e2e/tests_integration.py +++ b/tests/e2e/tests_integration.py @@ -2,9 +2,9 @@ import logging import pytest import unittest -from src.commands.auth.session import Session -from src.commands.server import Server -from src.execution.logger_config import log +from tabcmd.commands.auth.session import Session +from tabcmd.commands.server import Server +from tabcmd.execution.logger_config import log try: diff --git a/tests/parsers/common_setup.py b/tests/parsers/common_setup.py index 64a7d6a8..6530998b 100644 --- a/tests/parsers/common_setup.py +++ b/tests/parsers/common_setup.py @@ -1,4 +1,4 @@ -from src.execution import parent_parser +from tabcmd.execution import parent_parser from collections import namedtuple diff --git a/tests/parsers/test_login_parser.py b/tests/parsers/test_login_parser.py index 7369ed37..2276ef45 100644 --- a/tests/parsers/test_login_parser.py +++ b/tests/parsers/test_login_parser.py @@ -1,7 +1,7 @@ import unittest from unittest import mock -from src.commands.auth.login_command import LoginCommand +from tabcmd.commands.auth.login_command import LoginCommand from .common_setup import * commandname = "login" diff --git a/tests/parsers/test_logout_parser.py b/tests/parsers/test_logout_parser.py index 7e41fba3..5d59eab7 100644 --- a/tests/parsers/test_logout_parser.py +++ b/tests/parsers/test_logout_parser.py @@ -2,7 +2,7 @@ import unittest from unittest import mock -from src.commands.auth.logout_command import LogoutCommand +from tabcmd.commands.auth.logout_command import LogoutCommand from .common_setup import * commandname = "logout" diff --git a/tests/parsers/test_parser_add_user.py b/tests/parsers/test_parser_add_user.py index 62e1902e..fe229bce 100644 --- a/tests/parsers/test_parser_add_user.py +++ b/tests/parsers/test_parser_add_user.py @@ -1,7 +1,7 @@ import unittest from unittest import mock -from src.commands.user.add_users_command import AddUserCommand +from tabcmd.commands.user.add_users_command import AddUserCommand from .common_setup import * commandname = "addusers" diff --git a/tests/parsers/test_parser_create_extracts.py b/tests/parsers/test_parser_create_extracts.py index 6f7988f8..8a6d3f49 100644 --- a/tests/parsers/test_parser_create_extracts.py +++ b/tests/parsers/test_parser_create_extracts.py @@ -1,6 +1,6 @@ import unittest -from src.commands.extracts.create_extracts_command import CreateExtracts +from tabcmd.commands.extracts.create_extracts_command import CreateExtracts from .common_setup import * commandname = "createextracts" diff --git a/tests/parsers/test_parser_create_group.py b/tests/parsers/test_parser_create_group.py index 6790aaf2..de05934d 100644 --- a/tests/parsers/test_parser_create_group.py +++ b/tests/parsers/test_parser_create_group.py @@ -1,6 +1,6 @@ import unittest -from src.commands.group.create_group_command import CreateGroupCommand +from tabcmd.commands.group.create_group_command import CreateGroupCommand from .common_setup import * commandname = "creategroup" diff --git a/tests/parsers/test_parser_create_project.py b/tests/parsers/test_parser_create_project.py index e89ac0d2..b02319c6 100644 --- a/tests/parsers/test_parser_create_project.py +++ b/tests/parsers/test_parser_create_project.py @@ -1,6 +1,6 @@ import unittest -from src.commands.project.create_project_command import CreateProjectCommand +from tabcmd.commands.project.create_project_command import CreateProjectCommand from .common_setup import * commandname = "createproject" diff --git a/tests/parsers/test_parser_create_site.py b/tests/parsers/test_parser_create_site.py index 68f40b7d..ebe08813 100644 --- a/tests/parsers/test_parser_create_site.py +++ b/tests/parsers/test_parser_create_site.py @@ -1,6 +1,6 @@ import unittest -from src.commands.site.create_site_command import CreateSiteCommand +from tabcmd.commands.site.create_site_command import CreateSiteCommand from .common_setup import * commandname = "createsite" diff --git a/tests/parsers/test_parser_create_site_users.py b/tests/parsers/test_parser_create_site_users.py index 67e10fcb..ea157a98 100644 --- a/tests/parsers/test_parser_create_site_users.py +++ b/tests/parsers/test_parser_create_site_users.py @@ -1,7 +1,7 @@ import unittest from unittest import mock -from src.commands.user.create_site_users import CreateSiteUsersCommand +from tabcmd.commands.user.create_site_users import CreateSiteUsersCommand from .common_setup import * commandname = "createsiteusers" diff --git a/tests/parsers/test_parser_create_user.py b/tests/parsers/test_parser_create_user.py index 70bbe660..f93ea58f 100644 --- a/tests/parsers/test_parser_create_user.py +++ b/tests/parsers/test_parser_create_user.py @@ -1,7 +1,7 @@ import unittest from unittest import mock -from src.commands.user.create_users_command import CreateUsersCommand +from tabcmd.commands.user.create_users_command import CreateUsersCommand from .common_setup import * commandname = "createusers" diff --git a/tests/parsers/test_parser_decrypt_extracts.py b/tests/parsers/test_parser_decrypt_extracts.py index 4e327fa5..1fdd5dc2 100644 --- a/tests/parsers/test_parser_decrypt_extracts.py +++ b/tests/parsers/test_parser_decrypt_extracts.py @@ -1,6 +1,6 @@ import unittest -from src.commands.extracts.decrypt_extracts_command import DecryptExtracts +from tabcmd.commands.extracts.decrypt_extracts_command import DecryptExtracts from .common_setup import * commandname = "decryptextracts" diff --git a/tests/parsers/test_parser_delete.py b/tests/parsers/test_parser_delete.py index b2d1b508..9d6900d0 100644 --- a/tests/parsers/test_parser_delete.py +++ b/tests/parsers/test_parser_delete.py @@ -1,6 +1,6 @@ import unittest -from src.commands.datasources_and_workbooks.delete_command import DeleteCommand +from tabcmd.commands.datasources_and_workbooks.delete_command import DeleteCommand from .common_setup import * commandname = "delete" diff --git a/tests/parsers/test_parser_delete_extracts.py b/tests/parsers/test_parser_delete_extracts.py index c2e13b24..1d71d187 100644 --- a/tests/parsers/test_parser_delete_extracts.py +++ b/tests/parsers/test_parser_delete_extracts.py @@ -1,6 +1,6 @@ import unittest -from src.commands.extracts.delete_extracts_command import DeleteExtracts +from tabcmd.commands.extracts.delete_extracts_command import DeleteExtracts from .common_setup import * commandname = "deleteextracts" diff --git a/tests/parsers/test_parser_delete_group.py b/tests/parsers/test_parser_delete_group.py index 8269eda7..368e3cce 100644 --- a/tests/parsers/test_parser_delete_group.py +++ b/tests/parsers/test_parser_delete_group.py @@ -1,6 +1,6 @@ import unittest -from src.commands.group.delete_group_command import DeleteGroupCommand +from tabcmd.commands.group.delete_group_command import DeleteGroupCommand from .common_setup import * commandname = "deletegroup" diff --git a/tests/parsers/test_parser_delete_project.py b/tests/parsers/test_parser_delete_project.py index e4b2d1c7..5ecf43bc 100644 --- a/tests/parsers/test_parser_delete_project.py +++ b/tests/parsers/test_parser_delete_project.py @@ -1,6 +1,6 @@ import unittest -from src.commands.project.delete_project_command import DeleteProjectCommand +from tabcmd.commands.project.delete_project_command import DeleteProjectCommand from .common_setup import * commandname = "deleteproject" diff --git a/tests/parsers/test_parser_delete_site.py b/tests/parsers/test_parser_delete_site.py index 843e9c82..2d9562f7 100644 --- a/tests/parsers/test_parser_delete_site.py +++ b/tests/parsers/test_parser_delete_site.py @@ -1,6 +1,6 @@ import unittest -from src.commands.site.delete_site_command import DeleteSiteCommand +from tabcmd.commands.site.delete_site_command import DeleteSiteCommand from .common_setup import * commandname = "deletesite" diff --git a/tests/parsers/test_parser_delete_site_user.py b/tests/parsers/test_parser_delete_site_user.py index 8728a2b5..df66af66 100644 --- a/tests/parsers/test_parser_delete_site_user.py +++ b/tests/parsers/test_parser_delete_site_user.py @@ -1,7 +1,7 @@ import unittest from unittest import mock -from src.commands.user.delete_site_users_command import DeleteSiteUsersCommand +from tabcmd.commands.user.delete_site_users_command import DeleteSiteUsersCommand from .common_setup import * commandname = "deletesiteusers" diff --git a/tests/parsers/test_parser_edit_site.py b/tests/parsers/test_parser_edit_site.py index 19298a5a..447deecf 100644 --- a/tests/parsers/test_parser_edit_site.py +++ b/tests/parsers/test_parser_edit_site.py @@ -1,6 +1,6 @@ import unittest -from src.commands.site.edit_site_command import EditSiteCommand +from tabcmd.commands.site.edit_site_command import EditSiteCommand from .common_setup import * commandname = "editsites" diff --git a/tests/parsers/test_parser_encrypt_extracts.py b/tests/parsers/test_parser_encrypt_extracts.py index 683801ce..e87f8f2a 100644 --- a/tests/parsers/test_parser_encrypt_extracts.py +++ b/tests/parsers/test_parser_encrypt_extracts.py @@ -1,6 +1,6 @@ import unittest -from src.commands.extracts.encrypt_extracts_command import EncryptExtracts +from tabcmd.commands.extracts.encrypt_extracts_command import EncryptExtracts from .common_setup import * commandname = "encryptextracts" diff --git a/tests/parsers/test_parser_export.py b/tests/parsers/test_parser_export.py index ada43078..f90999a9 100644 --- a/tests/parsers/test_parser_export.py +++ b/tests/parsers/test_parser_export.py @@ -1,6 +1,6 @@ import unittest -from src.commands.datasources_and_workbooks.export_command import ExportCommand +from tabcmd.commands.datasources_and_workbooks.export_command import ExportCommand from .common_setup import * commandname = "export" diff --git a/tests/parsers/test_parser_get_url.py b/tests/parsers/test_parser_get_url.py index dcc601f1..5467e236 100644 --- a/tests/parsers/test_parser_get_url.py +++ b/tests/parsers/test_parser_get_url.py @@ -1,6 +1,6 @@ import unittest import argparse -from src.commands.datasources_and_workbooks.get_url_command import GetUrl +from tabcmd.commands.datasources_and_workbooks.get_url_command import GetUrl from .common_setup import * commandname = "listsites" diff --git a/tests/parsers/test_parser_list_sites.py b/tests/parsers/test_parser_list_sites.py index e1b025ef..71b01234 100644 --- a/tests/parsers/test_parser_list_sites.py +++ b/tests/parsers/test_parser_list_sites.py @@ -1,6 +1,6 @@ import unittest -from src.commands.site.list_sites_command import ListSiteCommand +from tabcmd.commands.site.list_sites_command import ListSiteCommand from .common_setup import * commandname = "listsites" diff --git a/tests/parsers/test_parser_publish.py b/tests/parsers/test_parser_publish.py index 356bfe60..261aeb6e 100644 --- a/tests/parsers/test_parser_publish.py +++ b/tests/parsers/test_parser_publish.py @@ -1,6 +1,6 @@ import unittest -from src.commands.datasources_and_workbooks.publish_command import PublishCommand +from tabcmd.commands.datasources_and_workbooks.publish_command import PublishCommand from .common_setup import * commandname = "Publish" diff --git a/tests/parsers/test_parser_publish_samples.py b/tests/parsers/test_parser_publish_samples.py index 52e04c6d..43c4062a 100644 --- a/tests/parsers/test_parser_publish_samples.py +++ b/tests/parsers/test_parser_publish_samples.py @@ -1,6 +1,6 @@ import unittest -from src.commands.project.publish_samples_command import PublishSamplesCommand +from tabcmd.commands.project.publish_samples_command import PublishSamplesCommand from .common_setup import * commandname = "publishsamples" diff --git a/tests/parsers/test_parser_reencrypt_extracts.py b/tests/parsers/test_parser_reencrypt_extracts.py index a465937c..79d82cda 100644 --- a/tests/parsers/test_parser_reencrypt_extracts.py +++ b/tests/parsers/test_parser_reencrypt_extracts.py @@ -1,6 +1,6 @@ import unittest -from src.commands.extracts.reencrypt_extracts_command import ReencryptExtracts +from tabcmd.commands.extracts.reencrypt_extracts_command import ReencryptExtracts from .common_setup import * commandname = "reencryptextracts" diff --git a/tests/parsers/test_parser_refresh_extracts.py b/tests/parsers/test_parser_refresh_extracts.py index 942182d3..a74ac760 100644 --- a/tests/parsers/test_parser_refresh_extracts.py +++ b/tests/parsers/test_parser_refresh_extracts.py @@ -1,6 +1,6 @@ import unittest -from src.commands.extracts.refresh_extracts_command import RefreshExtracts +from tabcmd.commands.extracts.refresh_extracts_command import RefreshExtracts from .common_setup import * commandname = "refreshextracts" diff --git a/tests/parsers/test_parser_remove_user.py b/tests/parsers/test_parser_remove_user.py index d77d00e1..83562c97 100644 --- a/tests/parsers/test_parser_remove_user.py +++ b/tests/parsers/test_parser_remove_user.py @@ -1,7 +1,7 @@ import unittest from unittest import mock -from src.commands.user.remove_users_command import RemoveUserCommand +from tabcmd.commands.user.remove_users_command import RemoveUserCommand from .common_setup import * commandname = "removeusers" diff --git a/tests/parsers/test_parser_runschedule.py b/tests/parsers/test_parser_runschedule.py index 48c71d37..445dfb58 100644 --- a/tests/parsers/test_parser_runschedule.py +++ b/tests/parsers/test_parser_runschedule.py @@ -1,6 +1,6 @@ import unittest -from src.commands.datasources_and_workbooks.runschedule_command import RunSchedule +from tabcmd.commands.datasources_and_workbooks.runschedule_command import RunSchedule from .common_setup import * commandname = "runschedule" From d000dc4a0e76717d1a1200fbcd5af19302bf722e Mon Sep 17 00:00:00 2001 From: Jac Date: Mon, 25 Jul 2022 17:42:36 -0700 Subject: [PATCH 12/20] Jac/publish to folder (#145) Defect 1428581: [Tabcmd WAM] Publishing to projects other than default throws an error --- .../datasources_and_workbooks/publish_command.py | 1 + tabcmd/commands/server.py | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/tabcmd/commands/datasources_and_workbooks/publish_command.py b/tabcmd/commands/datasources_and_workbooks/publish_command.py index c81c0091..ec607755 100644 --- a/tabcmd/commands/datasources_and_workbooks/publish_command.py +++ b/tabcmd/commands/datasources_and_workbooks/publish_command.py @@ -43,6 +43,7 @@ def run_command(args): ) 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 = "" diff --git a/tabcmd/commands/server.py b/tabcmd/commands/server.py index 38aac8ef..fd866055 100644 --- a/tabcmd/commands/server.py +++ b/tabcmd/commands/server.py @@ -106,11 +106,13 @@ def get_filename_extension_if_tableau_type(logger, filename): @staticmethod def get_project_by_name_and_parent_path(logger, server, project_name, parent_path): - logger.debug(_("content_type.project") + ":{0}, {1}".format(parent_path, project_name)) - project_tree = Server._parse_project_path_to_list(parent_path) if not project_name: - project = Server._get_parent_project_from_tree(logger, server, project_tree) + project_name = "Default" + if not parent_path: + project = Server._get_project_by_name_and_parent(logger, server, project_name, None) else: + logger.debug("Finding project within the given parent") + project_tree = Server._parse_project_path_to_list(parent_path) parent = Server._get_parent_project_from_tree(logger, server, project_tree) project = Server._get_project_by_name_and_parent(logger, server, project_name, parent) if not project: @@ -137,7 +139,7 @@ def _get_project_by_name_and_parent(logger, server, project_name, parent): @staticmethod def _get_parent_project_from_tree(logger, server, hierarchy): - # logger.debug("get parent project from tree: {0}".format(hierarchy)) + logger.debug("get parent project from tree: {0}".format(hierarchy)) tree_height = len(hierarchy) if tree_height == 0: return None From fdcb18df31b9892a830f34419fdfeaa7ef2c9760 Mon Sep 17 00:00:00 2001 From: Jac Date: Mon, 1 Aug 2022 15:58:47 -0700 Subject: [PATCH 13/20] Jac/delete command (#144) * filter for items in project * separate the use of ds/wb as flags vs as argument names * update publish_samples to call shared project logic --- .../delete_command.py | 45 +++++++---- .../project/publish_samples_command.py | 6 +- tabcmd/commands/server.py | 75 +++++++++++------- tabcmd/commands/site/list_sites_command.py | 2 +- tabcmd/execution/global_options.py | 78 ++++++++++--------- tests/commands/test_projects_utils.py | 2 +- tests/commands/test_run_commands.py | 3 + 7 files changed, 125 insertions(+), 86 deletions(-) diff --git a/tabcmd/commands/datasources_and_workbooks/delete_command.py b/tabcmd/commands/datasources_and_workbooks/delete_command.py index e9750a93..8e68cb41 100644 --- a/tabcmd/commands/datasources_and_workbooks/delete_command.py +++ b/tabcmd/commands/datasources_and_workbooks/delete_command.py @@ -21,7 +21,8 @@ class DeleteCommand(DatasourcesAndWorkbooks): @staticmethod def define_args(delete_parser): - delete_parser.add_argument("name", help=_("content_type.workbook") + _("content_type.datasource")) + delete_parser.add_argument("name", help=_("content_type.workbook") + "/" + _("content_type.datasource")) + set_ds_xor_wb_options(delete_parser) set_project_r_arg(delete_parser) set_parent_project_arg(delete_parser) @@ -31,26 +32,42 @@ def run_command(args): logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args) + content_type: str = "" + if args.workbook: + content_type = "workbook" + elif args.datasource: + content_type = "datasource" - logger.info(_("delete.status").format(args.name, "")) + container: TSC.ProjectItem = DeleteCommand.get_project_by_name_and_parent_path( + logger, server, args.project_name, args.parent_project_path + ) + if container: + item_name = (args.parent_project_path or "") + "/" + (args.project_name or "default") + "/" + args.name + else: + Errors.exit_with_error(logger, "Containing project could not be found") + logger.info(_("delete.status").format(content_type, item_name or args.name)) error = None - try: - item_to_delete = DeleteCommand.get_workbook_item(logger, server, args.name) - item_type = "workbook" - except TSC.ServerResponseError as workbook_error: - error = workbook_error - try: - item_to_delete = DeleteCommand.get_data_source_item(logger, server, args.name) - item_type = "datasource" - except TSC.ServerResponseError as ds_error: - error = ds_error - if not item_type: + if args.workbook or not content_type: + logger.debug("Attempt as workbook") + try: + item_to_delete = DeleteCommand.get_workbook_item(logger, server, args.name, container) + content_type = "workbook" + except TSC.ServerResponseError as error: + logger.debug(error) + if args.datasource or not content_type: + logger.debug("Attempt as datasource") + try: + item_to_delete = DeleteCommand.get_data_source_item(logger, server, args.name, container) + content_type = "datasource" + except TSC.ServerResponseError as error: + logger.debug(error) + if not content_type or not item_to_delete: logger.debug(error) Errors.exit_with_error(logger, _("delete.errors.requires_workbook_datasource")) try: - if item_type == "workbook": + if content_type == "workbook": server.workbooks.delete(item_to_delete.id) else: server.datasources.delete(item_to_delete.id) diff --git a/tabcmd/commands/project/publish_samples_command.py b/tabcmd/commands/project/publish_samples_command.py index 907f8a5c..65f47358 100644 --- a/tabcmd/commands/project/publish_samples_command.py +++ b/tabcmd/commands/project/publish_samples_command.py @@ -31,13 +31,9 @@ def run_command(args): logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args) - if args.parent_project_path is not None: - project_path = Server.get_project_by_name_and_parent_path(logger, server, None, args.parent_project_path) - else: - project_path = None try: project = PublishSamplesCommand.get_project_by_name_and_parent_path( - logger, server, args.project_name, project_path + logger, server, args.project_name, args.parent_project_path ) except Exception as e: Errors.exit_with_error(logger, _("tabcmd.report.error.publish_samples.expected_project"), exception=e) diff --git a/tabcmd/commands/server.py b/tabcmd/commands/server.py index fd866055..09fa965c 100644 --- a/tabcmd/commands/server.py +++ b/tabcmd/commands/server.py @@ -1,4 +1,6 @@ import os +from typing import List, Optional + import tableauserverclient as TSC from tabcmd.commands.constants import Errors @@ -10,24 +12,24 @@ class Server: @staticmethod def get_workbook_item(logger, server, workbook_name, container=None): try: - return Server.get_items_by_name(logger, server.workbooks, workbook_name, container=None)[0] + return Server.get_items_by_name(logger, server.workbooks, workbook_name, container)[0] except Exception as e: Errors.exit_with_error(logger, exception=e) @staticmethod def get_workbook_id(logger, server, workbook_name, container=None): - return Server.get_workbook_item(logger, server, workbook_name, container=None).id + return Server.get_workbook_item(logger, server, workbook_name, container).id @staticmethod def get_data_source_item(logger, server, data_source_name, container=None): try: - return Server.get_items_by_name(logger, server.datasources, data_source_name, container=None)[0] + return Server.get_items_by_name(logger, server.datasources, data_source_name, container)[0] except Exception as e: Errors.exit_with_error(logger, exception=e) @staticmethod def get_data_source_id(logger, server, data_source_name, container=None): - return Server.get_data_source_item(logger, server, data_source_name, container=None).id + return Server.get_data_source_item(logger, server, data_source_name, container).id @staticmethod def find_group(logger, server, group_name): @@ -48,28 +50,36 @@ def find_user_id(logger, server, username): Errors.exit_with_error(logger, exception=e) @staticmethod - def get_items_by_name(logger, item_endpoint, item_name, container=None): + def get_items_by_name(logger, item_endpoint, item_name: str, container: TSC.ProjectItem = None): item_type = type(item_endpoint).__name__ - item_log_name = item_name + item_log_name: str = "[" + item_type + "] " + item_name if container: - item_log_name = container + "/" + item_log_name - item_log_name = "[" + item_type + "] " + item_log_name + item_log_name = str(container) + "/" + item_log_name logger.debug(_("export.status").format(item_log_name)) req_option = TSC.RequestOptions() req_option.filter.add(TSC.Filter(TSC.RequestOptions.Field.Name, TSC.RequestOptions.Operator.Equals, item_name)) - if container: - logger.debug("Searching in project {}".format(container)) - req_option.filter.add( - TSC.Filter(TSC.RequestOptions.Field.ParentProjectId, TSC.RequestOptions.Operator.Equals, container) - ) + all_items, pagination_item = item_endpoint.get(req_option) if all_items is None or all_items == []: raise ValueError("[" + item_type + "] " + _("errors.xmlapi.not_found")) + if len(all_items) == 1: + logger.debug("Exactly one result found") + result = all_items if len(all_items) > 1: - logger.debug("{}+ items of this name were found. Returning first page.".format(len(all_items))) - logger.debug(all_items[0].name + ", " + all_items[1].name + ", " + all_items[2].name) + logger.debug( + "{}+ items of this name were found: {}".format( + len(all_items), all_items[0].name + ", " + all_items[1].name + ", ..." + ) + ) + + if container: + container_id = container.id + logger.debug("Filtering to items in project {}".format(container.id)) + result = list(filter(lambda item: item.project_id == container_id, all_items)) + else: + result = all_items - return all_items + return result # Get site by name or get currently logged in site @staticmethod @@ -105,28 +115,35 @@ def get_filename_extension_if_tableau_type(logger, filename): ) @staticmethod - def get_project_by_name_and_parent_path(logger, server, project_name, parent_path): - if not project_name: - project_name = "Default" + def get_project_by_name_and_parent_path(logger, server, project_name: str, parent_path: str) -> TSC.ProjectItem: + logger.debug(_("content_type.project") + ":{0}, {1}".format(parent_path, project_name)) if not parent_path: - project = Server._get_project_by_name_and_parent(logger, server, project_name, None) - else: - logger.debug("Finding project within the given parent") - project_tree = Server._parse_project_path_to_list(parent_path) - parent = Server._get_parent_project_from_tree(logger, server, project_tree) - project = Server._get_project_by_name_and_parent(logger, server, project_name, parent) + if not project_name: + project_name = "Default" + project: TSC.ProjectItem = Server.get_items_by_name(logger, server.projects, project_name, None) + return project + + project_tree: List[str] = Server._parse_project_path_to_list(parent_path) + if not project_name: + project = Server._get_parent_project_from_tree(logger, server, project_tree) + return project + + parent = Server._get_parent_project_from_tree(logger, server, project_tree) + project = Server._get_project_by_name_and_parent(logger, server, project_name, parent) if not project: Errors.exit_with_error(logger, message=_("publish.errors.server_resource_not_found")) return project @staticmethod - def _parse_project_path_to_list(project_path): - if project_path is None: + def _parse_project_path_to_list(project_path: str): + if project_path is None or project_path == "": return [] + if project_path.find("/") == -1: + return [project_path] return project_path.split("/") @staticmethod - def _get_project_by_name_and_parent(logger, server, project_name, parent): + def _get_project_by_name_and_parent(logger, server, project_name: str, parent: Optional[TSC.ProjectItem]): # logger.debug("get by name and parent: {0}, {1}".format(project_name, parent)) # get by name to narrow down the list projects = Server.get_items_by_name(logger, server.projects, project_name) @@ -138,7 +155,7 @@ def _get_project_by_name_and_parent(logger, server, project_name, parent): return projects[0] @staticmethod - def _get_parent_project_from_tree(logger, server, hierarchy): + def _get_parent_project_from_tree(logger, server, hierarchy: List[str]): logger.debug("get parent project from tree: {0}".format(hierarchy)) tree_height = len(hierarchy) if tree_height == 0: diff --git a/tabcmd/commands/site/list_sites_command.py b/tabcmd/commands/site/list_sites_command.py index 418f6caf..15d353bf 100644 --- a/tabcmd/commands/site/list_sites_command.py +++ b/tabcmd/commands/site/list_sites_command.py @@ -18,7 +18,7 @@ class ListSiteCommand(Server): @staticmethod def define_args(list_site_parser): - set_view_site_encryption(list_site_parser) + set_site_detail_option(list_site_parser) @staticmethod def run_command(args): diff --git a/tabcmd/execution/global_options.py b/tabcmd/execution/global_options.py index 8c259028..34672806 100644 --- a/tabcmd/execution/global_options.py +++ b/tabcmd/execution/global_options.py @@ -172,21 +172,19 @@ def set_project_arg(parser): return parser -def set_datasource_arg(parser, action="store_true"): - parser.add_argument("-d", "--datasource", help="The name of the target data source.", action=action) - return parser - - def set_site_url_arg(parser): parser.add_argument("--url", help="The canonical name for the resource as it appears in the URL") return parser -def set_workbook_arg(parser, action="store_true"): # true if the user adds --workbook - parser.add_argument("-w", "--workbook", help="The name of the target workbook.", action=action) +def set_ds_xor_wb_options(parser): + target_type_group = parser.add_mutually_exclusive_group(required=False) + target_type_group.add_argument("-d", "--datasource", action="store_true", help="The name of the target datasource.") + target_type_group.add_argument("-w", "--workbook", action="store_true", help="The name of the target workbook.") return parser +# pass arguments for either --datasource or --workbook def set_ds_xor_wb_args(parser): target_type_group = parser.add_mutually_exclusive_group(required=True) target_type_group.add_argument("-d", "--datasource", help="The name of the target datasource.") @@ -212,7 +210,7 @@ def set_site_status_arg(parser): # create-site/update-site - lots of these options are never used elsewhere # mismatched arguments: createsite says --url, editsite says --site-id # just let both commands use either of them -def set_site_id_options(parser): +def set_site_id_args(parser): site_id = parser.add_mutually_exclusive_group() site_id.add_argument("--site-id", help="Used in the URL to uniquely identify the site.") site_id.add_argument( @@ -226,24 +224,11 @@ def set_site_id_options(parser): # these options are all shared in create-site and edit-site def set_common_site_args(parser): - parser = set_site_id_options(parser) + parser = set_site_id_args(parser) parser.add_argument("--user-quota", type=int, help="Maximum number of users that can be added to the site.") - site_help = "Allows or denies site administrators the ability to add users to or remove users from the site." - site_group = parser.add_mutually_exclusive_group() - site_group.add_argument( - "--site-mode", - dest="site_admin_user_management", - action="store_true", - help=site_help, - ) - site_group.add_argument( - "--no-site-mode", - dest="site_admin_user_management", - action="store_false", - help=site_help, - ) + set_site_mode_option(parser) parser.add_argument( "--storage-quota", @@ -265,8 +250,25 @@ def set_common_site_args(parser): return parser +def set_site_mode_option(parser): + site_help = "Allows or denies site administrators the ability to add users to or remove users from the site." + site_group = parser.add_mutually_exclusive_group() + site_group.add_argument( + "--site-mode", + dest="site_admin_user_management", + action="store_true", + help=site_help, + ) + site_group.add_argument( + "--no-site-mode", + dest="site_admin_user_management", + action="store_false", + help=site_help, + ) + + # this option is only used by listsites -def set_view_site_encryption(parser): +def set_site_detail_option(parser): parser.add_argument( "--get-extract-encryption-mode", action="store_true", @@ -282,18 +284,7 @@ def set_filename_arg(parser, description=_("get.options.file")): def set_publish_args(parser): parser.add_argument("-n", "--name", help="Name to publish the new datasource or workbook by.") - append_group = parser.add_mutually_exclusive_group() - append_group.add_argument( - "-o", - "--overwrite", - action="store_true", - help="Overwrites the workbook, data source, or data extract if it already exists on the server.", - ) - append_group.add_argument( - "--append", - action="store_true", - help="Append the extract file to the existing data source.", - ) + set_overwrite_option(parser) parser.add_argument( "--db-username", help="Use this option to publish a database user name with the workbook, data source, or data extract.", @@ -329,6 +320,21 @@ def set_publish_args(parser): parser.add_argument("--thumbnail-group", help="Not yet implemented") # not implemented in the REST API +def set_overwrite_option(parser): + append_group = parser.add_mutually_exclusive_group() + append_group.add_argument( + "-o", + "--overwrite", + action="store_true", + help="Overwrites the workbook, data source, or data extract if it already exists on the server.", + ) + append_group.add_argument( + "--append", + action="store_true", + help="Append the extract file to the existing data source.", + ) + + # refresh-extracts def set_incremental_options(parser): sync_group = parser.add_mutually_exclusive_group() diff --git a/tests/commands/test_projects_utils.py b/tests/commands/test_projects_utils.py index 61799bd7..9ba3c09d 100644 --- a/tests/commands/test_projects_utils.py +++ b/tests/commands/test_projects_utils.py @@ -16,7 +16,7 @@ class ProjectsTest(unittest.TestCase): @staticmethod def test_parent_path_to_list(): assert Server._parse_project_path_to_list(None) == [] - assert Server._parse_project_path_to_list("") == [""] + assert Server._parse_project_path_to_list("") == [] assert Server._parse_project_path_to_list("parent") == ["parent"] assert Server._parse_project_path_to_list("parent/child") == ["parent", "child"] diff --git a/tests/commands/test_run_commands.py b/tests/commands/test_run_commands.py index 4ab980c3..7e7e3d45 100644 --- a/tests/commands/test_run_commands.py +++ b/tests/commands/test_run_commands.py @@ -89,8 +89,11 @@ def test_delete(self, mock_session, mock_server): RunCommandsTest._set_up_session(mock_session, mock_server) mock_server.workbooks = getter mock_server.datasources = getter + mock_server.projects = getter mock_args.workbook = True mock_args.datasource = False + mock_args.project_name = None + mock_args.parent_project_path = None mock_args.name = "name for on server" delete_command.DeleteCommand.run_command(mock_args) From cc4ee24c1418812856e7607c7e235e9c42727865 Mon Sep 17 00:00:00 2001 From: Jac Date: Wed, 3 Aug 2022 01:01:11 -0700 Subject: [PATCH 14/20] Jac/e2e test workflow (#154) * Create run-e2-tests.yml --- .github/workflows/run-e2-tests.yml | 45 +++++++++++++++++++++++ tests/e2e/online_tests.py | 59 ++++++++++++++++++++++-------- 2 files changed, 89 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/run-e2-tests.yml diff --git a/.github/workflows/run-e2-tests.yml b/.github/workflows/run-e2-tests.yml new file mode 100644 index 00000000..371577f5 --- /dev/null +++ b/.github/workflows/run-e2-tests.yml @@ -0,0 +1,45 @@ +name: Python tests + +on: + workflow_dispatch: + inputs: + server: + required: true + site: + required: true + patname: + required: true + pat: + required: true + +jobs: + build: + strategy: + fail-fast: true + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + python-version: ['3.7', '3.8', '3.9', '3.10', '3'] + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python ${{ matrix.python-version }} on ${{ matrix.os }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python --version + python -m pip install --upgrade pip + pip install -e .[build] + pip install -e .[test] + doit version + python setup.py build + + - name: Run e2e tests + run: | + python -m tabcmd login --server "${{ github.event.inputs.server }}" --site "${{ github.event.inputs.site }}" --token-name "${{ github.event.inputs.patname }}" --token-value "${{ github.event.inputs.pat }}" + pytest -q tests\e2e\online_tests.py -r pfE diff --git a/tests/e2e/online_tests.py b/tests/e2e/online_tests.py index 7aa69610..b1e6afbb 100644 --- a/tests/e2e/online_tests.py +++ b/tests/e2e/online_tests.py @@ -18,6 +18,14 @@ # you can either run setup with a stored credentials file, or simply log in # before running the suite so a session is active +# alpodev +parent_location = "WAM" +project_name = "Developer Platform" + +server_admin = False +site_admin = True +project_admin = True + def _test_command(test_args: list[str]): # this will raise an exception if it gets a non-zero return code @@ -43,18 +51,18 @@ def setup_class(cls): def _create_project(self, project_name, parent_path=None): command = "createproject" arguments = [command, "--name", project_name] - if parent_path: + if parent_path or parent_location: arguments.append("--parent-project-path") - arguments.append(parent_path) + arguments.append(parent_path or parent_location) print(arguments) _test_command(arguments) def _delete_project(self, project_name, parent_path=None): command = "deleteproject" arguments = [command, project_name] - if parent_path: + if parent_path or parent_location: arguments.append("--parent-project-path") - arguments.append(parent_path) + arguments.append(parent_path or parent_location) _test_command(arguments) def _publish_samples(self, project_name): @@ -135,6 +143,8 @@ def test_help(self): @pytest.mark.order(2) def test_create_site_users(self): + if not server_admin and not site_admin: + pytest.skip("Must be server or site administrator to create site users") command = "createsiteusers" users = os.path.join("tests", "assets", "detailed_users.csv") arguments = [command, users, "--role", "Publisher"] @@ -142,6 +152,8 @@ def test_create_site_users(self): @pytest.mark.order(3) def test_creategroup(self): + if not server_admin and not site_admin: + pytest.skip("Must be server or site administrator to create groups") groupname = group_name command = "creategroup" arguments = [command, groupname] @@ -149,6 +161,9 @@ def test_creategroup(self): @pytest.mark.order(4) def test_add_users_to_group(self): + if not server_admin and not site_admin: + pytest.skip("Must be server or site administrator to add to groups") + groupname = group_name command = "addusers" filename = os.path.join("tests", "assets", "usernames.csv") @@ -157,6 +172,9 @@ def test_add_users_to_group(self): @pytest.mark.order(5) def test_remove_users_to_group(self): + if not server_admin and not site_admin: + pytest.skip("Must be server or site administrator to remove from groups") + groupname = group_name command = "removeusers" filename = os.path.join("tests", "assets", "usernames.csv") @@ -165,22 +183,19 @@ def test_remove_users_to_group(self): @pytest.mark.order(6) def test_deletegroup(self): + if not server_admin and not site_admin: + pytest.skip("Must be server or site administrator to delete groups") + groupname = group_name command = "deletegroup" arguments = [command, groupname] _test_command(arguments) - @pytest.mark.order(7) - def test_publish_samples(self): - project_name = "sample-proj" - self._create_project(project_name) - time.sleep(indexing_sleep_time) - - self._publish_samples(project_name) - self._delete_project(project_name) - @pytest.mark.order(8) def test_create_projects(self): + + if not project_admin: + pytest.skip("Must be project administrator to create projects") # project 1 self._create_project(project_name) time.sleep(indexing_sleep_time) @@ -194,12 +209,14 @@ def test_create_projects(self): @pytest.mark.order(9) def test_delete_projects(self): + if not project_admin: + pytest.skip("Must be project administrator to create projects") self._delete_project("project_name_2", project_name) # project 2 self._delete_project(project_name) @pytest.mark.order(9) def test_publish_samples(self): - self._publish_samples("Default") + self._publish_samples(project_name) @pytest.mark.order(10) def test_publish(self): @@ -256,6 +273,7 @@ def test_create_extract(self): @pytest.mark.order(14) def test_refresh_extract(self): + # must be a datasource owned by the test user name_on_server = OnlineCommandTest.TWBX_WITH_EXTRACT_NAME self._refresh_extract(name_on_server) self._delete_wb(name_on_server) @@ -292,11 +310,22 @@ def test_export_view(self): @pytest.mark.order(16) def test_delete_site_users(self): + if not server_admin and not site_admin: + pytest.skip("Must be server or site administrator to delete site users") + command = "deletesiteusers" users = os.path.join("tests", "assets", "usernames.csv") _test_command([command, users]) @pytest.mark.order(20) def test_list_sites(self): + if not server_admin: + pytest.skip("Must be server administrator to list sites") + command = "listsites" - _test_command([command]) + try: + _test_command([command]) + except Exception as E: + print("yay") + result = True + assert result From 7fb7b9cb41368ce7123206773785cb270ab155bd Mon Sep 17 00:00:00 2001 From: Jac Date: Fri, 5 Aug 2022 13:56:27 -0700 Subject: [PATCH 15/20] Add command to list items of a given content type (#153) * new command: list content --- tabcmd.py | 2 +- tabcmd/commands/site/list_command.py | 45 ++++++++++++++++++++++ tabcmd/commands/site/list_sites_command.py | 5 +-- tabcmd/execution/map_of_commands.py | 2 + tests/e2e/online_tests.py | 9 +++++ 5 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 tabcmd/commands/site/list_command.py diff --git a/tabcmd.py b/tabcmd.py index 1c987d62..55e7e634 100644 --- a/tabcmd.py +++ b/tabcmd.py @@ -1,4 +1,4 @@ -from src import tabcmd +from tabcmd import tabcmd if __name__ == "__main__": tabcmd.main() diff --git a/tabcmd/commands/site/list_command.py b/tabcmd/commands/site/list_command.py new file mode 100644 index 00000000..2eff8ec5 --- /dev/null +++ b/tabcmd/commands/site/list_command.py @@ -0,0 +1,45 @@ +import tableauserverclient as TSC + +from tabcmd.commands.auth.session import Session +from tabcmd.commands.constants import Errors +from tabcmd.commands.server import Server +from tabcmd.execution.global_options import * +from tabcmd.execution.localize import _ +from tabcmd.execution.logger_config import log + + +class ListCommand(Server): + """ + Command to return a list of content the user can access + """ + + name: str = "list" + description: str = "List content items of a specified type" + + @staticmethod + def define_args(list_parser): + list_parser.add_argument("content", choices=["projects", "workbooks", "datasources"], help="View content") + + @staticmethod + def run_command(args): + logger = log(__name__, args.logging_level) + logger.debug(_("tabcmd.launching")) + session = Session() + server = session.create_session(args) + content_type = args.content + + try: + if content_type == "projects": + items = server.projects.all() + elif content_type == "workbooks": + items = server.workbooks.all() + elif content_type == "datasources": + items = server.datasources.all() + + logger.info("===== Listing {0} content for user {1}...".format(content_type, session.username)) + for item in items: + print("NAME:".rjust(10), item.name) + print("ID:".rjust(10), item.id) + + except TSC.ServerResponseError as e: + Errors.exit_with_error(logger, e) diff --git a/tabcmd/commands/site/list_sites_command.py b/tabcmd/commands/site/list_sites_command.py index 15d353bf..55e69f85 100644 --- a/tabcmd/commands/site/list_sites_command.py +++ b/tabcmd/commands/site/list_sites_command.py @@ -30,10 +30,9 @@ def run_command(args): sites, pagination = server.sites.get() logger.info(_("listsites.status").format(session.username)) for site in sites: - print("NAME:", site.name) - print("SITEID:", site.content_url) + print("NAME:".rjust(10), site.name) + print("SITEID:".rjust(10), site.content_url) if args.get_extract_encryption_mode: print("EXTRACTENCRYPTION:", site.extract_encryption_mode) - print("") except TSC.ServerResponseError as e: Errors.exit_with_error(logger, e) diff --git a/tabcmd/execution/map_of_commands.py b/tabcmd/execution/map_of_commands.py index 87ff0a1d..c6f7a679 100644 --- a/tabcmd/execution/map_of_commands.py +++ b/tabcmd/execution/map_of_commands.py @@ -23,6 +23,7 @@ from tabcmd.commands.user.add_users_command import * from tabcmd.commands.user.create_site_users import * from tabcmd.commands.user.delete_site_users_command import * +from tabcmd.commands.site.list_command import * # from tabcmd.commands.user.create_users import * from tabcmd.commands.user.remove_users_command import * @@ -51,6 +52,7 @@ class CommandsMap: GetUrl, HelpCommand, ListSiteCommand, + ListCommand, LoginCommand, LogoutCommand, PublishCommand, diff --git a/tests/e2e/online_tests.py b/tests/e2e/online_tests.py index b1e6afbb..cb391e29 100644 --- a/tests/e2e/online_tests.py +++ b/tests/e2e/online_tests.py @@ -114,6 +114,11 @@ def _delete_extract(self, wb_name): arguments = [command, "-w", wb_name] _test_command(arguments) + def _list(self, item_type: str): + command = "list" + arguments = [command, item_type] + _test_command(arguments) + # actual tests TWBX_FILE_WITH_EXTRACT = "extract-data-access.twbx" TWBX_WITH_EXTRACT_NAME = "WorkbookWithExtract" @@ -207,6 +212,10 @@ def test_create_projects(self): self._create_project(project_name, parent_path) time.sleep(indexing_sleep_time) + @pytest.mark.order(8) + def test_list_projects(self): + self._list("projects") + @pytest.mark.order(9) def test_delete_projects(self): if not project_admin: From 6722d613445e2335c902e99cc40393469dba7401 Mon Sep 17 00:00:00 2001 From: Jac Date: Thu, 1 Sep 2022 11:33:49 -0700 Subject: [PATCH 16/20] make export use filters, add tests (#164) --- .../export_command.py | 87 ++++++++++++------- tabcmd/execution/logger_config.py | 2 + tests/commands/test_geturl_utils.py | 80 ++++++++++++++++- tests/commands/test_run_commands.py | 9 ++ tests/e2e/online_tests.py | 2 +- 5 files changed, 147 insertions(+), 33 deletions(-) diff --git a/tabcmd/commands/datasources_and_workbooks/export_command.py b/tabcmd/commands/datasources_and_workbooks/export_command.py index 72f7375a..11cf2b82 100644 --- a/tabcmd/commands/datasources_and_workbooks/export_command.py +++ b/tabcmd/commands/datasources_and_workbooks/export_command.py @@ -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 @@ -73,24 +54,25 @@ 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: @@ -98,32 +80,65 @@ def run_command(args): 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 @@ -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("/") @@ -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)) diff --git a/tabcmd/execution/logger_config.py b/tabcmd/execution/logger_config.py index 1837e951..b478909e 100644 --- a/tabcmd/execution/logger_config.py +++ b/tabcmd/execution/logger_config.py @@ -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 diff --git a/tests/commands/test_geturl_utils.py b/tests/commands/test_geturl_utils.py index 2430bf28..0bdb4555 100644 --- a/tests/commands/test_geturl_utils.py +++ b/tests/commands/test_geturl_utils.py @@ -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): @@ -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) diff --git a/tests/commands/test_run_commands.py b/tests/commands/test_run_commands.py index 7e7e3d45..22b9dda0 100644 --- a/tests/commands/test_run_commands.py +++ b/tests/commands/test_run_commands.py @@ -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() diff --git a/tests/e2e/online_tests.py b/tests/e2e/online_tests.py index cb391e29..73a0e5dc 100644 --- a/tests/e2e/online_tests.py +++ b/tests/e2e/online_tests.py @@ -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) From c8451dbe758f270159093f8c740559022bfb8806 Mon Sep 17 00:00:00 2001 From: Jac Date: Wed, 7 Sep 2022 23:05:28 -0700 Subject: [PATCH 17/20] Fix some e2e tests (#166) * Fix some e2e tests --- tabcmd/commands/group/delete_group_command.py | 2 +- tabcmd/commands/server.py | 25 +++--- .../user/delete_site_users_command.py | 2 +- tabcmd/commands/user/user_data.py | 31 +++++-- tests/e2e/online_tests.py | 82 +++++++++---------- 5 files changed, 75 insertions(+), 67 deletions(-) diff --git a/tabcmd/commands/group/delete_group_command.py b/tabcmd/commands/group/delete_group_command.py index 01a8a4b5..6d2741f1 100644 --- a/tabcmd/commands/group/delete_group_command.py +++ b/tabcmd/commands/group/delete_group_command.py @@ -27,7 +27,7 @@ def run_command(args): server = session.create_session(args) try: logger.info(_("tabcmd.find.group").format(args.name)) - group_id = Server.find_group_id(logger, server, args.name) + group_id = Server.find_group(logger, server, args.name).id logger.info(_("deletegroup.status").format(group_id)) server.groups.delete(group_id) logger.info(_("common.output.succeeded")) diff --git a/tabcmd/commands/server.py b/tabcmd/commands/server.py index 09fa965c..cf3700cf 100644 --- a/tabcmd/commands/server.py +++ b/tabcmd/commands/server.py @@ -39,18 +39,12 @@ def find_group(logger, server, group_name): Errors.exit_with_error(logger, exception=e) @staticmethod - def find_group_id(logger, server, group_name): - return Server.find_group(logger, server, group_name).id + def find_user(logger, server, username): + return Server.get_items_by_name(logger, server.users, username)[0] @staticmethod - def find_user_id(logger, server, username): - try: - return Server.get_items_by_name(logger, server.users, username)[0].id - except Exception as e: - Errors.exit_with_error(logger, exception=e) - - @staticmethod - def get_items_by_name(logger, item_endpoint, item_name: str, container: TSC.ProjectItem = None): + def get_items_by_name(logger, item_endpoint, item_name: str, container: TSC.ProjectItem = None) -> List: + # TODO: typing should reflect that this returns TSC.TableauItem and item_endpoint is of type TSC.QuerysetEndpoint[same] item_type = type(item_endpoint).__name__ item_log_name: str = "[" + item_type + "] " + item_name if container: @@ -58,10 +52,13 @@ def get_items_by_name(logger, item_endpoint, item_name: str, container: TSC.Proj logger.debug(_("export.status").format(item_log_name)) req_option = TSC.RequestOptions() req_option.filter.add(TSC.Filter(TSC.RequestOptions.Field.Name, TSC.RequestOptions.Operator.Equals, item_name)) - all_items, pagination_item = item_endpoint.get(req_option) if all_items is None or all_items == []: - raise ValueError("[" + item_type + "] " + _("errors.xmlapi.not_found")) + raise TSC.ServerResponseError( + code=404, + summary=_("errors.xmlapi.not_found"), + detail=_("errors.xmlapi.not_found") + ": " + item_log_name, + ) if len(all_items) == 1: logger.debug("Exactly one result found") result = all_items @@ -120,7 +117,7 @@ def get_project_by_name_and_parent_path(logger, server, project_name: str, paren if not parent_path: if not project_name: project_name = "Default" - project: TSC.ProjectItem = Server.get_items_by_name(logger, server.projects, project_name, None) + project: TSC.ProjectItem = Server.get_items_by_name(logger, server.projects, project_name, None)[0] return project project_tree: List[str] = Server._parse_project_path_to_list(parent_path) @@ -129,7 +126,9 @@ def get_project_by_name_and_parent_path(logger, server, project_name: str, paren return project parent = Server._get_parent_project_from_tree(logger, server, project_tree) + logger.debug(parent) project = Server._get_project_by_name_and_parent(logger, server, project_name, parent) + logger.debug(project) if not project: Errors.exit_with_error(logger, message=_("publish.errors.server_resource_not_found")) return project diff --git a/tabcmd/commands/user/delete_site_users_command.py b/tabcmd/commands/user/delete_site_users_command.py index 7f46a311..aa749a5a 100644 --- a/tabcmd/commands/user/delete_site_users_command.py +++ b/tabcmd/commands/user/delete_site_users_command.py @@ -42,7 +42,7 @@ def run_command(args): for user_obj in user_obj_list: logger.info(_("importcsvsummary.line.processed").format(number_of_users_deleted)) try: - user_id = UserCommand.find_user_id(logger, server, user_obj.name) + user_id = UserCommand.find_user(logger, server, user_obj.name).id server.users.remove(user_id) logger.debug(_("tabcmd.result.success.delete_user").format(user_obj.name, user_id)) number_of_users_deleted += 1 diff --git a/tabcmd/commands/user/user_data.py b/tabcmd/commands/user/user_data.py index ed7082c4..f8c088f7 100644 --- a/tabcmd/commands/user/user_data.py +++ b/tabcmd/commands/user/user_data.py @@ -229,17 +229,25 @@ def act_on_users( logger.debug(_("importcsvsummary.line.processed").format(n_users_listed)) error_list = [] + line_no = 0 user_obj_list: List[TSC.UserItem] = UserCommand.get_users_from_file(args.users) logger.debug(_("tabcmd.result.success.parsed_users").format(len(user_obj_list))) for user_obj in user_obj_list: - username: str = user_obj.name or "unknown user" + line_no += 1 + if not user_obj.name: + number_of_errors += 1 + error_list.append(_("importcsvsummary.error.line").format(line_no, "No username", "")) + continue + try: - user_id: str = UserCommand.find_user_id(logger, server, username) + username: str = user_obj.name + user_id: str = UserCommand.find_user(logger, server, username).id logger.debug("{} user {} ({})".format(action_name, username, user_id)) except TSC.ServerResponseError as e: - Errors.check_common_error_codes_and_explain(logger, e) number_of_errors += 1 - error_list.append(e) + error_list.append( + _("importcsvsummary.error.line").format(line_no, username, "{}: {}".format(e.code, e.detail)) + ) logger.debug(_("tabcmd.result.failure.user").format(username)) continue @@ -248,11 +256,20 @@ def act_on_users( n_users_handled += 1 logger.info(_("tabcmd.result.success.user_actions").format(action_name, username, group)) except TSC.ServerResponseError as e: - Errors.check_common_error_codes_and_explain(logger, e) number_of_errors += 1 - error_list.append(e) + error_list.append( + _("importcsvsummary.error.line").format(line_no, username, "{}: {}".format(e.code, e.detail)) + ) logger.info(_("session.monitorjob.percent_complete").format(100)) logger.info(_("importcsvsummary.errors.count").format(number_of_errors)) if number_of_errors > 0: - logger.info(_("importcsvsummary.error.details").format(error_list)) + i = 0 + max_printing = 5 + logger.info(_("importcsvsummary.error.details")) + while i < number_of_errors and i < max_printing: + logger.info(error_list[i]) + i += 1 + if number_of_errors > max_printing: + logger.info(_("importcsvsummary.error.too_many_errors")) + logger.info(_("importcsvsummary.remainingerrors")) diff --git a/tests/e2e/online_tests.py b/tests/e2e/online_tests.py index 73a0e5dc..bede34d0 100644 --- a/tests/e2e/online_tests.py +++ b/tests/e2e/online_tests.py @@ -51,10 +51,9 @@ def setup_class(cls): def _create_project(self, project_name, parent_path=None): command = "createproject" arguments = [command, "--name", project_name] - if parent_path or parent_location: + if parent_path: arguments.append("--parent-project-path") - arguments.append(parent_path or parent_location) - print(arguments) + arguments.append(parent_path) _test_command(arguments) def _delete_project(self, project_name, parent_path=None): @@ -136,7 +135,7 @@ def test_login(self): @pytest.mark.order(1) def test_version(self): - command = "" + command = "-v" arguments = [command] _test_command(arguments) @@ -147,7 +146,7 @@ def test_help(self): _test_command(arguments) @pytest.mark.order(2) - def test_create_site_users(self): + def test_users_create_site_users(self): if not server_admin and not site_admin: pytest.skip("Must be server or site administrator to create site users") command = "createsiteusers" @@ -156,7 +155,7 @@ def test_create_site_users(self): _test_command(arguments) @pytest.mark.order(3) - def test_creategroup(self): + def test_group_creategroup(self): if not server_admin and not site_admin: pytest.skip("Must be server or site administrator to create groups") groupname = group_name @@ -165,7 +164,7 @@ def test_creategroup(self): _test_command(arguments) @pytest.mark.order(4) - def test_add_users_to_group(self): + def test_users_add_to_group(self): if not server_admin and not site_admin: pytest.skip("Must be server or site administrator to add to groups") @@ -176,7 +175,7 @@ def test_add_users_to_group(self): _test_command(arguments) @pytest.mark.order(5) - def test_remove_users_to_group(self): + def test_users_remove_from_group(self): if not server_admin and not site_admin: pytest.skip("Must be server or site administrator to remove from groups") @@ -187,7 +186,7 @@ def test_remove_users_to_group(self): _test_command(arguments) @pytest.mark.order(6) - def test_deletegroup(self): + def test_group_deletegroup(self): if not server_admin and not site_admin: pytest.skip("Must be server or site administrator to delete groups") @@ -198,9 +197,12 @@ def test_deletegroup(self): @pytest.mark.order(8) def test_create_projects(self): - if not project_admin: pytest.skip("Must be project administrator to create projects") + + # project 1 + self._create_project(parent_location) + time.sleep(indexing_sleep_time) # project 1 self._create_project(project_name) time.sleep(indexing_sleep_time) @@ -216,58 +218,54 @@ def test_create_projects(self): def test_list_projects(self): self._list("projects") - @pytest.mark.order(9) + """ + @pytest.mark.order(9) + def test_publish_samples(self): + self._publish_samples(project_name) + """ + + @pytest.mark.order(10) def test_delete_projects(self): if not project_admin: pytest.skip("Must be project administrator to create projects") self._delete_project("project_name_2", project_name) # project 2 self._delete_project(project_name) - @pytest.mark.order(9) - def test_publish_samples(self): - self._publish_samples(project_name) - @pytest.mark.order(10) - def test_publish(self): + def test_wb_publish(self): name_on_server = OnlineCommandTest.TWBX_WITH_EXTRACT_NAME file = os.path.join("tests", "assets", OnlineCommandTest.TWBX_FILE_WITH_EXTRACT) self._publish_wb(file, name_on_server) @pytest.mark.order(10) - def test__get_wb(self): - wb_name_on_server = OnlineCommandTest.TWBX_WITHOUT_EXTRACT_NAME - self._get_workbook(wb_name_on_server + ".twbx") + def test_wb_get(self): + self._get_workbook(OnlineCommandTest.TWBX_WITH_EXTRACT_NAME + ".twbx") @pytest.mark.order(10) - def test__get_wb(self): + def test_view_get_pdf(self): wb_name_on_server = OnlineCommandTest.TWBX_WITH_EXTRACT_NAME - self._get_workbook(wb_name_on_server + ".twbx") - - @pytest.mark.order(10) - def test__get_view(self): - wb_name_on_server = OnlineCommandTest.TWBX_WITHOUT_EXTRACT_NAME - sheet_name = OnlineCommandTest.TWBX_WITHOUT_EXTRACT_SHEET + sheet_name = OnlineCommandTest.TWBX_WITH_EXTRACT_SHEET self._get_view(wb_name_on_server, sheet_name + ".pdf") @pytest.mark.order(10) - def test__get_view_csv(self): + def test_view_get_csv(self): wb_name_on_server = OnlineCommandTest.TWBX_WITH_EXTRACT_NAME sheet_name = OnlineCommandTest.TWBX_WITH_EXTRACT_SHEET self._get_view(wb_name_on_server, sheet_name + ".csv") @pytest.mark.order(10) - def test__get_view_png(self): + def test_view_get_png(self): wb_name_on_server = OnlineCommandTest.TWBX_WITH_EXTRACT_NAME sheet_name = OnlineCommandTest.TWBX_WITH_EXTRACT_SHEET self._get_view(wb_name_on_server, sheet_name + ".png") @pytest.mark.order(11) - def test__delete_wb(self): + def test_wb_delete(self): name_on_server = OnlineCommandTest.TWBX_WITH_EXTRACT_NAME self._delete_wb(name_on_server) @pytest.mark.order(12) - def test_delete_extract(self): + def test_extract_delete(self): # fails because the extract has a bad data connection :/ name_on_server = OnlineCommandTest.TWBX_WITH_EXTRACT_NAME file = os.path.join("tests", "assets", OnlineCommandTest.TWBX_FILE_WITH_EXTRACT) @@ -275,30 +273,24 @@ def test_delete_extract(self): self._delete_extract(name_on_server) @pytest.mark.order(13) - def test_create_extract(self): + def test_extract_create(self): # Fails because it 'already has an extract' :/ name_on_server = OnlineCommandTest.TWBX_WITHOUT_EXTRACT_NAME self._create_extract(name_on_server) @pytest.mark.order(14) - def test_refresh_extract(self): + def test_extract_refresh(self): # must be a datasource owned by the test user + name_on_server = OnlineCommandTest.TWBX_WITH_EXTRACT_NAME + file = os.path.join("tests", "assets", OnlineCommandTest.TWBX_FILE_WITH_EXTRACT) + self._publish_wb(file, name_on_server) + name_on_server = OnlineCommandTest.TWBX_WITH_EXTRACT_NAME self._refresh_extract(name_on_server) self._delete_wb(name_on_server) - def test_version(self): - _test_command(["-v"]) - - def test_help(self): - _test_command(["help"]) - - # this just gets in the way :( - # def test_logout(self): - # _test_command(["logout"]) - @pytest.mark.order(15) - def test_export_wb(self): + def test_export_wb_pdf(self): name_on_server = OnlineCommandTest.TWBX_WITH_EXTRACT_NAME file = os.path.join("tests", "assets", OnlineCommandTest.TWBX_FILE_WITH_EXTRACT) self._publish_wb(file, name_on_server) @@ -308,7 +300,7 @@ def test_export_wb(self): _test_command(arguments) @pytest.mark.order(15) - def test_export_view(self): + def test_export_view_pdf(self): name_on_server = OnlineCommandTest.TWBX_WITH_EXTRACT_NAME file = os.path.join("tests", "assets", OnlineCommandTest.TWBX_FILE_WITH_EXTRACT) self._publish_wb(file, name_on_server) @@ -318,7 +310,7 @@ def test_export_view(self): _test_command(arguments) @pytest.mark.order(16) - def test_delete_site_users(self): + def test_users_delete_site_users(self): if not server_admin and not site_admin: pytest.skip("Must be server or site administrator to delete site users") From ed3efeadd31cf72c8ed29ca4a52a84b48a3fb33d Mon Sep 17 00:00:00 2001 From: Jac Date: Wed, 7 Sep 2022 23:06:57 -0700 Subject: [PATCH 18/20] Trace logging and bug fix (#165) * Add trace logging details and error stack * handle url input more robustly Co-authored-by: Brian Cantoni --- tabcmd/commands/auth/session.py | 5 +-- tabcmd/commands/constants.py | 22 ++++++++++ .../get_url_command.py | 10 ++++- tabcmd/execution/__init__.py | 3 ++ tabcmd/execution/logger_config.py | 40 ++++++++++++++++--- tabcmd/execution/parent_parser.py | 2 +- tabcmd/execution/tabcmd_controller.py | 7 +++- 7 files changed, 76 insertions(+), 13 deletions(-) diff --git a/tabcmd/commands/auth/session.py b/tabcmd/commands/auth/session.py index ba98fd2d..866efbc8 100644 --- a/tabcmd/commands/auth/session.py +++ b/tabcmd/commands/auth/session.py @@ -4,7 +4,6 @@ import requests import tableauserverclient as TSC -import tableauserverclient.server.endpoint.exceptions from urllib3.exceptions import InsecureRequestWarning from tabcmd.commands.constants import Errors @@ -45,8 +44,8 @@ def __init__(self): self.timeout = None self.logging_level = "info" - self.logger = log(__class__.__name__, self.logging_level) self._read_from_json() + self.logger = log(__name__, self.logging_level) # instantiate here mostly for tests self.tableau_server = None # this one is an object that doesn't get persisted in the file # called before we connect to the server @@ -55,7 +54,6 @@ def _update_session_data(self, args): # user id and site id are never passed in as args # last_login_using and tableau_server are internal data # self.command = args.??? - # TODO: if server/username/token are changed, clear others self.username = args.username or self.username self.site_name = args.site_name or self.site_name or "" if self.site_name == "default": @@ -216,6 +214,7 @@ def create_session(self, args): self._read_existing_state() self._update_session_data(args) self.logging_level = args.logging_level or self.logging_level + self.logger = log(__name__, self.logging_level) credentials = None if args.password: diff --git a/tabcmd/commands/constants.py b/tabcmd/commands/constants.py index fe642bbb..0eaa9b3c 100644 --- a/tabcmd/commands/constants.py +++ b/tabcmd/commands/constants.py @@ -1,3 +1,4 @@ +import inspect import sys from tabcmd.execution.localize import _ @@ -29,9 +30,30 @@ def is_login_error(error): if hasattr(error, "code"): return error.code == Constants.login_error + # https://gist.github.com/FredLoney/5454553 + @staticmethod + def log_stack(logger): + try: + """The log header message formatter.""" + HEADER_FMT = "Printing Call Stack at %s::%s" + """The log stack message formatter.""" + STACK_FMT = "%s, line %d in function %s." + stack = inspect.stack() + here = stack[0] + file, line, func = here[1:4] + start = 0 + n_lines = 5 + logger.trace(HEADER_FMT % (file, func)) + for frame in stack[start + 1 : n_lines]: + file, line, func = frame[1:4] + logger.trace(STACK_FMT % (file, line, func)) + except BaseException as e: + logger.info("Error printing stack trace:", e) + @staticmethod def exit_with_error(logger, message=None, exception=None): try: + Errors.log_stack(logger) if message and not exception: logger.error(message) if exception: diff --git a/tabcmd/commands/datasources_and_workbooks/get_url_command.py b/tabcmd/commands/datasources_and_workbooks/get_url_command.py index 5b23dc4e..1e6010d9 100644 --- a/tabcmd/commands/datasources_and_workbooks/get_url_command.py +++ b/tabcmd/commands/datasources_and_workbooks/get_url_command.py @@ -37,6 +37,10 @@ def run_command(args): if " " in args.url: Errors.exit_with_error(logger, _("export.errors.white_space_workbook_view")) + if not args.url.startswith("/"): + args.url = "/" + args.url + logger.trace("helpfully fix format of url: " + args.url) + file_type = GetUrl.get_file_type_from_filename(logger, args.filename, args.url) content_type = GetUrl.evaluate_content_type(logger, args.url) if content_type == "workbook": @@ -65,9 +69,11 @@ def evaluate_content_type(logger, url): elif url.find("/workbooks/") == 0: return "workbook" else: - Errors.exit_with_error( - logger, message=_("export.errors.requires_workbook_view_param").format(__class__.__name__) + view_example = "/views//." + message = "{} [{}]".format( + _("export.errors.requires_workbook_view_param").format(__class__.__name__), view_example ) + Errors.exit_with_error(logger, message) @staticmethod def get_file_type_from_filename(logger, file_name, url): diff --git a/tabcmd/execution/__init__.py b/tabcmd/execution/__init__.py index e69de29b..26cf34a5 100644 --- a/tabcmd/execution/__init__.py +++ b/tabcmd/execution/__init__.py @@ -0,0 +1,3 @@ +from tabcmd.execution.logger_config import add_trace_level + +add_trace_level() diff --git a/tabcmd/execution/logger_config.py b/tabcmd/execution/logger_config.py index b478909e..215b3ee6 100644 --- a/tabcmd/execution/logger_config.py +++ b/tabcmd/execution/logger_config.py @@ -4,14 +4,42 @@ path = os.path.dirname(os.path.abspath(__file__)) FORMATS = { - logging.ERROR: "ERROR: %(name)-10s: %(lineno)d: %(message)s", - logging.WARN: "WARN: %(message)s", - logging.DEBUG: "DEBUG: %(name)-10s: %(lineno)d: %(message)-10s", - logging.INFO: "%(message)s", - "TRACE": "TRACE: %(asctime)-12s %(name)-10s: %(lineno)d: %(message)-10s", - "DEFAULT": "%(message)s", + logging.ERROR: "%(asctime)s %(levelname)-5s:(%(name)-10s %(filename)-10s: %(lineno)d): %(message)-30s", + logging.WARN: "%(asctime)s %(levelname)s : (%(name)-10s %(filename)-10s: %(lineno)d): %(message)-30s", + logging.INFO: "%(message)-30s", + logging.DEBUG: "%(asctime)s %(levelname)s : (%(name)-10s %(filename)-10s: %(lineno)d): %(message)-30s", } +# https://stackoverflow.com/questions/2183233/how-to-add-a-custom-loglevel-to-pythons-logging-facility +def add_log_level(level_name, level_num, method_name=None): + if not method_name: + method_name = level_name.lower() + + if hasattr(logging, level_name): + raise AttributeError("{} already defined in logging module".format(level_name)) + if hasattr(logging, method_name): + raise AttributeError("{} already defined in logging module".format(method_name)) + if hasattr(logging.getLoggerClass(), method_name): + raise AttributeError("{} already defined in logger class".format(method_name)) + + def logForLevel(self, message, *args, **kwargs): + if self.isEnabledFor(level_num): + self._log(level_num, message, args, **kwargs) + + def logToRoot(message, *args, **kwargs): + logging.log(level_num, message, *args, **kwargs) + + logging.addLevelName(level_num, level_name) + setattr(logging, level_name, level_num) + setattr(logging.getLoggerClass(), method_name, logForLevel) + setattr(logging, method_name, logToRoot) + + +def add_trace_level(): + trace_level: int = logging.DEBUG - 5 + add_log_level("TRACE", trace_level) + FORMATS[trace_level] = FORMATS[logging.ERROR] + def configure_log(name: str, logging_level_input: str): """function for logging statements to console and logfile""" diff --git a/tabcmd/execution/parent_parser.py b/tabcmd/execution/parent_parser.py index 82261f28..cb1a68d0 100644 --- a/tabcmd/execution/parent_parser.py +++ b/tabcmd/execution/parent_parser.py @@ -65,7 +65,7 @@ def parent_parser_with_global_options(self): parser.add_argument( "-l", "--logging-level", - choices=["DEBUG", "INFO", "ERROR"], + choices=["TRACE", "DEBUG", "INFO", "ERROR"], type=str.upper, # coerce input to uppercase to act case insensitive default="info", help="Use the specified logging level. The default level is INFO.", diff --git a/tabcmd/execution/tabcmd_controller.py b/tabcmd/execution/tabcmd_controller.py index f54f6b02..0dee0ad0 100644 --- a/tabcmd/execution/tabcmd_controller.py +++ b/tabcmd/execution/tabcmd_controller.py @@ -25,9 +25,14 @@ def run(parser, user_input=None): sys.exit(0) user_input = user_input or sys.argv[1:] namespace = parser.parse_args(user_input) + if namespace.logging_level: + print("logging:", namespace.logging_level) logger = log(__name__, namespace.logging_level or logging.INFO) - # logger.debug(namespace) + if namespace.password: + logger.trace(namespace.func) + else: + logger.trace(namespace) if namespace.language: set_client_locale(namespace.language, logger) From a12ee64d85e71a94aeaef97075cb308b00f4bfc2 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 8 Sep 2022 00:20:30 -0700 Subject: [PATCH 19/20] Merge branch 'main' into development --- .github/workflows/run-e2-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-e2-tests.yml b/.github/workflows/run-e2-tests.yml index 371577f5..a3601d94 100644 --- a/.github/workflows/run-e2-tests.yml +++ b/.github/workflows/run-e2-tests.yml @@ -1,4 +1,4 @@ -name: Python tests +name: e2e tests on: workflow_dispatch: @@ -42,4 +42,4 @@ jobs: - name: Run e2e tests run: | python -m tabcmd login --server "${{ github.event.inputs.server }}" --site "${{ github.event.inputs.site }}" --token-name "${{ github.event.inputs.patname }}" --token-value "${{ github.event.inputs.pat }}" - pytest -q tests\e2e\online_tests.py -r pfE + pytest -q tests/e2e/online_tests.py -r pfE From 5eb3d7c4e68bc4c7a01ef108afdb1f10d16aef27 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 8 Sep 2022 00:21:39 -0700 Subject: [PATCH 20/20] Update run-e2-tests.yml remove 3.7 and 3.8 to get past a typing bug in those versions --- .github/workflows/run-e2-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-e2-tests.yml b/.github/workflows/run-e2-tests.yml index a3601d94..26f2222c 100644 --- a/.github/workflows/run-e2-tests.yml +++ b/.github/workflows/run-e2-tests.yml @@ -18,7 +18,7 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: ['3.7', '3.8', '3.9', '3.10', '3'] + python-version: ['3.9', '3.10', '3'] runs-on: ${{ matrix.os }}