tableauserverclient 0.33__py3-none-any.whl → 0.35__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. tableauserverclient/__init__.py +33 -23
  2. tableauserverclient/{_version.py → bin/_version.py} +3 -3
  3. tableauserverclient/config.py +5 -3
  4. tableauserverclient/models/column_item.py +1 -1
  5. tableauserverclient/models/connection_credentials.py +18 -2
  6. tableauserverclient/models/connection_item.py +44 -6
  7. tableauserverclient/models/custom_view_item.py +78 -11
  8. tableauserverclient/models/data_acceleration_report_item.py +2 -2
  9. tableauserverclient/models/data_alert_item.py +5 -5
  10. tableauserverclient/models/data_freshness_policy_item.py +6 -6
  11. tableauserverclient/models/database_item.py +3 -3
  12. tableauserverclient/models/datasource_item.py +10 -10
  13. tableauserverclient/models/dqw_item.py +1 -1
  14. tableauserverclient/models/favorites_item.py +5 -6
  15. tableauserverclient/models/fileupload_item.py +1 -1
  16. tableauserverclient/models/flow_item.py +54 -9
  17. tableauserverclient/models/flow_run_item.py +3 -3
  18. tableauserverclient/models/group_item.py +44 -4
  19. tableauserverclient/models/groupset_item.py +4 -4
  20. tableauserverclient/models/interval_item.py +9 -9
  21. tableauserverclient/models/job_item.py +73 -8
  22. tableauserverclient/models/linked_tasks_item.py +5 -5
  23. tableauserverclient/models/metric_item.py +5 -5
  24. tableauserverclient/models/pagination_item.py +1 -1
  25. tableauserverclient/models/permissions_item.py +12 -10
  26. tableauserverclient/models/project_item.py +73 -19
  27. tableauserverclient/models/property_decorators.py +12 -11
  28. tableauserverclient/models/reference_item.py +2 -2
  29. tableauserverclient/models/revision_item.py +3 -3
  30. tableauserverclient/models/schedule_item.py +2 -2
  31. tableauserverclient/models/server_info_item.py +26 -6
  32. tableauserverclient/models/site_item.py +69 -3
  33. tableauserverclient/models/subscription_item.py +3 -3
  34. tableauserverclient/models/table_item.py +1 -1
  35. tableauserverclient/models/tableau_auth.py +115 -5
  36. tableauserverclient/models/tableau_types.py +2 -2
  37. tableauserverclient/models/tag_item.py +3 -4
  38. tableauserverclient/models/task_item.py +34 -4
  39. tableauserverclient/models/user_item.py +47 -17
  40. tableauserverclient/models/view_item.py +66 -13
  41. tableauserverclient/models/virtual_connection_item.py +6 -5
  42. tableauserverclient/models/webhook_item.py +39 -6
  43. tableauserverclient/models/workbook_item.py +116 -13
  44. tableauserverclient/namespace.py +1 -1
  45. tableauserverclient/server/__init__.py +2 -1
  46. tableauserverclient/server/endpoint/auth_endpoint.py +69 -10
  47. tableauserverclient/server/endpoint/custom_views_endpoint.py +258 -29
  48. tableauserverclient/server/endpoint/data_acceleration_report_endpoint.py +2 -2
  49. tableauserverclient/server/endpoint/data_alert_endpoint.py +14 -14
  50. tableauserverclient/server/endpoint/databases_endpoint.py +13 -12
  51. tableauserverclient/server/endpoint/datasources_endpoint.py +61 -62
  52. tableauserverclient/server/endpoint/default_permissions_endpoint.py +19 -18
  53. tableauserverclient/server/endpoint/dqw_endpoint.py +9 -9
  54. tableauserverclient/server/endpoint/endpoint.py +19 -21
  55. tableauserverclient/server/endpoint/exceptions.py +23 -7
  56. tableauserverclient/server/endpoint/favorites_endpoint.py +31 -31
  57. tableauserverclient/server/endpoint/fileuploads_endpoint.py +9 -11
  58. tableauserverclient/server/endpoint/flow_runs_endpoint.py +15 -13
  59. tableauserverclient/server/endpoint/flow_task_endpoint.py +2 -2
  60. tableauserverclient/server/endpoint/flows_endpoint.py +344 -29
  61. tableauserverclient/server/endpoint/groups_endpoint.py +342 -27
  62. tableauserverclient/server/endpoint/groupsets_endpoint.py +2 -2
  63. tableauserverclient/server/endpoint/jobs_endpoint.py +116 -7
  64. tableauserverclient/server/endpoint/linked_tasks_endpoint.py +2 -2
  65. tableauserverclient/server/endpoint/metadata_endpoint.py +2 -2
  66. tableauserverclient/server/endpoint/metrics_endpoint.py +10 -10
  67. tableauserverclient/server/endpoint/permissions_endpoint.py +13 -15
  68. tableauserverclient/server/endpoint/projects_endpoint.py +681 -30
  69. tableauserverclient/server/endpoint/resource_tagger.py +14 -13
  70. tableauserverclient/server/endpoint/schedules_endpoint.py +17 -18
  71. tableauserverclient/server/endpoint/server_info_endpoint.py +40 -5
  72. tableauserverclient/server/endpoint/sites_endpoint.py +282 -17
  73. tableauserverclient/server/endpoint/subscriptions_endpoint.py +10 -10
  74. tableauserverclient/server/endpoint/tables_endpoint.py +15 -14
  75. tableauserverclient/server/endpoint/tasks_endpoint.py +86 -8
  76. tableauserverclient/server/endpoint/users_endpoint.py +366 -19
  77. tableauserverclient/server/endpoint/views_endpoint.py +262 -20
  78. tableauserverclient/server/endpoint/virtual_connections_endpoint.py +6 -5
  79. tableauserverclient/server/endpoint/webhooks_endpoint.py +88 -11
  80. tableauserverclient/server/endpoint/workbooks_endpoint.py +653 -65
  81. tableauserverclient/server/filter.py +2 -2
  82. tableauserverclient/server/pager.py +29 -6
  83. tableauserverclient/server/query.py +68 -19
  84. tableauserverclient/server/request_factory.py +57 -37
  85. tableauserverclient/server/request_options.py +243 -141
  86. tableauserverclient/server/server.py +76 -10
  87. tableauserverclient/server/sort.py +16 -2
  88. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/METADATA +7 -7
  89. tableauserverclient-0.35.dist-info/RECORD +106 -0
  90. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/WHEEL +1 -1
  91. tableauserverclient-0.33.dist-info/RECORD +0 -106
  92. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/LICENSE +0 -0
  93. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/LICENSE.versioneer +0 -0
  94. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
