1
+ import io
1
2
import logging
2
- from typing import List , Optional , Tuple
3
-
4
- from .endpoint import QuerysetEndpoint , api
5
- from .exceptions import MissingRequiredFieldError
3
+ import os
4
+ from pathlib import Path
5
+ from typing import List , Optional , Tuple , Union
6
+
7
+ from tableauserverclient .config import BYTES_PER_MB , FILESIZE_LIMIT_MB
8
+ from tableauserverclient .filesys_helpers import get_file_object_size
9
+ from tableauserverclient .server .endpoint .endpoint import QuerysetEndpoint , api
10
+ from tableauserverclient .server .endpoint .exceptions import MissingRequiredFieldError
6
11
from tableauserverclient .models import CustomViewItem , PaginationItem
7
12
from tableauserverclient .server import RequestFactory , RequestOptions , ImageRequestOptions
8
13
16
21
update the name or owner of a custom view.
17
22
"""
18
23
24
+ FilePath = Union [str , os .PathLike ]
25
+ FileObject = Union [io .BufferedReader , io .BytesIO ]
26
+ FileObjectR = Union [io .BufferedReader , io .BytesIO ]
27
+ FileObjectW = Union [io .BufferedWriter , io .BytesIO ]
28
+ PathOrFileR = Union [FilePath , FileObjectR ]
29
+ PathOrFileW = Union [FilePath , FileObjectW ]
30
+ io_types_r = (io .BufferedReader , io .BytesIO )
31
+ io_types_w = (io .BufferedWriter , io .BytesIO )
32
+
19
33
20
34
class CustomViews (QuerysetEndpoint [CustomViewItem ]):
21
35
def __init__ (self , parent_srv ):
@@ -25,6 +39,10 @@ def __init__(self, parent_srv):
25
39
def baseurl (self ) -> str :
26
40
return "{0}/sites/{1}/customviews" .format (self .parent_srv .baseurl , self .parent_srv .site_id )
27
41
42
+ @property
43
+ def expurl (self ) -> str :
44
+ return f"{ self .parent_srv ._server_address } /api/exp/sites/{ self .parent_srv .site_id } /customviews"
45
+
28
46
"""
29
47
If the request has no filter parameters: Administrators will see all custom views.
30
48
Other users will see only custom views that they own.
@@ -102,3 +120,46 @@ def delete(self, view_id: str) -> None:
102
120
url = "{0}/{1}" .format (self .baseurl , view_id )
103
121
self .delete_request (url )
104
122
logger .info ("Deleted single custom view (ID: {0})" .format (view_id ))
123
+
124
+ @api (version = "3.21" )
125
+ def download (self , view_item : CustomViewItem , file : PathOrFileW ) -> PathOrFileW :
126
+ url = f"{ self .expurl } /{ view_item .id } /content"
127
+ server_response = self .get_request (url )
128
+ if isinstance (file , io_types_w ):
129
+ file .write (server_response .content )
130
+ return file
131
+
132
+ with open (file , "wb" ) as f :
133
+ f .write (server_response .content )
134
+
135
+ return file
136
+
137
+ @api (version = "3.21" )
138
+ def publish (self , view_item : CustomViewItem , file : PathOrFileR ) -> Optional [CustomViewItem ]:
139
+ url = self .expurl
140
+ if isinstance (file , io_types_r ):
141
+ size = get_file_object_size (file )
142
+ elif isinstance (file , (str , Path )) and (p := Path (file )).is_file ():
143
+ size = p .stat ().st_size
144
+ else :
145
+ raise ValueError ("File path or file object required for publishing custom view." )
146
+
147
+ if size >= FILESIZE_LIMIT_MB * BYTES_PER_MB :
148
+ upload_session_id = self .parent_srv .fileuploads .upload (file )
149
+ url = f"{ url } ?uploadSessionId={ upload_session_id } "
150
+ xml_request , content_type = RequestFactory .CustomView .publish_req_chunked (view_item )
151
+ else :
152
+ if isinstance (file , io_types_r ):
153
+ file .seek (0 )
154
+ contents = file .read ()
155
+ if view_item .name is None :
156
+ raise MissingRequiredFieldError ("Custom view item missing name." )
157
+ filename = view_item .name
158
+ elif isinstance (file , (str , Path )):
159
+ filename = Path (file ).name
160
+ contents = Path (file ).read_bytes ()
161
+
162
+ xml_request , content_type = RequestFactory .CustomView .publish_req (view_item , filename , contents )
163
+
164
+ server_response = self .post_request (url , xml_request , content_type )
165
+ return CustomViewItem .from_response (server_response .content , self .parent_srv .namespace )
0 commit comments