Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion tableauserverclient/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from tableauserverclient.namespace import NEW_NAMESPACE as DEFAULT_NAMESPACE
from tableauserverclient.models import (
BackgroundJobItem,
CollectionItem,
ColumnItem,
ConnectionCredentials,
ConnectionItem,
Expand Down Expand Up @@ -73,7 +74,7 @@

__all__ = [
"BackgroundJobItem",
"BackgroundJobItem",
"CollectionItem",
"ColumnItem",
"ConnectionCredentials",
"ConnectionItem",
Expand Down
2 changes: 2 additions & 0 deletions tableauserverclient/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from tableauserverclient.models.collection_item import CollectionItem
from tableauserverclient.models.column_item import ColumnItem
from tableauserverclient.models.connection_credentials import ConnectionCredentials
from tableauserverclient.models.connection_item import ConnectionItem
Expand Down Expand Up @@ -53,6 +54,7 @@
from tableauserverclient.models.extract_item import ExtractItem

__all__ = [
"CollectionItem",
"ColumnItem",
"ConnectionCredentials",
"ConnectionItem",
Expand Down
52 changes: 52 additions & 0 deletions tableauserverclient/models/collection_item.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from datetime import datetime
from typing import Optional
from xml.etree.ElementTree import Element

from defusedxml.ElementTree import fromstring
from typing_extensions import Self

from tableauserverclient.datetime_helpers import parse_datetime
from tableauserverclient.models.user_item import UserItem


class CollectionItem:
def __init__(self) -> None:
self.id: Optional[str] = None
self.name: Optional[str] = None
self.description: Optional[str] = None
self.created_at: Optional[datetime] = None
self.updated_at: Optional[datetime] = None
self.owner: Optional[UserItem] = None
self.total_item_count: Optional[int] = None
self.permissioned_item_count: Optional[int] = None
self.visibility: Optional[str] = None # Assuming visibility is a string, adjust as necessary

@classmethod
def from_response(cls, response: bytes, ns) -> list[Self]:
parsed_response = fromstring(response)

collection_elements = parsed_response.findall(".//t:collection", namespaces=ns)
if not collection_elements:
raise ValueError("No collection element found in the response")

collections = [cls.from_xml(c, ns) for c in collection_elements]
return collections

@classmethod
def from_xml(cls, xml: Element, ns) -> Self:
collection_item = cls()
collection_item.id = xml.get("id")
collection_item.name = xml.get("name")
collection_item.description = xml.get("description")
collection_item.created_at = parse_datetime(xml.get("createdAt"))
collection_item.updated_at = parse_datetime(xml.get("updatedAt"))
owner_element = xml.find(".//t:owner", namespaces=ns)
if owner_element is not None:
collection_item.owner = UserItem.from_xml(owner_element, ns)
else:
collection_item.owner = None
collection_item.total_item_count = int(xml.get("totalItemCount", 0))
collection_item.permissioned_item_count = int(xml.get("permissionedItemCount", 0))
collection_item.visibility = xml.get("visibility")

return collection_item
29 changes: 21 additions & 8 deletions tableauserverclient/models/favorites_item.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import logging

from typing import Union
from typing import TypedDict, Union
from defusedxml.ElementTree import fromstring

from tableauserverclient.models.tableau_types import TableauItem
from tableauserverclient.models.collection_item import CollectionItem
from tableauserverclient.models.datasource_item import DatasourceItem
from tableauserverclient.models.flow_item import FlowItem
from tableauserverclient.models.project_item import ProjectItem
Expand All @@ -13,16 +12,22 @@

from tableauserverclient.helpers.logging import logger

FavoriteType = dict[
str,
list[TableauItem],
]

class FavoriteType(TypedDict):
collections: list[CollectionItem]
datasources: list[DatasourceItem]
flows: list[FlowItem]
projects: list[ProjectItem]
metrics: list[MetricItem]
views: list[ViewItem]
workbooks: list[WorkbookItem]


class FavoriteItem:
@classmethod
def from_response(cls, xml: Union[str, bytes], namespace: dict) -> FavoriteType:
favorites: FavoriteType = {
"collections": [],
"datasources": [],
"flows": [],
"projects": [],
Expand All @@ -32,6 +37,7 @@ def from_response(cls, xml: Union[str, bytes], namespace: dict) -> FavoriteType:
}
parsed_response = fromstring(xml)

collections_xml = parsed_response.findall(".//t:favorite/t:collection", namespace)
datasources_xml = parsed_response.findall(".//t:favorite/t:datasource", namespace)
flows_xml = parsed_response.findall(".//t:favorite/t:flow", namespace)
metrics_xml = parsed_response.findall(".//t:favorite/t:metric", namespace)
Expand All @@ -40,13 +46,14 @@ def from_response(cls, xml: Union[str, bytes], namespace: dict) -> FavoriteType:
workbooks_xml = parsed_response.findall(".//t:favorite/t:workbook", namespace)

logger.debug(
"ds: {}, flows: {}, metrics: {}, projects: {}, views: {}, wbs: {}".format(
"ds: {}, flows: {}, metrics: {}, projects: {}, views: {}, wbs: {}, collections: {}".format(
len(datasources_xml),
len(flows_xml),
len(metrics_xml),
len(projects_xml),
len(views_xml),
len(workbooks_xml),
len(collections_xml),
)
)
for datasource in datasources_xml:
Expand Down Expand Up @@ -85,5 +92,11 @@ def from_response(cls, xml: Union[str, bytes], namespace: dict) -> FavoriteType:
logger.debug(fav_workbook)
favorites["workbooks"].append(fav_workbook)

for collection in collections_xml:
fav_collection = CollectionItem.from_xml(collection, namespace)
if fav_collection:
logger.debug(fav_collection)
favorites["collections"].append(fav_collection)

logger.debug(favorites)
return favorites
5 changes: 3 additions & 2 deletions tableauserverclient/models/user_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

if TYPE_CHECKING:
from tableauserverclient.server import Pager
from tableauserverclient.models.favorites_item import FavoriteType


class UserItem:
Expand Down Expand Up @@ -131,7 +132,7 @@ def __init__(
self._id: Optional[str] = None
self._last_login: Optional[datetime] = None
self._workbooks = None
self._favorites: Optional[dict[str, list]] = None
self._favorites: Optional["FavoriteType"] = None
self._groups = None
self.email: Optional[str] = None
self.fullname: Optional[str] = None
Expand Down Expand Up @@ -218,7 +219,7 @@ def workbooks(self) -> "Pager":
return self._workbooks()

@property
def favorites(self) -> dict[str, list]:
def favorites(self) -> "FavoriteType":
if self._favorites is None:
error = "User item must be populated with favorites first."
raise UnpopulatedPropertyError(error)
Expand Down
14 changes: 13 additions & 1 deletion test/assets/favorites_get.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,17 @@
<tags />
</datasource>
</favorite>
<favorite>
<collection id="8c57cb8a-d65f-4a32-813e-5a3f86e8f94e"
name="sample collection"
description="description for sample collection"
totalItemCount="3"
permissionedItemCount="2"
visibility="Private"
createdAt="2016-08-11T21:22:40Z"
updatedAt="2016-08-11T21:34:17Z">
<owner id="5de011f8-5aa9-4d5b-b991-f462c8dd6bb7" />
</collection>
</favorite>
</favorites>
</tsResponse>
</tsResponse>
12 changes: 12 additions & 0 deletions test/test_favorites.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import requests_mock

import tableauserverclient as TSC
from tableauserverclient.datetime_helpers import parse_datetime
from ._utils import read_xml_asset

GET_FAVORITES_XML = "favorites_get.xml"
Expand Down Expand Up @@ -48,6 +49,17 @@ def test_get(self) -> None:
self.assertEqual(datasource.id, "e76a1461-3b1d-4588-bf1b-17551a879ad9")
self.assertEqual(project.id, "1d0304cd-3796-429f-b815-7258370b9b74")

collection = self.user.favorites["collections"][0]

assert collection.id == "8c57cb8a-d65f-4a32-813e-5a3f86e8f94e"
assert collection.name == "sample collection"
assert collection.description == "description for sample collection"
assert collection.total_item_count == 3
assert collection.permissioned_item_count == 2
assert collection.visibility == "Private"
assert collection.created_at == parse_datetime("2016-08-11T21:22:40Z")
assert collection.updated_at == parse_datetime("2016-08-11T21:34:17Z")

def test_add_favorite_workbook(self) -> None:
response_xml = read_xml_asset(ADD_FAVORITE_WORKBOOK_XML)
workbook = TSC.WorkbookItem("")
Expand Down
Loading