Skip to content

Commit e874411

Browse files
committed
feat: views support all fields
1 parent e004f60 commit e874411

File tree

7 files changed

+263
-6
lines changed

7 files changed

+263
-6
lines changed

tableauserverclient/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
LinkedTaskItem,
2626
LinkedTaskStepItem,
2727
LinkedTaskFlowRunItem,
28+
LocationItem,
2829
MetricItem,
2930
MonthlyInterval,
3031
PaginationItem,
@@ -100,6 +101,7 @@
100101
"LinkedTaskFlowRunItem",
101102
"LinkedTaskItem",
102103
"LinkedTaskStepItem",
104+
"LocationItem",
103105
"MetricItem",
104106
"MissingRequiredFieldError",
105107
"MonthlyInterval",

tableauserverclient/models/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
LinkedTaskStepItem,
2929
LinkedTaskFlowRunItem,
3030
)
31+
from tableauserverclient.models.location_item import LocationItem
3132
from tableauserverclient.models.metric_item import MetricItem
3233
from tableauserverclient.models.pagination_item import PaginationItem
3334
from tableauserverclient.models.permissions_item import PermissionsRule, Permission
@@ -75,6 +76,7 @@
7576
"MonthlyInterval",
7677
"HourlyInterval",
7778
"BackgroundJobItem",
79+
"LocationItem",
7880
"MetricItem",
7981
"PaginationItem",
8082
"Permission",

tableauserverclient/models/user_item.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,12 @@ def from_response_as_owner(cls, resp, ns) -> list["UserItem"]:
249249
element_name = ".//t:owner"
250250
return cls._parse_xml(element_name, resp, ns)
251251

252+
@classmethod
253+
def from_xml(cls, xml: ET.Element, ns: Optional[dict] = None) -> "UserItem":
254+
item = cls()
255+
item._set_values(*cls._parse_element(xml, ns))
256+
return item
257+
252258
@classmethod
253259
def _parse_xml(cls, element_name, resp, ns):
254260
all_user_items = []

tableauserverclient/models/view_item.py

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
import copy
22
from datetime import datetime
33
from requests import Response
4-
from typing import Callable, Optional
4+
from typing import TYPE_CHECKING, Callable, Optional, overload
55
from collections.abc import Iterator
66

77
from defusedxml.ElementTree import fromstring
88

99
from tableauserverclient.datetime_helpers import parse_datetime
1010
from tableauserverclient.models.exceptions import UnpopulatedPropertyError
11+
from tableauserverclient.models.location_item import LocationItem
1112
from tableauserverclient.models.permissions_item import PermissionsRule
13+
from tableauserverclient.models.project_item import ProjectItem
1214
from tableauserverclient.models.tag_item import TagItem
15+
from tableauserverclient.models.user_item import UserItem
16+
17+
if TYPE_CHECKING:
18+
from tableauserverclient.models.workbook_item import WorkbookItem
1319

1420

1521
class ViewItem:
@@ -84,11 +90,18 @@ def __init__(self) -> None:
8490
self._workbook_id: Optional[str] = None
8591
self._permissions: Optional[Callable[[], list[PermissionsRule]]] = None
8692
self.tags: set[str] = set()
93+
self._favorites_total: Optional[int] = None
94+
self._view_url_name: Optional[str] = None
8795
self._data_acceleration_config = {
8896
"acceleration_enabled": None,
8997
"acceleration_status": None,
9098
}
9199