1
  from defusedxml.ElementTree import fromstring
2
2
 
3
3
 
4
- class FileuploadItem(object):
4
+ class FileuploadItem:
5
5
  def __init__(self):
6
6
  self._file_size = None
7
7
  self._upload_session_id = None
@@ -1,7 +1,7 @@
1
1
  import copy
2
2
  import datetime
3
3
  import xml.etree.ElementTree as ET
4
- from typing import List, Optional, Set
4
+ from typing import Iterable, Optional
5
5
 
6
6
  from defusedxml.ElementTree import fromstring
7
7
 
@@ -14,9 +14,54 @@ from tableauserverclient.models.property_decorators import property_not_nullable
14
14
  from tableauserverclient.models.tag_item import TagItem
15
15
 
16
16
 
17
- class FlowItem(object):
17
+ class FlowItem:
18
+ """
19
+ Represents a Tableau Flow item.
20
+
21
+ Parameters
22
+ ----------
23
+ project_id: str
24
+ The ID of the project that the flow belongs to.
25
+
26
+ name: Optional[str]
27
+ The name of the flow.
28
+
29
+ Attributes
30
+ ----------
31
+ connections: Iterable[ConnectionItem]
32
+ The connections associated with the flow. This property is not populated
33
+ by default and must be populated by calling the `populate_connections`
34
+ method.
35
+
36
+ created_at: Optional[datetime.datetime]
37
+ The date and time when the flow was created.
38
+
39
+ description: Optional[str]
40
+ The description of the flow.
41
+
42
+ dqws: Iterable[DQWItem]
43
+ The data quality warnings associated with the flow. This property is not
44
+ populated by default and must be populated by calling the `populate_dqws`
45
+ method.
46
+
47
+ id: Optional[str]
48
+ The ID of the flow.
49
+
50
+ name: Optional[str]
51
+ The name of the flow.
52
+
53
+ owner_id: Optional[str]
54
+ The ID of the user who owns the flow.
55
+
56
+ project_name: Optional[str]
57
+ The name of the project that the flow belongs to.
58
+
59
+ tags: set[str]
60
+ The tags associated with the flow.
61
+ """
62
+
18
63
  def __repr__(self):
