Example #1
0
        def __construct_file_dict_tree(folder: str) -> dict:
            """
            Recurses through the project directories.

            Constructs a file tree by subsequent calls to the API
            """
            try:
                sorted_files_folders = __api_call_list_files(folder)
            except exceptions.NoDataError as e:
                if folder is None:
                    raise exceptions.NoDataError(
                        "No files or folders found for the specified project"
                    )
                else:
                    raise exceptions.NoDataError(f"Could not find folder: '{folder}'")

            tree = {}

            for f in sorted_files_folders:
                is_folder = f.pop("folder")
                name = f["name"]
                if not is_folder:
                    tree[name] = {"name": name, "is_folder": False, "children": {}}
                    if show_size:
                        tree[f["name"]]["size"] = f.get("size")
                else:
                    children = __construct_file_dict_tree(
                        os.path.join(folder, name) if folder else name
                    )
                    tree[name] = {"name": name, "is_folder": True, "children": children}

            return tree
Example #2
0
        def __construct_file_tree(folder: str, basename: str) -> Tuple[FileTree, int, int]:
            """
            Recurses through the project directories.

            Constructs a file tree by subsequent calls to the API
            """
            tree = FileTree([], f"{basename}/")
            try:
                sorted_files_folders = __api_call_list_files(folder)
            except exceptions.NoDataError as e:
                if folder is None:
                    raise exceptions.NoDataError(
                        "No files or folders found for the specified project"
                    )
                else:
                    raise exceptions.NoDataError(f"Could not find folder: '{escape(folder)}'")

            # Get max length of file name
            max_string = max([len(x["name"]) for x in sorted_files_folders])

            # Get max length of size string
            max_size = max(
                [
                    len(x["size"].split(" ")[0])
                    for x in sorted_files_folders
                    if show_size and "size" in x
                ],
                default=0,
            )
            # Rich outputs precisely one line per file/folder
            for f in sorted_files_folders:
                is_folder = f.pop("folder")

                if not is_folder:
                    tree.subtrees.append((escape(f["name"]), f.get("size") if show_size else None))
                else:
                    subtree, _max_string, _max_size = __construct_file_tree(
                        os.path.join(folder, f["name"]) if folder else f["name"],
                        f"[bold deep_sky_blue3]{escape(f['name'])}",
                    )
                    # Due to indentation, the filename strings of
                    # subdirectories are 4 characters deeper than
                    # their parent directories
                    max_string = max(max_string, _max_string + 4)
                    max_size = max(max_size, _max_size)
                    tree.subtrees.append(subtree)

            return tree, max_string, max_size
Example #3
0
    def __print_users_table(self, research_users):
        # TODO: call on future list_project_users function, will be implemented for dds user ls --project
        default_format = {"justify": "left", "style": "", "footer": "", "overflow": "fold"}
        column_formatting = {
            **{x: default_format for x in ["User Name", "Primary email", "Role"]},
        }
        table = Table(
            title="Project User(s)",
            show_header=True,
            header_style="bold",
        )
        # Add columns to table
        for colname, colformat in column_formatting.items():
            table.add_column(
                colname,
                justify=colformat["justify"],
                style=colformat["style"],
                footer=colformat["footer"],
                overflow=colformat["overflow"],
            )

        # Add all column values for each row to table
        for user in research_users:
            table.add_row(*[user[i] for i in column_formatting])

        # Print to stdout if there are any lines
        if table.rows:
            # Use a pager if output is taller than the visible terminal
            if len(research_users) + 5 > dds_cli.utils.console.height:
                with dds_cli.utils.console.pager():
                    dds_cli.utils.console.print(table)
            else:
                dds_cli.utils.console.print(table)
        else:
            raise exceptions.NoDataError("No users found.")
Example #4
0
        def __api_call_list_files(folder: str):
            # Make call to API
            try:
                resp_json = requests.get(
                    DDSEndpoint.LIST_FILES,
                    params={"project": self.project},
                    json={"subpath": folder, "show_size": show_size},
                    headers=self.token,
                    timeout=DDSEndpoint.TIMEOUT,
                )
            except requests.exceptions.RequestException as err:
                raise exceptions.APIError(f"Problem with database response: '{err}'")

            resp_json = resp_json.json()

            if not "files_folders" in resp_json:
                raise exceptions.NoDataError(f"Could not find folder: '{folder}'")

            sorted_files_folders = sorted(resp_json["files_folders"], key=lambda f: f["name"])

            if not sorted_files_folders:
                raise exceptions.NoDataError(f"Could not find folder: '{folder}'")

            return sorted_files_folders