100+
self._owner: Optional[UserItem] = None
101+
self._project: Optional[ProjectItem] = None
102+
self._workbook: Optional["WorkbookItem"] = None
103+
self._location: Optional[LocationItem] = None
104+
92105
def __str__(self):
93106
return "<ViewItem {} '{}' contentUrl='{}' project={}>".format(
94107
self._id, self.name, self.content_url, self.project_id
@@ -190,6 +203,14 @@ def updated_at(self) -> Optional[datetime]:
190203
def workbook_id(self) -> Optional[str]:
191204
return self._workbook_id
192205

206+
@property
207+
def view_url_name(self) -> Optional[str]:
208+
return self._view_url_name
209+
210+
@property
211+
def favorites_total(self) -> Optional[int]:
212+
return self._favorites_total
213+
193214
@property
194215
def data_acceleration_config(self):
195216
return self._data_acceleration_config
@@ -198,6 +219,22 @@ def data_acceleration_config(self):
198219
def data_acceleration_config(self, value):
199220
self._data_acceleration_config = value
200221

222+
@property
223+
def project(self) -> Optional["ProjectItem"]:
224+
return self._project
225+
226+
@property
227+
def workbook(self) -> Optional["WorkbookItem"]:
228+
return self._workbook
229+
230+
@property
231+
def owner(self) -> Optional[UserItem]:
232+
return self._owner
233+
234+
@property
235+
def location(self) -> Optional[LocationItem]:
236+
return self._location
237+
201238
@property
202239
def permissions(self) -> list[PermissionsRule]:
203240
if self._permissions is None:
@@ -228,30 +265,43 @@ def from_xml(cls, view_xml, ns, workbook_id="") -> "ViewItem":
228265
workbook_elem = view_xml.find(".//t:workbook", namespaces=ns)
229266
owner_elem = view_xml.find(".//t:owner", namespaces=ns)
230267
project_elem = view_xml.find(".//t:project", namespaces=ns)
231-
tags_elem = view_xml.find(".//t:tags", namespaces=ns)
268+
tags_elem = view_xml.find("./t:tags", namespaces=ns)
232269
data_acceleration_config_elem = view_xml.find(".//t:dataAccelerationConfig", namespaces=ns)
233270
view_item._created_at = parse_datetime(view_xml.get("createdAt", None))
234271
view_item._updated_at = parse_datetime(view_xml.get("updatedAt", None))
235272
view_item._id = view_xml.get("id", None)
236273
view_item._name = view_xml.get("name", None)
237274
view_item._content_url = view_xml.get("contentUrl", None)
238275
view_item._sheet_type = view_xml.get("sheetType", None)
276+
view_item._favorites_total = string_to_int(view_xml.get("favoritesTotal", None))
277+
view_item._view_url_name = view_xml.get("viewUrlName", None)
239278
if usage_elem is not None:
240279
total_view = usage_elem.get("totalViewCount", None)
241280
if total_view:
242281
view_item._total_views = int(total_view)
243282
if owner_elem is not None:
283+
user = UserItem.from_xml(owner_elem, ns)
284+
view_item._owner = user
244285
view_item._owner_id = owner_elem.get("id", None)
245286
if project_elem is not None:
246-
view_item._project_id = project_elem.get("id", None)
287+
project_item = ProjectItem.from_xml(project_elem, ns)
288+
view_item._project = project_item
289+
view_item._project_id = project_item.id
247290
if workbook_id:
248291
view_item._workbook_id = workbook_id
249292
elif workbook_elem is not None:
250-
view_item._workbook_id = workbook_elem.get("id", None)
293+
from tableauserverclient.models.workbook_item import WorkbookItem
294+
295+
workbook_item = WorkbookItem.from_xml(workbook_elem, ns)
296+
view_item._workbook = workbook_item
297+
view_item._workbook_id = workbook_item.id
251298
if tags_elem is not None:
252299
tags = TagItem.from_xml_element(tags_elem, ns)
253300
view_item.tags = tags
254301
view_item._initial_tags = copy.copy(tags)
302+
if (location_elem := view_xml.find(".//t:location", namespaces=ns)) is not None:
303+
location = LocationItem.from_xml(location_elem, ns)
304+
view_item._location = location
255305
if data_acceleration_config_elem is not None:
256306
data_acceleration_config = parse_data_acceleration_config(data_acceleration_config_elem)
257307
view_item.data_acceleration_config = data_acceleration_config
@@ -274,3 +324,15 @@ def parse_data_acceleration_config(data_acceleration_elem):
274324

275325
def string_to_bool(s: str) -> bool:
276326
return s.lower() == "true"
327+
328+
329+
@overload
330+
def string_to_int(s: None) -> None: ...
331+
332+
333+
@overload
334+
def string_to_int(s: str) -> int: ...
335+
336+
337+
def string_to_int(s):
338+
return int(s) if s is not None else None

tableauserverclient/models/workbook_item.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import datetime
33
import uuid
44
import xml.etree.ElementTree as ET
5-
from typing import Callable, Optional
5+
from typing import Callable, Optional, overload
66

77
from defusedxml.ElementTree import fromstring
88

@@ -139,6 +139,8 @@ def __init__(
139139
self._permissions = None
140140
self.thumbnails_user_id = thumbnails_user_id
141141
self.thumbnails_group_id = thumbnails_group_id
142+
self._sheet_count: Optional[int] = None
143+
self._has_extracts: Optional[bool] = None
142144

143145
return None
144146

@@ -234,6 +236,14 @@ def show_tabs(self, value: bool):
234236
def size(self):
235237
return self._size
236238

239+
@property
240+
def sheet_count(self) -> Optional[int]:
241+
return self._sheet_count
242+
243+
@property
244+
def has_extracts(self) -> Optional[bool]:
245+
return self._has_extracts
246+
237247
@property
238248
def updated_at(self) -> Optional[datetime.datetime]:
239249
return self._updated_at
@@ -342,6 +352,8 @@ def _parse_common_tags(self, workbook_xml, ns):
342352
views,
343353
data_acceleration_config,
344354
data_freshness_policy,
355+
sheet_count,
356+
has_extracts,
345357
) = self._parse_element(workbook_xml, ns)
346358

347359
self._set_values(
@@ -361,6 +373,8 @@ def _parse_common_tags(self, workbook_xml, ns):
361373
views,
362374
data_acceleration_config,
363375
data_freshness_policy,
376+
sheet_count,
377+
has_extracts,
364378
)
365379

366380
return self
@@ -383,6 +397,8 @@ def _set_values(
383397
views,
384398
data_acceleration_config,
385399
data_freshness_policy,
400+
sheet_count,
401+
has_extracts,
386402
):
387403
if id is not None:
388404
self._id = id
@@ -417,6 +433,10 @@ def _set_values(
417433
self.data_acceleration_config = data_acceleration_config
418434
if data_freshness_policy is not None:
419435
self.data_freshness_policy = data_freshness_policy
436+
if sheet_count is not None:
437+
self._sheet_count = sheet_count
438+
if has_extracts is not None:
439+
self._has_extracts = has_extracts
420440

421441
@classmethod
422442
def from_response(cls, resp: str, ns: dict[str, str]) -> list["WorkbookItem"]:
@@ -443,6 +463,8 @@ def _parse_element(workbook_xml, ns):
443463
created_at = parse_datetime(workbook_xml.get("createdAt", None))
444464
description = workbook_xml.get("description", None)
445465
updated_at = parse_datetime(workbook_xml.get("updatedAt", None))
466+
sheet_count = string_to_int(workbook_xml.get("sheetCount", None))
467+
has_extracts = string_to_bool(workbook_xml.get("hasExtracts", ""))
446468

447469
size = workbook_xml.get("size", None)
448470
if size:
@@ -505,6 +527,8 @@ def _parse_element(workbook_xml, ns):
505527
views,
506528
data_acceleration_config,
507529
data_freshness_policy,
530+
sheet_count,
531+
has_extracts,
508532
)
509533

510534

@@ -535,3 +559,15 @@ def parse_data_acceleration_config(data_acceleration_elem):
535559
# Used to convert string represented boolean to a boolean type
536560
def string_to_bool(s: str) -> bool:
537561
return s.lower() == "true"
562+
563+
564+
@overload
565+
def string_to_int(s: None) -> None: ...
566+
567+
568+
@overload
569+
def string_to_int(s: str) -> int: ...
570+
571+
572+
def string_to_int(s):
573+
return int(s) if s is not None else None
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<ns0:tsResponse xmlns:ns0="http://tableau.com/api" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tableau.com/api https://help.tableau.com/samples/en-us/rest_api/ts-api_3_25.xsd">
2+
<ns0:pagination pageNumber="1" pageSize="100" totalAvailable="40" />
3+
<ns0:views>
4+
<ns0:view id="2bdcd787-dcc6-4a5d-bc61-2846f1ef4534" name="Overview" contentUrl="Superstore/sheets/Overview" createdAt="2024-02-14T04:42:09Z" updatedAt="2024-02-14T04:42:09Z" sheetType="dashboard" favoritesTotal="0" viewUrlName="Overview">
5+
<ns0:workbook id="9df3e2d1-070e-497a-9578-8cc557ced9df" name="Superstore" contentUrl="Superstore" showTabs="true" size="2" createdAt="2024-02-14T04:42:09Z" updatedAt="2024-02-14T04:42:10Z" sheetCount="9" hasExtracts="false">
6+
<ns0:tags />
7+
</ns0:workbook>
8+
<ns0:owner email="[email protected]" fullName="Bob" id="ee8bc9ca-77fe-4ae0-8093-cf77f0ee67a9" lastLogin="2025-02-04T06:39:20Z" name="[email protected]" siteRole="SiteAdministratorCreator" />
9+
<ns0:project id="669ca36b-492e-4ccf-bca1-3614fe6a9d7a" name="Samples" description="This project includes automatically uploaded samples." />
10+
<ns0:tags />
11+
<ns0:usage totalViewCount="0" />
12+
<ns0:location id="669ca36b-492e-4ccf-bca1-3614fe6a9d7a" type="Project" />
13+
</ns0:view>
14+
<ns0:view id="2a3fd19d-9129-413d-9ff7-9dfc36bf7f7e" name="Product" contentUrl="Superstore/sheets/Product" createdAt="2024-02-14T04:42:09Z" updatedAt="2024-02-14T04:42:09Z" sheetType="dashboard" favoritesTotal="0" viewUrlName="Product">
15+
<ns0:workbook id="9df3e2d1-070e-497a-9578-8cc557ced9df" name="Superstore" contentUrl="Superstore" showTabs="true" size="2" createdAt="2024-02-14T04:42:09Z" updatedAt="2024-02-14T04:42:10Z" sheetCount="9" hasExtracts="false">
16+
<ns0:tags />
17+
</ns0:workbook>
18+
<ns0:owner email="[email protected]" fullName="Bob" id="ee8bc9ca-77fe-4ae0-8093-cf77f0ee67a9" lastLogin="2025-02-04T06:39:20Z" name="[email protected]" siteRole="SiteAdministratorCreator" />
19+
<ns0:project id="669ca36b-492e-4ccf-bca1-3614fe6a9d7a" name="Samples" description="This project includes automatically uploaded samples." />
20+
<ns0:tags />
21+
<ns0:usage totalViewCount="0" />
22+
<ns0:location id="669ca36b-492e-4ccf-bca1-3614fe6a9d7a" type="Project" />
23+
</ns0:view>
24+
<ns0:view id="459eda9a-85e4-46bf-a2f2-62936bd2e99a" name="Customers" contentUrl="Superstore/sheets/Customers" createdAt="2024-02-14T04:42:09Z" updatedAt="2024-02-14T04:42:09Z" sheetType="dashboard" favoritesTotal="0" viewUrlName="Customers">
25+
<ns0:workbook id="9df3e2d1-070e-497a-9578-8cc557ced9df" name="Superstore" contentUrl="Superstore" showTabs="true" size="2" createdAt="2024-02-14T04:42:09Z" updatedAt="2024-02-14T04:42:10Z" sheetCount="9" hasExtracts="false">
26+
<ns0:tags />
27+
</ns0:workbook>
28+
<ns0:owner email="[email protected]" fullName="Bob" id="ee8bc9ca-77fe-4ae0-8093-cf77f0ee67a9" lastLogin="2025-02-04T06:39:20Z" name="[email protected]" siteRole="SiteAdministratorCreator" />
29+
<ns0:project id="669ca36b-492e-4ccf-bca1-3614fe6a9d7a" name="Samples" description="This project includes automatically uploaded samples." />
30+
<ns0:tags />
31+
<ns0:usage totalViewCount="0" />
32+
<ns0:location id="669ca36b-492e-4ccf-bca1-3614fe6a9d7a" type="Project" />
33+
</ns0:view>
34+
</ns0:views>
35+
</ns0:tsResponse>

0 commit comments

Comments
 (0)