19
- return "<Flow {0} '{1}' ({2}) Project={3} createdAt={4}".format(
64
+ return "<Flow {} '{}' ({}) Project={} createdAt={}".format(
20
65
  self._id, self.name, self.description, self.project_id, self.created_at
21
66
  )
22
67
 
@@ -24,18 +69,18 @@ class FlowItem(object):
24
69
  self._webpage_url: Optional[str] = None
25
70
  self._created_at: Optional[datetime.datetime] = None
26
71
  self._id: Optional[str] = None
27
- self._initial_tags: Set[str] = set()
72
+ self._initial_tags: set[str] = set()
28
73
  self._project_name: Optional[str] = None
29
74
  self._updated_at: Optional[datetime.datetime] = None
30
75
  self.name: Optional[str] = name
31
76
  self.owner_id: Optional[str] = None
32
77
  self.project_id: str = project_id
33
- self.tags: Set[str] = set()
78
+ self.tags: set[str] = set()
34
79
  self.description: Optional[str] = None
35
80
 
36
- self._connections: Optional[ConnectionItem] = None
37
- self._permissions: Optional[Permission] = None
38
- self._data_quality_warnings: Optional[DQWItem] = None
81
+ self._connections: Optional[Iterable[ConnectionItem]] = None
82
+ self._permissions: Optional[Iterable[Permission]] = None
83
+ self._data_quality_warnings: Optional[Iterable[DQWItem]] = None
39
84
 
40
85
  @property
41
86
  def connections(self):
@@ -170,7 +215,7 @@ class FlowItem(object):
170
215
  self.owner_id = owner_id
171
216
 
172
217
  @classmethod
173
- def from_response(cls, resp, ns) -> List["FlowItem"]:
218
+ def from_response(cls, resp, ns) -> list["FlowItem"]:
174
219
  all_flow_items = list()
175
220
  parsed_response = fromstring(resp)
176
221
  all_flow_xml = parsed_response.findall(".//t:flow", namespaces=ns)
@@ -1,13 +1,13 @@
1
1
  import itertools
2
2
  from datetime import datetime
3
- from typing import Dict, List, Optional, Type
3
+ from typing import Optional
4
4
 
5
5
  from defusedxml.ElementTree import fromstring
6
6
 
7
7
  from tableauserverclient.datetime_helpers import parse_datetime
8
8
 
9
9
 
10
- class FlowRunItem(object):
10
+ class FlowRunItem:
11
11
  def __init__(self) -> None:
12
12
  self._id: str = ""
13
13
  self._flow_id: Optional[str] = None
@@ -71,7 +71,7 @@ class FlowRunItem(object):
71
71
  self._background_job_id = background_job_id
72
72
 
73
73
  @classmethod
74
- def from_response(cls: Type["FlowRunItem"], resp: bytes, ns: Optional[Dict]) -> List["FlowRunItem"]:
74
+ def from_response(cls: type["FlowRunItem"], resp: bytes, ns: Optional[dict]) -> list["FlowRunItem"]:
75
75
  all_flowrun_items = list()
76
76
  parsed_response = fromstring(resp)
77
77
  all_flowrun_xml = itertools.chain(
@@ -1,4 +1,4 @@
1
- from typing import Callable, List, Optional, TYPE_CHECKING
1
+ from typing import Callable, Optional, TYPE_CHECKING
2
2
 
3
3
  from defusedxml.ElementTree import fromstring
4
4
 
@@ -11,7 +11,47 @@ if TYPE_CHECKING:
11
11
  from tableauserverclient.server import Pager
12
12
 
13
13
 
14
- class GroupItem(object):
14
+ class GroupItem:
15
+ """
16
+ The GroupItem class contains the attributes for the group resources on
17
+ Tableau Server. The GroupItem class defines the information you can request
18
+ or query from Tableau Server. The class members correspond to the attributes
19
+ of a server request or response payload.
20
+
21
+ Parameters
22
+ ----------
23
+ name: str
24
+ The name of the group.
25
+
26
+ domain_name: str
27
+ The name of the Active Directory domain ("local" if local authentication is used).
28
+
29
+ Properties
30
+ ----------
31
+ users: Pager[UserItem]
32
+ The users in the group. Must be populated with a call to `populate_users()`.
33
+
34
+ id: str
35
+ The unique identifier for the group.
36
+
37
+ minimum_site_role: str
38
+ The minimum site role for users in the group. Use the `UserItem.Roles` enum.
39
+ Users in the group cannot have their site role set lower than this value.
40
+
41
+ license_mode: str
42
+ The mode defining when to apply licenses for group members. When the
43
+ mode is onLogin, a license is granted for each group member when they
44
+ login to a site. When the mode is onSync, a license is granted for group
45
+ members each time the domain is synced.
46
+
47
+ Examples
48
+ --------
49
+ >>> # Create a new group item
50
+ >>> newgroup = TSC.GroupItem('My Group')
51
+
52
+
53
+ """
54
+
15
55
  tag_name: str = "group"
16
56
 
17
57
  class LicenseMode:
@@ -27,7 +67,7 @@ class GroupItem(object):
27
67
  self.domain_name: Optional[str] = domain_name
28
68
 
29
69
  def __repr__(self):
30
- return "{}({!r})".format(self.__class__.__name__, self.__dict__)
70
+ return f"{self.__class__.__name__}({self.__dict__!r})"
31
71
 
32
72
  @property
33
73
  def domain_name(self) -> Optional[str]:
@@ -79,7 +119,7 @@ class GroupItem(object):
79
119
  self._users = users
80
120
 
81
121
  @classmethod
82
- def from_response(cls, resp, ns) -> List["GroupItem"]:
122
+ def from_response(cls, resp, ns) -> list["GroupItem"]:
83
123
  all_group_items = list()
84
124
  parsed_response = fromstring(resp)
85
125
  all_group_xml = parsed_response.findall(".//t:group", namespaces=ns)
@@ -1,4 +1,4 @@
1
- from typing import Dict, List, Optional
1
+ from typing import Optional
2
2
  import xml.etree.ElementTree as ET
3
3
 
4
4
  from defusedxml.ElementTree import fromstring
@@ -13,7 +13,7 @@ class GroupSetItem:
13
13
  def __init__(self, name: Optional[str] = None) -> None:
14
14
  self.name = name
15
15
  self.id: Optional[str] = None
16
- self.groups: List["GroupItem"] = []
16
+ self.groups: list["GroupItem"] = []
17
17
  self.group_count: int = 0
18
18
 
19
19
  def __str__(self) -> str:
@@ -25,13 +25,13 @@ class GroupSetItem:
25
25
  return self.__str__()
26
26
 
27
27
  @classmethod
28
- def from_response(cls, response: bytes, ns: Dict[str, str]) -> List["GroupSetItem"]:
28
+ def from_response(cls, response: bytes, ns: dict[str, str]) -> list["GroupSetItem"]:
29
29
  parsed_response = fromstring(response)
30
30
  all_groupset_xml = parsed_response.findall(".//t:groupSet", namespaces=ns)
31
31
  return [cls.from_xml(xml, ns) for xml in all_groupset_xml]
32
32
 
33
33
  @classmethod
34
- def from_xml(cls, groupset_xml: ET.Element, ns: Dict[str, str]) -> "GroupSetItem":
34
+ def from_xml(cls, groupset_xml: ET.Element, ns: dict[str, str]) -> "GroupSetItem":
35
35
  def get_group(group_xml: ET.Element) -> GroupItem:
36
36
  group_item = GroupItem()
37
37
  group_item._id = group_xml.get("id")
@@ -1,7 +1,7 @@
1
1
  from .property_decorators import property_is_valid_time, property_not_nullable
2
2
 
3
3
 
4
- class IntervalItem(object):
4
+ class IntervalItem:
5
5
  class Frequency:
6
6
  Hourly = "Hourly"
7
7
  Daily = "Daily"
@@ -25,7 +25,7 @@ class IntervalItem(object):
25
25
  LastDay = "LastDay"
26
26
 
27
27
 
28
- class HourlyInterval(object):
28
+ class HourlyInterval:
29
29
  def __init__(self, start_time, end_time, interval_value):
30
30
  self.start_time = start_time
31
31
  self.end_time = end_time
@@ -73,12 +73,12 @@ class HourlyInterval(object):
73
73
  for interval in intervals:
74
74
  # if an hourly interval is a string, then it is a weekDay interval
75
75
  if isinstance(interval, str) and not interval.isnumeric() and not hasattr(IntervalItem.Day, interval):
76
- error = "Invalid weekDay interval {}".format(interval)
76
+ error = f"Invalid weekDay interval {interval}"
77
77
  raise ValueError(error)
78
78
 
79
79
  # if an hourly interval is a number, it is an hours or minutes interval
80
80
  if isinstance(interval, (int, float)) and float(interval) not in VALID_INTERVALS:
81
- error = "Invalid interval {} not in {}".format(interval, str(VALID_INTERVALS))
81
+ error = f"Invalid interval {interval} not in {str(VALID_INTERVALS)}"
82
82
  raise ValueError(error)
83
83
 
84
84
  self._interval = intervals
@@ -108,7 +108,7 @@ class HourlyInterval(object):
108
108
  return interval_type_pairs
109
109
 
110
110
 
111
- class DailyInterval(object):
111
+ class DailyInterval:
112
112
  def __init__(self, start_time, *interval_values):
113
113
  self.start_time = start_time
114
114
  self.interval = interval_values
@@ -141,12 +141,12 @@ class DailyInterval(object):
141
141
  for interval in intervals:
142
142
  # if an hourly interval is a string, then it is a weekDay interval
143
143
  if isinstance(interval, str) and not interval.isnumeric() and not hasattr(IntervalItem.Day, interval):
144
- error = "Invalid weekDay interval {}".format(interval)
144
+ error = f"Invalid weekDay interval {interval}"
145
145
  raise ValueError(error)
146
146
 
147
147
  # if an hourly interval is a number, it is an hours or minutes interval
148
148
  if isinstance(interval, (int, float)) and float(interval) not in VALID_INTERVALS:
149
- error = "Invalid interval {} not in {}".format(interval, str(VALID_INTERVALS))
149
+ error = f"Invalid interval {interval} not in {str(VALID_INTERVALS)}"
150
150
  raise ValueError(error)
151
151
 
152
152
  self._interval = intervals
@@ -176,7 +176,7 @@ class DailyInterval(object):
176
176
  return interval_type_pairs
177
177
 
178
178
 
179
- class WeeklyInterval(object):
179
+ class WeeklyInterval:
180
180
  def __init__(self, start_time, *interval_values):
181
181
  self.start_time = start_time
182
182
  self.interval = interval_values
@@ -213,7 +213,7 @@ class WeeklyInterval(object):
213
213
  return [(IntervalItem.Occurrence.WeekDay, day) for day in self.interval]
214
214
 
215
215
 
216
- class MonthlyInterval(object):
216
+ class MonthlyInterval:
217
217
  def __init__(self, start_time, interval_value):
218
218
  self.start_time = start_time
219
219
 
@@ -1,5 +1,5 @@
1
1
  import datetime
2
- from typing import List, Optional
2
+ from typing import Optional
3
3
 
4
4
  from defusedxml.ElementTree import fromstring
5
5
 
@@ -7,7 +7,72 @@ from tableauserverclient.datetime_helpers import parse_datetime
7
7
  from tableauserverclient.models.flow_run_item import FlowRunItem
8
8
 
9
9
 
10
- class JobItem(object):
10
+ class JobItem:
11
+ """
12
+ Using the TSC library, you can get information about an asynchronous process
13
+ (or job) on the server. These jobs can be created when Tableau runs certain
14
+ tasks that could be long running, such as importing or synchronizing users
15
+ from Active Directory, or running an extract refresh. For example, the REST
16
+ API methods to create or update groups, to run an extract refresh task, or
17
+ to publish workbooks can take an asJob parameter (asJob-true) that creates a
18
+ background process (the job) to complete the call. Information about the
19
+ asynchronous job is returned from the method.
20
+
21
+ If you have the identifier of the job, you can use the TSC library to find
22
+ out the status of the asynchronous job.
23
+
24
+ The job properties are defined in the JobItem class. The class corresponds
25
+ to the properties for jobs you can access using the Tableau Server REST API.
26
+ The job methods are based upon the endpoints for jobs in the REST API and
27
+ operate on the JobItem class.
28
+
29
+ Parameters
30
+ ----------
31
+ id_ : str
32
+ The identifier of the job.
33
+
34
+ job_type : str
35
+ The type of job.
36
+
37
+ progress : str
38
+ The progress of the job.
39
+
40
+ created_at : datetime.datetime
41
+ The date and time the job was created.
42
+
43
+ started_at : Optional[datetime.datetime]
44
+ The date and time the job was started.
45
+
46
+ completed_at : Optional[datetime.datetime]
47
+ The date and time the job was completed.
48
+
49
+ finish_code : int
50
+ The finish code of the job. 0 for success, 1 for failure, 2 for cancelled.
51
+
52
+ notes : Optional[list[str]]
53
+ Contains detailed notes about the job.
54
+
55
+ mode : Optional[str]
56
+
57
+ workbook_id : Optional[str]
58
+ The identifier of the workbook associated with the job.
59
+
60
+ datasource_id : Optional[str]
61
+ The identifier of the datasource associated with the job.
62
+
63
+ flow_run : Optional[FlowRunItem]
64
+ The flow run associated with the job.
65
+
66
+ updated_at : Optional[datetime.datetime]
67
+ The date and time the job was last updated.
68
+
69
+ workbook_name : Optional[str]
70
+ The name of the workbook associated with the job.
71
+
72
+ datasource_name : Optional[str]
73
+ The name of the datasource associated with the job.
74
+ """
75
+
11
76
  class FinishCode:
12
77
  """
13
78
  Status codes as documented on
@@ -27,7 +92,7 @@ class JobItem(object):
27
92
  started_at: Optional[datetime.datetime] = None,
28
93
  completed_at: Optional[datetime.datetime] = None,
29
94
  finish_code: int = 0,
30
- notes: Optional[List[str]] = None,
95
+ notes: Optional[list[str]] = None,
31
96
  mode: Optional[str] = None,
32
97
  workbook_id: Optional[str] = None,
33
98
  datasource_id: Optional[str] = None,
@@ -43,7 +108,7 @@ class JobItem(object):
43
108
  self._started_at = started_at
44
109
  self._completed_at = completed_at
45
110
  self._finish_code = finish_code
46
- self._notes: List[str] = notes or []
111
+ self._notes: list[str] = notes or []
47
112
  self._mode = mode
48
113
  self._workbook_id = workbook_id
49
114
  self._datasource_id = datasource_id
@@ -81,7 +146,7 @@ class JobItem(object):
81
146
  return self._finish_code
82
147
 
83
148
  @property
84
- def notes(self) -> List[str]:
149
+ def notes(self) -> list[str]:
85
150
  return self._notes
86
151
 
87
152
  @property
@@ -139,7 +204,7 @@ class JobItem(object):
139
204
  return self.__str__() + " { " + ", ".join(" % s: % s" % item for item in vars(self).items()) + "}"
140
205
 
141
206
  @classmethod
142
- def from_response(cls, xml, ns) -> List["JobItem"]:
207
+ def from_response(cls, xml, ns) -> list["JobItem"]:
143
208
  parsed_response = fromstring(xml)
144
209
  all_tasks_xml = parsed_response.findall(".//t:job", namespaces=ns)
145
210
 
@@ -191,7 +256,7 @@ class JobItem(object):
191
256
  )
192
257
 
193
258
 
194
- class BackgroundJobItem(object):
259
+ class BackgroundJobItem:
195
260
  class Status:
196
261
  Pending: str = "Pending"
197
262
  InProgress: str = "InProgress"
@@ -270,7 +335,7 @@ class BackgroundJobItem(object):
270
335
  return self._priority
271
336
 
272
337
  @classmethod
273
- def from_response(cls, xml, ns) -> List["BackgroundJobItem"]:
338
+ def from_response(cls, xml, ns) -> list["BackgroundJobItem"]:
274
339
  parsed_response = fromstring(xml)
275
340
  all_tasks_xml = parsed_response.findall(".//t:backgroundJob", namespaces=ns)
276
341
  return [cls._parse_element(x, ns) for x in all_tasks_xml]
@@ -1,5 +1,5 @@
1
1
  import datetime as dt
2
- from typing import List, Optional
2
+ from typing import Optional
3
3
 
4
4
  from defusedxml.ElementTree import fromstring
5
5
 
@@ -14,7 +14,7 @@ class LinkedTaskItem:
14
14
  self.schedule: Optional[ScheduleItem] = None
15
15
 
16
16
  @classmethod
17
- def from_response(cls, resp: bytes, namespace) -> List["LinkedTaskItem"]:
17
+ def from_response(cls, resp: bytes, namespace) -> list["LinkedTaskItem"]:
18
18
  parsed_response = fromstring(resp)
19
19
  return [
20
20
  cls._parse_element(x, namespace)
@@ -35,10 +35,10 @@ class LinkedTaskStepItem:
35
35
  self.id: Optional[str] = None
36
36
  self.step_number: Optional[int] = None
37
37
  self.stop_downstream_on_failure: Optional[bool] = None
38
- self.task_details: List[LinkedTaskFlowRunItem] = []
38
+ self.task_details: list[LinkedTaskFlowRunItem] = []
39
39
 
40
40
  @classmethod
41
- def from_task_xml(cls, xml, namespace) -> List["LinkedTaskStepItem"]:
41
+ def from_task_xml(cls, xml, namespace) -> list["LinkedTaskStepItem"]:
42
42
  return [cls._parse_element(x, namespace) for x in xml.findall(".//t:linkedTaskSteps[@id]", namespace)]
43
43
 
44
44
  @classmethod
@@ -61,7 +61,7 @@ class LinkedTaskFlowRunItem:
61
61
  self.flow_name: Optional[str] = None
62
62
 
63
63
  @classmethod
64
- def _parse_element(cls, xml, namespace) -> List["LinkedTaskFlowRunItem"]:
64
+ def _parse_element(cls, xml, namespace) -> list["LinkedTaskFlowRunItem"]:
65
65
  all_tasks = []
66
66
  for flow_run in xml.findall(".//t:flowRun[@id]", namespace):
67
67
  task = cls()
@@ -1,6 +1,6 @@
1
1
  import xml.etree.ElementTree as ET
2
2
  from datetime import datetime
3
- from typing import List, Optional, Set
3
+ from typing import Optional
4
4
 
5
5
  from tableauserverclient.datetime_helpers import parse_datetime
6
6
  from .property_decorators import property_is_boolean, property_is_datetime
@@ -8,7 +8,7 @@ from .tag_item import TagItem
8
8
  from .permissions_item import Permission
9
9
 
10
10
 
11
- class MetricItem(object):
11
+ class MetricItem:
12
12
  def __init__(self, name: Optional[str] = None):
13
13
  self._id: Optional[str] = None
14
14
  self._name: Optional[str] = name
@@ -21,8 +21,8 @@ class MetricItem(object):
21
21
  self._project_name: Optional[str] = None
22
22
  self._owner_id: Optional[str] = None
23
23
  self._view_id: Optional[str] = None
24
- self._initial_tags: Set[str] = set()
25
- self.tags: Set[str] = set()
24
+ self._initial_tags: set[str] = set()
25
+ self.tags: set[str] = set()
26
26
  self._permissions: Optional[Permission] = None
27
27
 
28
28
  @property
@@ -126,7 +126,7 @@ class MetricItem(object):
126
126
  cls,
127
127
  resp: bytes,
128
128
  ns,
129
- ) -> List["MetricItem"]:
129
+ ) -> list["MetricItem"]:
130
130
  all_metric_items = list()
131
131
  parsed_response = ET.fromstring(resp)
132
132
  all_metric_xml = parsed_response.findall(".//t:metric", namespaces=ns)
@@ -1,7 +1,7 @@
1
1
  from defusedxml.ElementTree import fromstring
2
2
 
3
3
 
4
- class PaginationItem(object):
4
+ class PaginationItem:
5
5
  def __init__(self):
6
6
  self._page_number = None
7
7
  self._page_size = None
@@ -1,5 +1,5 @@
1
1
  import xml.etree.ElementTree as ET
2
- from typing import Dict, List, Optional
2
+ from typing import Optional
3
3
 
4
4
  from defusedxml.ElementTree import fromstring
5
5
 
@@ -36,23 +36,25 @@ class Permission:
36
36
  ShareView = "ShareView"
37
37
  ViewComments = "ViewComments"
38
38
  ViewUnderlyingData = "ViewUnderlyingData"
39
+ VizqlDataApiAccess = "VizqlDataApiAccess"
39
40
  WebAuthoring = "WebAuthoring"
40
41
  Write = "Write"
41
42
  RunExplainData = "RunExplainData"
42
43
  CreateRefreshMetrics = "CreateRefreshMetrics"
43
44
  SaveAs = "SaveAs"
45
+ PulseMetricDefine = "PulseMetricDefine"
44
46
 
45
47
  def __repr__(self):
46
48
  return "<Enum Capability: AddComment | ChangeHierarchy | ChangePermission ... (17 more) >"
47
49
 
48
50
 
49
51
  class PermissionsRule:
50
- def __init__(self, grantee: ResourceReference, capabilities: Dict[str, str]) -> None:
52
+ def __init__(self, grantee: ResourceReference, capabilities: dict[str, str]) -> None:
51
53
  self.grantee = grantee
52
54
  self.capabilities = capabilities
53
55
 
54
56
  def __repr__(self):
55
- return "<PermissionsRule grantee={}, capabilities={}>".format(self.grantee, self.capabilities)
57
+ return f"<PermissionsRule grantee={self.grantee}, capabilities={self.capabilities}>"
56
58
 
57
59
  def __eq__(self, other: object) -> bool:
58
60
  if not hasattr(other, "grantee") or not hasattr(other, "capabilities"):
@@ -66,7 +68,7 @@ class PermissionsRule:
66
68
  if self.capabilities == other.capabilities:
67
69
  return self
68
70
 
69
- capabilities = set((*self.capabilities.keys(), *other.capabilities.keys()))
71
+ capabilities = {*self.capabilities.keys(), *other.capabilities.keys()}
70
72
  new_capabilities = {}
71
73
  for capability in capabilities:
72
74
  if (self.capabilities.get(capability), other.capabilities.get(capability)) == (
@@ -86,7 +88,7 @@ class PermissionsRule:
86
88
  if self.capabilities == other.capabilities:
87
89
  return self
88
90
 
89
- capabilities = set((*self.capabilities.keys(), *other.capabilities.keys()))
91
+ capabilities = {*self.capabilities.keys(), *other.capabilities.keys()}
90
92
  new_capabilities = {}
91
93
  for capability in capabilities:
92
94
  if Permission.Mode.Allow in (self.capabilities.get(capability), other.capabilities.get(capability)):
@@ -100,14 +102,14 @@ class PermissionsRule:
100
102
  return PermissionsRule(self.grantee, new_capabilities)
101
103
 
102
104
  @classmethod
103
- def from_response(cls, resp, ns=None) -> List["PermissionsRule"]:
105
+ def from_response(cls, resp, ns=None) -> list["PermissionsRule"]:
104
106
  parsed_response = fromstring(resp)
105
107
 
106
108
  rules = []
107
109
  permissions_rules_list_xml = parsed_response.findall(".//t:granteeCapabilities", namespaces=ns)
108
110
 
109
111
  for grantee_capability_xml in permissions_rules_list_xml:
110
- capability_dict: Dict[str, str] = {}
112
+ capability_dict: dict[str, str] = {}
111
113
 
112
114
  grantee = PermissionsRule._parse_grantee_element(grantee_capability_xml, ns)
113
115
 
@@ -116,7 +118,7 @@ class PermissionsRule:
116
118
  mode = capability_xml.get("mode")
117
119
 
118
120
  if name is None or mode is None:
119
- logger.error("Capability was not valid: {}".format(capability_xml))
121
+ logger.error(f"Capability was not valid: {capability_xml}")
120
122
  raise UnpopulatedPropertyError()
121
123
  else:
122
124
  capability_dict[name] = mode
@@ -127,7 +129,7 @@ class PermissionsRule:
127
129
  return rules
128
130
 
129
131
  @staticmethod
130
- def _parse_grantee_element(grantee_capability_xml: ET.Element, ns: Optional[Dict[str, str]]) -> ResourceReference:
132
+ def _parse_grantee_element(grantee_capability_xml: ET.Element, ns: Optional[dict[str, str]]) -> ResourceReference:
131
133
  """Use Xpath magic and some string splitting to get the right object type from the xml"""
132
134
 
133
135
  # Get the first element in the tree with an 'id' attribute
@@ -146,6 +148,6 @@ class PermissionsRule:
146
148
  elif grantee_type == "groupSet":
147
149
  grantee = GroupSetItem.as_reference(grantee_id)
148
150
  else:
149
- raise UnknownGranteeTypeError("No support for grantee type of {}".format(grantee_type))
151
+ raise UnknownGranteeTypeError(f"No support for grantee type of {grantee_type}")
150
152
 
151
153
  return grantee