Example #5
0
    def list_projects(self, sort_by="Updated"):
        """Get a list of project(s) the user is involved in."""
        # Get projects from API
        try:
            response = requests.get(
                DDSEndpoint.LIST_PROJ,
                headers=self.token,
                json={"usage": self.show_usage},
                timeout=DDSEndpoint.TIMEOUT,
            )
        except requests.exceptions.RequestException as err:
            raise exceptions.ApiRequestError(
                message=(
                    "Failed to get list of projects"
                    + (
                        ": The database seems to be down."
                        if isinstance(err, requests.exceptions.ConnectionError)
                        else "."
                    )
                )
            )

        # Check response
        if not response.ok:
            raise exceptions.APIError(f"Failed to get any projects: {response.text}")

        # Get result from API
        try:
            resp_json = response.json()
        except simplejson.JSONDecodeError as err:
            raise exceptions.APIError(f"Could not decode JSON response: {err}")

        # Cancel if user not involved in any projects
        usage_info = resp_json.get("total_usage")
        total_size = resp_json.get("total_size")
        project_info = resp_json.get("project_info")
        always_show = resp_json.get("always_show", False)
        if not project_info:
            raise exceptions.NoDataError("No project info was retrieved. No files to list.")

        for project in project_info:
            try:
                last_updated = pytz.timezone("UTC").localize(
                    datetime.datetime.strptime(project["Last updated"], "%a, %d %b %Y %H:%M:%S GMT")
                )
            except ValueError as err:
                raise exceptions.ApiResponseError(
                    f"Time zone mismatch: Incorrect zone '{project['Last updated'].split()[-1]}'"
                )
            else:
                project["Last updated"] = last_updated.astimezone(tzlocal.get_localzone()).strftime(
                    "%a, %d %b %Y %H:%M:%S %Z"
                )

        # Sort projects according to chosen or default, first ID
        sorted_projects = self.__sort_projects(projects=project_info, sort_by=sort_by)

        if not self.json:
            self.__print_project_table(sorted_projects, usage_info, total_size, always_show)

        # Return the list of projects
        return sorted_projects
Example #6
0
    def list_files(self, folder: str = None, show_size: bool = False):
        """Create a tree displaying the files within the project."""
        LOG.info(f"Listing files for project '{self.project}'")
        if folder:
            LOG.info(f"Showing files in folder '{escape(folder)}'")

        if folder is None:
            folder = ""
        # Make call to API
        try:
            response = requests.get(
                DDSEndpoint.LIST_FILES,
                params={"project": self.project},
                json={"subpath": folder, "show_size": show_size},
                headers=self.token,
                timeout=DDSEndpoint.TIMEOUT,
            )
        except requests.exceptions.RequestException as err:
            raise exceptions.APIError(
                message=(
                    f"Failed to get list of files in project '{self.project}'"
                    + (
                        ": The database seems to be down."
                        if isinstance(err, requests.exceptions.ConnectionError)
                        else "."
                    )
                )
            )

        if not response.ok:
            raise exceptions.APIError(f"Failed to get list of files: '{response.text}'")

        # Get response
        try:
            resp_json = response.json()
        except simplejson.JSONDecodeError as err:
            raise exceptions.APIError(f"Could not decode JSON response: '{err}'")

        # Check if project empty
        if "num_items" in resp_json and resp_json["num_items"] == 0:
            raise exceptions.NoDataError(f"Project '{self.project}' is empty.")

        # Get files
        files_folders = resp_json["files_folders"]

        # Sort the file/folders according to names
        sorted_files_folders = sorted(files_folders, key=lambda f: f["name"])

        # Create tree
        tree_title = escape(folder) or f"Files / directories in project: [green]{self.project}"
        tree = Tree(f"[bold magenta]{tree_title}")

        if not sorted_files_folders:
            raise exceptions.NoDataError(f"Could not find folder: '{escape(folder)}'")

        # Get max length of file name
        max_string = max([len(x["name"]) for x in sorted_files_folders])

        # Get max length of size string
        max_size = max(
            [
                len(
                    dds_cli.utils.format_api_response(
                        response=x["size"], key="Size", binary=self.binary
                    ).split(" ", maxsplit=1)[0]
                )
                for x in sorted_files_folders
                if show_size and "size" in x
            ],
            default=0,
        )

        # Visible folders
        visible_folders = []

        # Add items to tree
        for x in sorted_files_folders:
            # Check if string is folder
            is_folder = x.pop("folder")

            # Att 1 for folders due to trailing /
            tab = th.TextHandler.format_tabs(
                string_len=len(x["name"]) + (1 if is_folder else 0),
                max_string_len=max_string,
            )

            # Add formatting if folder and set string name
            line = ""
            if is_folder:
                line = "[bold deep_sky_blue3]"
                visible_folders.append(x["name"])
            line += escape(x["name"]) + ("/" if is_folder else "")

            # Add size to line if option specified
            if show_size and "size" in x:
                size = dds_cli.utils.format_api_response(
                    response=x["size"], key="Size", binary=self.binary
                )
                line += f"{tab}{size.split()[0]}"

                # Define space between number and size format
                tabs_bf_format = th.TextHandler.format_tabs(
                    string_len=len(size), max_string_len=max_size, tab_len=2
                )
                line += f"{tabs_bf_format}{size.split()[1]}"
            tree.add(line)

        # Print output to stdout
        if len(files_folders) + 5 > dds_cli.utils.console.height:
            with dds_cli.utils.console.pager():
                dds_cli.utils.console.print(Padding(tree, 1))
        else:
            dds_cli.utils.console.print(Padding(tree, 1))

        # Return variable
        return visible_folders