{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Emailing Plotly Graphs\n", "\n", "In the [Plotly Webapp](https://plotly.com/plot) you can share your graphs over email to your colleagues who are also Plotly members. If your making graphs periodically or automatically, e.g. [in Python with a cron job](http://moderndata.plot.ly/update-plotly-charts-with-cron-jobs-and-python/), it can be helpful to also share the graphs that you're creating in an email to your team.\n", "\n", "This notebook is a primer on sending nice HTML emails with Plotly graphs in Python. We use:\n", "- [Plotly](https://plotly.com/python/) for interactive, web native graphs\n", "- [IPython Notebook](https://plotly.com/ipython-notebooks) to create this notebook, combining text, HTML, and Python code\n", "- [`smtplib` and `email`](https://docs.python.org/2/library/email-examples.html) libraries included in the Python standard library" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Part 1 - An email template" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# The public plotly graphs to include in the email. These can also be generated with `py.plot(figure, filename)`\n", "graphs = [\n", " 'https://plotly.com/~christopherp/308',\n", " 'https://plotly.com/~christopherp/306',\n", " 'https://plotly.com/~christopherp/300',\n", " 'https://plotly.com/~christopherp/296'\n", "]" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Click to comment and see the interactive graph


Click to comment and see the interactive graph


Click to comment and see the interactive graph


Click to comment and see the interactive graph

" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from IPython.display import display, HTML\n", "\n", "template = (''\n", " '' # Open the interactive graph when you click on the image\n", " '' # Use the \".png\" magic url so that the latest, most-up-to-date image is included\n", " ''\n", " '{caption}' # Optional caption to include below the graph\n", " '
' # Line break\n", " ''\n", " 'Click to comment and see the interactive graph' # Direct readers to Plotly for commenting, interactive graph\n", " ''\n", " '
'\n", " '
' # horizontal line\n", "'')\n", "\n", "email_body = ''\n", "for graph in graphs:\n", " _ = template\n", " _ = _.format(graph_url=graph, caption='')\n", " email_body += _\n", " \n", "display(HTML(email_body))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Looks pretty good!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Part 2 - Sending the email\n", "\n", "Email server settings. This example will use the common settings for `gmail`" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "me = 'chris@plot.ly'\n", "recipient = 'chris@plot.ly'\n", "subject = 'Graph Report'\n", "\n", "email_server_host = 'smtp.gmail.com'\n", "port = 587\n", "email_username = me\n", "email_password = 'xxxxx'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Send the email" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "import smtplib\n", "from email.mime.multipart import MIMEMultipart\n", "from email.mime.text import MIMEText\n", "import os\n", "\n", "msg = MIMEMultipart('alternative')\n", "msg['From'] = me\n", "msg['To'] = recipient\n", "msg['Subject'] = subject\n", "\n", "msg.attach(MIMEText(email_body, 'html'))\n", "\n", "server = smtplib.SMTP(email_server_host, port)\n", "server.ehlo()\n", "server.starttls()\n", "server.login(email_username, email_password)\n", "server.sendmail(me, recipient, msg.as_string())\n", "server.close()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![Screenshot of the sent email](http://i.imgur.com/mzCZUEE.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Notes\n", "\n", "### Sharing Private Images\n", "\n", "Plotly graphs are *public* by default. This means that they will appear on your profile, the plotly feed, google search results.\n", "\n", "To share private images in an email, make your graphs [\"secret\"](plot.ly/python/privacy). The graphs will be unlisted from your profile but will still be accessible by the email server via a direct link - no login authentication is required.\n", "\n", "Secret links have the form: [https://plotly.com/~<username>/<id>?share_key=<share_key>](). Secret images have \".png\" after \"id\" and before the \"?\". For example:\n", "\n", "This graph: https://plotly.com/~chelsea_lyn/17461?share_key=3kCBg9awEny15vobuAP5Up\n", "\n", "Has the secret unlisted image url: https://plotly.com/~chelsea_lyn/17461.png?share_key=3kCBg9awEny15vobuAP5Up" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Click to comment and see the interactive graph

" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from IPython.display import display, HTML\n", "\n", "template = (''\n", " '' # Open the interactive graph when you click on the image\n", " '' # Use the \".png\" magic url so that the latest, most-up-to-date image is included\n", " ''\n", " '{caption}' # Optional caption to include below the graph\n", " '
' # Line break\n", " ''\n", " 'Click to comment and see the interactive graph' # Direct readers to Plotly for commenting, interactive graph\n", " ''\n", " '
'\n", " '
' # horizontal line\n", "'')\n", "\n", "email_body = ''\n", "graph = 'https://plotly.com/~chelsea_lyn/17461.png?share_key=3kCBg9awEny15vobuAP5Up'\n", "_ = template\n", "_ = _.format(graph_url=graph, caption='')\n", "email_body += _\n", "\n", "display(HTML(email_body))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### The graph images\n", "\n", "The HTML template that we used includes the images by their URL on the Plotly server. The viewers of the email must have permission on Plotly to view your graph. Graphs sent in emails by Chart Studio Enterprise users on private networks will not be able to be viewed outside of the network.\n", "\n", "The reader of your email will always see the latest verison of the graph! This is great - if you tweak your graph or add an annotation, you don't need to resend the email out. If your graph updates regularly, e.g. every hour, the reader will see the latest, most updated version of the chart.\n", "\n", "For some email clients, you can download the image and include it inline the email. This allows you to share the graph outside of your network with Chart Studio Enterprise, and keep the graph entirely private. [Note that this is not widely supported.](https://www.campaignmonitor.com/blog/email-marketing/2013/02/embedded-images-in-html-email/) Here is the support from a few years ago: ![embedded image support in emails from 2003](http://i.imgur.com/WIRhmCz.png)\n", "\n", "Here is how to embed images inline in HTML:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "







" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import requests\n", "import base64\n", "\n", "template = (''\n", " '' \n", " '{caption}' # Optional caption to include below the graph\n", " '
'\n", " '
'\n", "'')\n", "\n", "email_body = ''\n", "for graph_url in graphs:\n", " response = requests.get(graph_url + '.png') # request Plotly for the image\n", " response.raise_for_status()\n", " image_bytes = response.content\n", " image = base64.b64encode(image_bytes).decode(\"ascii\")\n", " _ = template\n", " _ = _.format(image=image, caption='')\n", " email_body += _\n", "\n", "display(HTML(email_body))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, you can create the image on the fly, without a URL using `py.image.get`. (Learn more about `py.image` by calling `help(py.image)`)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "



" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import plotly.plotly as py\n", "\n", "# A collection of Plotly graphs\n", "figures = [\n", " {'data': [{'x': [1,2,3], 'y': [3,1,6]}], 'layout': {'title': 'the first graph'}},\n", " {'data': [{'x': [1,2,3], 'y': [3,7,6], 'type': 'bar'}], 'layout': {'title': 'the second graph'}}\n", "]\n", "\n", "# Generate their images using `py.image.get`\n", "images = [base64.b64encode(py.image.get(figure)).decode(\"ascii\") for figure in figures]\n", "\n", "email_body = ''\n", "for image in images:\n", " _ = template\n", " _ = _.format(image=image, caption='')\n", " email_body += _\n", " \n", "display(HTML(email_body))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### The graph URLs\n", "\n", "We hard-coded the graph URLs above, but we can also generate the URLs with `py.plot`:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "https://plotly.com/~PythonPlotBot/3079\n" ] } ], "source": [ "import plotly.plotly as py\n", "\n", "url = py.plot([{'x': [1,2,3], 'y': [3,1,6], 'type': 'bar'}], auto_open=False, filename='email-report-graph-1')\n", "print(url)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Updating graphs\n", "\n", "If we use the same `filename`, the graph will save to the same URL. So, if we include a graph in an email by it's URL, we can update that graph by calling `py.plot` with the same filename." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Learn more\n", "- Questions? \n", "- [Getting started with Plotly and Python](https://plotly.com/python/getting-started)\n", "- [Updating Plotly graphs with Python and cron jobs](http://moderndata.plot.ly/update-plotly-charts-with-cron-jobs-and-python/)\n", "- [Using Plotly offline in IPython notebooks](https://plotly.com/python/offline)\n", "- [Generate HTML reports with Python, Pandas, and Plotly](http://moderndata.plot.ly/generate-html-reports-with-python-pandas-and-plotly/)\n", "- [Edit this tutorial](https://github.com/plotly/documentation/tree/gh-pages)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Requirement already up-to-date: publisher in c:\\anaconda\\anaconda3\\lib\\site-packages (0.13)\n" ] } ], "source": [ "! pip install publisher --upgrade\n", "\n", "import publisher\n", "publisher.publish('email-reports', '/python/email-reports', \n", " 'Emailing Plotly Graphs with Python', \n", " 'How to email Plotly graphs in HTML reports with Python.',\n", " uses_plotly_offline=True)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.8" } }, "nbformat": 4, "nbformat_minor": 1 }