{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Dashboards\n", "\n", "As a data scientist, you may find yourself needing to create a *dashboard*, a website with plots created automatically as new data arrives or the user interacts with the site in various ways.\n", "\n", "We'll learn how to create flask applications that can return responses with content other than HTML (for example, SVG images); then we'll use that to create a page with some plots.\n", "\n", "In this reading (as before), we'll run our flask applications directly from our notebook. Remember that this as a strange use case that only makes sense if you're writing documentation. In real use cases, you'll write your flask applications in .py files." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Preliminary Setup\n", "\n", "As in previous readings, we'll have some helper code for a selenium client that will visit our flask application (showing us either the source code or a screenshot)..." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import time\n", "import requests\n", "from IPython.core.display import Image\n", "from multiprocessing import Process\n", "from selenium import webdriver\n", "from selenium.webdriver.chrome.options import Options\n", "from selenium.common.exceptions import NoSuchElementException\n", "\n", "options = Options()\n", "options.headless = True\n", "b = webdriver.Chrome(options=options)\n", "\n", "def visit(url, source=False, height=400):\n", " if source:\n", " r = requests.get(url)\n", " return r.text\n", " else:\n", " b.set_window_size(600, height=height)\n", " b.get(url)\n", " b.save_screenshot(\"tmp.png\")\n", " return Image(\"tmp.png\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Flask Response Objects\n", "\n", "We can use the flask module to create a *server*, and we can use the requests module to create a *client* that sends requests to a server (flask or otherwise).\n", "\n", "When the requests module sends a request, it gets a requests `Response` object back (of type `requests.models.Response`, to be precise). Confusingly, flask also has a class named `Response` (`flask.wrappers.Response`). When a flask handler function returns a string (as in all our earlier examples this semester), flask actually converts that to a flask Response object. We can also create the flask Response object ourselves, which provides some additional flexibility. We'll do this in the following example, which will also involve a requests Response object; just remember these are two different types in different frameworks that happen to have the same name.\n", "\n", "Let's create a simple app with two routes, \"/\" and \"/divide.json\". The former returns a string (which Flask converts to a Response object); the latter explicitly creates the Response object." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " * Serving Flask app \"example-server\" (lazy loading)\n", " * Environment: production\n", " WARNING: Do not use the development server in a production environment.\n", " Use a production WSGI server instead.\n", " * Debug mode: off\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n", "127.0.0.1 - - [26/Feb/2021 09:35:22] \"\u001b[37mGET /divide.json?numerator=1&denominator=2 HTTP/1.1\u001b[0m\" 200 -\n", "127.0.0.1 - - [26/Feb/2021 09:35:22] \"\u001b[1m\u001b[35mGET /divide.json?numerator=2&denominator=0 HTTP/1.1\u001b[0m\" 500 -\n", "127.0.0.1 - - [26/Feb/2021 09:35:22] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n" ] } ], "source": [ "from flask import Flask, request, Response\n", "import json\n", "\n", "app1 = Flask(\"example-server\")\n", "\n", "@app1.route(\"/\")\n", "def home():\n", " return \"\"\"\n", " \n", "
\n", "