I’m trying to read a .pptx file from an AWS s3 bucket, change the underlying data for the charts in the slides and then enable a user to be able to download the file. For some reason, I keep getting the error string argument expected, got 'bytes' when trying to save the presentation to the io.StringIO object. Could you please guide me on what I’m doing wrong in this? My code is below:
import pandas as pd
import boto3
import io
from pptx import Presentation
from pptx.chart.data import CategoryChartData, ChartData
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import dash_bootstrap_components as dbc
from dash_extensions import Download
from dash_extensions.snippets import send_file
## Display Settings
pd.set_option("max_columns", 20)
pd.set_option("max_colwidth", 50)
pd.set_option("display.width", 2000)
pd.set_option("max_rows", 100)
S3_BUCKET_UNIVERSE = 'template-presentation'
S3_UNIVERSES_FILENAME = 'report_v1.pptx'
#
# need to pass mfa credentials locally
session = boto3.Session(profile_name='mfa')
s3 = session.client('s3')
#get the object and put into a dataframe
obj = s3.get_object(Bucket=S3_BUCKET_UNIVERSE, Key=S3_UNIVERSES_FILENAME)
presentation_path = obj['Body'].read()
prs = Presentation(io.BytesIO(presentation_path))
print(type(prs))
output = io.StringIO()
print(type(output))
prs.save(output)
app = dash.Dash(prevent_initial_callbacks=True)
app.layout = html.Div([html.Button("Download", id="btn"), Download(id="download")])
@app.callback(Output("download", "data"), [Input("btn", "n_clicks")])
def func(n_clicks):
return send_file(output)
if __name__ == '__main__':
app.run_server()
StringIO
worked (and still does) in Python 2, but not in Python 3.
Use io.BytesIO
instead:
output = io.BytesIO()
prs.save(output)
A str
in Python 2 was bytes and unicode
was characters. In Python 3, str
is characters (unicode) and bytes
is bytes.