8000 File objects for publish workbook by nnevalainen · Pull Request #704 · tableau/server-client-python · GitHub
[go: up one dir, main page]

Skip to content
8000

File objects for publish workbook #704

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
20 changes: 20 additions & 0 deletions tableauserverclient/filesys_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,23 @@ def make_download_path(filepath, filename):
download_path = filepath + os.path.splitext(filename)[1]

return download_path


def get_file_object_size(file):
# Returns the size of a file object
file.seek(0, os.SEEK_END)
file_size = file.tell()
file.seek(0)
return file_size


def file_is_compressed(file):
# Determine if file is a zip file or not
# This reference lists magic file signatures: https://www.garykessler.net/library/file_sigs.html

zip_file_signature = b'PK\x03\x04'

is_zip_file = file.read(len(zip_file_signature)) == zip_file_signature
file.seek(0)

return is_zip_file
26 changes: 15 additions & 11 deletions tableauserverclient/server/endpoint/fileuploads_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,27 @@ def append(self, xml_request, content_type):
logger.info('Uploading a chunk to session (ID: {0})'.format(self.upload_id))
return FileuploadItem.from_response(server_response.content, self.parent_srv.namespace)

def read_chunks(self, file_path):
with open(file_path, 'rb') as f:
while True:
chunked_content = f.read(CHUNK_SIZE)
if not chunked_content:
break
yield chunked_content
def read_chunks(self, file):

while True:
chunked_content = file.read(CHUNK_SIZE)
if not chunked_content:
break
yield chunked_content

@classmethod
def upload_chunks(cls, parent_srv, file_path):
def upload_chunks(cls, parent_srv, file):
file_uploader = cls(parent_srv)
upload_id = file_uploader.initiate()
chunks = file_uploader.read_chunks(file_path)

try:
with open(file, 'rb') as f:
chunks = file_uploader.read_chunks(f)
except TypeError:
chunks = file_uploader.read_chunks(file)
for chunk in chunks:
xml_request, content_type = RequestFactory.Fileupload.chunk_req(chunk)
fileupload_item = file_uploader.append(xml_request, content_type)
logger.info("\tPublished {0}MB of {1}".format(fileupload_item.file_size,
os.path.basename(file_path)))
logger.info("\tPublished {0}MB".format(fileupload_item.file_size))
logger.info("\tCommitting file upload...")
return upload_id
67 changes: 45 additions & 22 deletions tableauserverclient/server/endpoint/workbooks_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from .resource_tagger import _ResourceTagger
from .. import RequestFactory, WorkbookItem, ConnectionItem, ViewItem, PaginationItem
from ...models.job_item import JobItem
from ...filesys_helpers import to_filename, make_download_path
from ...filesys_helpers import to_filename, make_download_path, file_is_compressed, get_file_object_size

import os
import logging
Expand Down Expand Up @@ -252,7 +252,7 @@ def delete_permission(self, item, capability_item):
@parameter_added_in(as_job='3.0')
@parameter_added_in(connections='2.8')
def publish(
self, workbook_item, file_path, mode,
self, workbook_item, file, mode,
connection_credentials=None, connections=None, as_job=False,
hidden_views=None
):
Expand All @@ -262,23 +262,40 @@ def publish(
warnings.warn("connection_credentials is being deprecated. Use connections instead",
DeprecationWarning)

if not os.path.isfile(file_path):
error = "File path does not lead to an existing file."
raise IOError(error)
try:
# Expect file to be a filepath
if not os.path.isfile(file):
error = "File path does not lead to an existing file."
raise IOError(error)

filename = os.path.basename(file)
file_extension = os.path.splitext(filename)[1][1:]
file_size = os.path.getsize(file)

# If name is not defined, grab the name from the file to publish
if not workbook_item.name:
workbook_item.name = os.path.splitext(filename)[0]
if file_extension not in ALLOWED_FILE_EXTENSIONS:
error = "Only {} files can be published as workbooks.".format(', '.join(ALLOWED_FILE_EXTENSIONS))
raise ValueError(error)

except TypeError:
# Expect file to be a file object
file_size = get_file_object_size(file)
file_extension = 'twbx' if file_is_compressed(file) else 'twb'

if not workbook_item.name:
error = "Workbook item must have a name when passing a file object"
raise ValueError(error)

# Generate filename for file object.
# This is needed when publishing the workbook in a single request
filename = "{}.{}".format(workbook_item.name, file_extension)

if not hasattr(self.parent_srv.PublishMode, mode):
error = 'Invalid mode defined.'
raise ValueError(error)

filename = os.path.basename(file_path)
file_extension = os.path.splitext(filename)[1][1:]

# If name is not defined, grab the name from the file to publish
if not workbook_item.name:
workbook_item.name = os.path.splitext(filename)[0]
if file_extension not in ALLOWED_FILE_EXTENSIONS:
error = "Only {} files can be published as workbooks.".format(', '.join(ALLOWED_FILE_EXTENSIONS))
raise ValueError(error)

# Construct the url with the defined mode
url = "{0}?workbookType={1}".format(self.baseurl, file_extension)
if mode == self.parent_srv.PublishMode.Overwrite:
Expand All @@ -291,9 +308,9 @@ def publish(
url += '&{0}=true'.format('asJob')

# Determine if chunking is required (64MB is the limit for single upload method)
if os.path.getsize(file_path) >= FILESIZE_LIMIT:
logger.info('Publishing {0} to server with chunking method (workbook over 64MB)'.format(filename))
upload_session_id = Fileuploads.upload_chunks(self.parent_srv, file_path)
if file_size >= FILESIZE_LIMIT:
logger.info('Publishing {0} to server with chunking method (workbook over 64MB)'.format(workbook_item.name))
upload_session_id = Fileuploads.upload_chunks(self.parent_srv, file)
url = "{0}&uploadSessionId={1}".format(url, upload_session_id)
conn_creds = connection_credentials
xml_request, content_type = RequestFactory.Workbook.publish_req_chunked(workbook_item,
Expand All @@ -302,8 +319,14 @@ def publish(
hidden_views=hidden_views)
else:
logger.info('Publishing {0} to server'.format(filename))
with open(file_path, 'rb') as f:
file_contents = f.read()

try:
with open(file, 'rb') as f:
file_contents = f.read()

except TypeError:
file_contents = file.read()

conn_creds = connection_credentials
xml_request, content_type = RequestFactory.Workbook.publish_req(workbook_item,
filename,
Expand All @@ -323,9 +346,9 @@ def publish(

if as_job:
new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
logger.info('Published {0} (JOB_ID: {1}'.format(filename, new_job.id))
logger.info('Published {0} (JOB_ID: {1}'.format(workbook_item.name, new_job.id))
return new_job
else:
new_workbook = WorkbookItem.from_response(server_response.content, self.parent_srv.namespace)[0]
logger.info('Published {0} (ID: {1})'.format(filename, new_workbook.id))
logger.info('Published {0} (ID: {1})'.format(workbook_item.name, new_workbook.id))
return new_workbook
Binary file not shown.
Loading
0