diff --git a/tableauserverclient/models/custom_view_item.py b/tableauserverclient/models/custom_view_item.py index a0c0a984..623e8060 100644 --- a/tableauserverclient/models/custom_view_item.py +++ b/tableauserverclient/models/custom_view_item.py @@ -5,14 +5,57 @@ from typing import Callable, Optional from collections.abc import Iterator -from .exceptions import UnpopulatedPropertyError -from .user_item import UserItem -from .view_item import ViewItem -from .workbook_item import WorkbookItem -from ..datetime_helpers import parse_datetime +from tableauserverclient.models.exceptions import UnpopulatedPropertyError +from tableauserverclient.models.user_item import UserItem +from tableauserverclient.models.view_item import ViewItem +from tableauserverclient.models.workbook_item import WorkbookItem +from tableauserverclient.datetime_helpers import parse_datetime class CustomViewItem: + """ + Represents a Custom View item on Tableau Server. + + Parameters + ---------- + id : Optional[str] + The ID of the Custom View item. + + name : Optional[str] + The name of the Custom View item. + + Attributes + ---------- + content_url : Optional[str] + The content URL of the Custom View item. + + created_at : Optional[datetime] + The date and time the Custom View item was created. + + image: bytes + The image of the Custom View item. Must be populated first. + + pdf: bytes + The PDF of the Custom View item. Must be populated first. + + csv: Iterator[bytes] + The CSV of the Custom View item. Must be populated first. + + shared : Optional[bool] + Whether the Custom View item is shared. + + updated_at : Optional[datetime] + The date and time the Custom View item was last updated. + + owner : Optional[UserItem] + The id of the owner of the Custom View item. + + workbook : Optional[WorkbookItem] + The id of the workbook the Custom View item belongs to. + + view : Optional[ViewItem] + The id of the view the Custom View item belongs to. + """ def __init__(self, id: Optional[str] = None, name: Optional[str] = None) -> None: self._content_url: Optional[str] = None # ? self._created_at: Optional["datetime"] = None diff --git a/tableauserverclient/server/endpoint/custom_views_endpoint.py b/tableauserverclient/server/endpoint/custom_views_endpoint.py index b02b05d7..8d78dca7 100644 --- a/tableauserverclient/server/endpoint/custom_views_endpoint.py +++ b/tableauserverclient/server/endpoint/custom_views_endpoint.py @@ -3,7 +3,7 @@ import os from contextlib import closing from pathlib import Path -from typing import Optional, Union +from typing import Optional, Union, TYPE_CHECKING from collections.abc import Iterator from tableauserverclient.config import BYTES_PER_MB, config @@ -21,6 +21,9 @@ from tableauserverclient.helpers.logging import logger +if TYPE_CHECKING: + from tableauserverclient.server.query import QuerySet + """ Get a list of custom views on a site get the details of a custom view @@ -51,19 +54,31 @@ def baseurl(self) -> str: def expurl(self) -> str: return f"{self.parent_srv._server_address}/api/exp/sites/{self.parent_srv.site_id}/customviews" - """ - If the request has no filter parameters: Administrators will see all custom views. - Other users will see only custom views that they own. - If the filter parameters include ownerId: Users will see only custom views that they own. - If the filter parameters include viewId and/or workbookId, and don't include ownerId: - Users will see those custom views that they have Write and WebAuthoring permissions for. - If site user visibility is not set to Limited, the Users will see those custom views that are "public", - meaning the value of their shared attribute is true. - If site user visibility is set to Limited, ???? - """ - @api(version="3.18") def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[CustomViewItem], PaginationItem]: + """ + Get a list of custom views on a site. + + If the request has no filter parameters: Administrators will see all custom views. + Other users will see only custom views that they own. + If the filter parameters include ownerId: Users will see only custom views that they own. + If the filter parameters include viewId and/or workbookId, and don't include ownerId: + Users will see those custom views that they have Write and WebAuthoring permissions for. + If site user visibility is not set to Limited, the Users will see those custom views that are "public", + meaning the value of their shared attribute is true. + If site user visibility is set to Limited, ???? + + Rest API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#list_custom_views + + Parameters + ---------- + req_options : RequestOptions, optional + Filtering options for the request, by default None + + Returns + ------- + tuple[list[CustomViewItem], PaginationItem] + """ logger.info("Querying all custom views on site") url = self.baseurl server_response = self.get_request(url, req_options) @@ -73,6 +88,19 @@ def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[Cust @api(version="3.18") def get_by_id(self, view_id: str) -> Optional[CustomViewItem]: + """ + Get the details of a specific custom view. + + Rest API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#get_custom_view + + Parameters + ---------- + view_id : str + + Returns + ------- + Optional[CustomViewItem] + """ if not view_id: error = "Custom view item missing ID." raise MissingRequiredFieldError(error) @@ -83,6 +111,27 @@ def get_by_id(self, view_id: str) -> Optional[CustomViewItem]: @api(version="3.18") def populate_image(self, view_item: CustomViewItem, req_options: Optional["ImageRequestOptions"] = None) -> None: + """ + Populate the image of a custom view. + + Rest API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#get_custom_view_image + + Parameters + ---------- + view_item : CustomViewItem + + req_options : ImageRequestOptions, optional + Options to customize the image returned, by default None + + Returns + ------- + None + + Raises + ------ + MissingRequiredFieldError + If the view_item is missing an ID + """ if not view_item.id: error = "Custom View item missing ID." raise MissingRequiredFieldError(error) @@ -101,6 +150,26 @@ def _get_view_image(self, view_item: CustomViewItem, req_options: Optional["Imag @api(version="3.23") def populate_pdf(self, custom_view_item: CustomViewItem, req_options: Optional["PDFRequestOptions"] = None) -> None: + """ + Populate the PDF of a custom view. + + Parameters + ---------- + custom_view_item : CustomViewItem + The custom view item to populate the PDF for. + + req_options : PDFRequestOptions, optional + Options to customize the PDF returned, by default None + + Returns + ------- + None + + Raises + ------ + MissingRequiredFieldError + If the custom view item is missing an ID + """ if not custom_view_item.id: error = "Custom View item missing ID." raise MissingRequiredFieldError(error) @@ -121,6 +190,26 @@ def _get_custom_view_pdf( @api(version="3.23") def populate_csv(self, custom_view_item: CustomViewItem, req_options: Optional["CSVRequestOptions"] = None) -> None: + """ + Populate the CSV of a custom view. + + Parameters + ---------- + custom_view_item : CustomViewItem + The custom view item to populate the CSV for. + + req_options : CSVRequestOptions, optional + Options to customize the CSV returned, by default None + + Returns + ------- + None + + Raises + ------ + MissingRequiredFieldError + If the custom view item is missing an ID + """ if not custom_view_item.id: error = "Custom View item missing ID." raise MissingRequiredFieldError(error) @@ -141,6 +230,21 @@ def _get_custom_view_csv( @api(version="3.18") def update(self, view_item: CustomViewItem) -> Optional[CustomViewItem]: + """ + Updates the name, owner, or shared status of a custom view. + + Rest API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#update_custom_view + + Parameters + ---------- + view_item : CustomViewItem + The custom view item to update. + + Returns + ------- + Optional[CustomViewItem] + The updated custom view item. + """ if not view_item.id: error = "Custom view item missing ID." raise MissingRequiredFieldError(error) @@ -158,6 +262,25 @@ def update(self, view_item: CustomViewItem) -> Optional[CustomViewItem]: # Delete 1 view by id @api(version="3.19") def delete(self, view_id: str) -> None: + """ + Deletes a single custom view by ID. + + Rest API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#delete_custom_view + + Parameters + ---------- + view_id : str + The ID of the custom view to delete. + + Returns + ------- + None + + Raises + ------ + ValueError + If the view_id is not provided. + """ if not view_id: error = "Custom View ID undefined." raise ValueError(error) @@ -167,6 +290,27 @@ def delete(self, view_id: str) -> None: @api(version="3.21") def download(self, view_item: CustomViewItem, file: PathOrFileW) -> PathOrFileW: + """ + Download the definition of a custom view as json. The file parameter can + be a file path or a file object. If a file path is provided, the file + will be written to that location. If a file object is provided, the file + will be written to that object. + + May contain sensitive information. + + Parameters + ---------- + view_item : CustomViewItem + The custom view item to download. + + file : PathOrFileW + The file path or file object to write the custom view to. + + Returns + ------- + PathOrFileW + The file path or file object that the custom view was written to. + """ url = f"{self.expurl}/{view_item.id}/content" server_response = self.get_request(url) if isinstance(file, io_types_w): @@ -180,6 +324,25 @@ def download(self, view_item: CustomViewItem, file: PathOrFileW) -> PathOrFileW: @api(version="3.21") def publish(self, view_item: CustomViewItem, file: PathOrFileR) -> Optional[CustomViewItem]: + """ + Publish a custom view to Tableau Server. The file parameter can be a + file path or a file object. If a file path is provided, the file will be + read from that location. If a file object is provided, the file will be + read from that object. + + Parameters + ---------- + view_item : CustomViewItem + The custom view item to publish. + + file : PathOrFileR + The file path or file object to read the custom view from. + + Returns + ------- + Optional[CustomViewItem] + The published custom view item. + """ url = self.expurl if isinstance(file, io_types_r): size = get_file_object_size(file) @@ -207,3 +370,25 @@ def publish(self, view_item: CustomViewItem, file: PathOrFileR) -> Optional[Cust server_response = self.post_request(url, xml_request, content_type) return CustomViewItem.from_response(server_response.content, self.parent_srv.namespace) + + def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> "QuerySet[CustomViewItem]": + """ + Queries the Tableau Server for items using the specified filters. Page + size can be specified to limit the number of items returned in a single + request. If not specified, the default page size is 100. Page size can + be an integer between 1 and 1000. + + No positional arguments are allowed. All filters must be specified as + keyword arguments. If you use the equality operator, you can specify it + through =. If you want to use a different operator, + you can specify it through __=. Field + names can either be in snake_case or camelCase. + + This endpoint supports the following fields and operators: + + view_id=... + workbook_id=... + owner_id=... + """ + + return super().filter(*invalid, page_size=page_size, **kwargs) diff --git a/tableauserverclient/server/request_options.py b/tableauserverclient/server/request_options.py index d79ac7f7..2a5bb805 100644 --- a/tableauserverclient/server/request_options.py +++ b/tableauserverclient/server/request_options.py @@ -122,6 +122,7 @@ class Field: NotificationType = "notificationType" OwnerDomain = "ownerDomain" OwnerEmail = "ownerEmail" + OwnerId = "ownerId" OwnerName = "ownerName" ParentProjectId = "parentProjectId" Priority = "priority" @@ -148,8 +149,10 @@ class Field: UpdatedAt = "updatedAt" UserCount = "userCount" UserId = "userId" + ViewId = "viewId" ViewUrlName = "viewUrlName" WorkbookDescription = "workbookDescription" + WorkbookId = "workbookId" WorkbookName = "workbookName" class Direction: