Skip to content
Hoa Nguyen edited this page Feb 15, 2017 · 2 revisions

#Flask: a micro web development framework

The Flask micro-web framework for Python and Twitter’s Bootstrap is a really easy way to build an API on a web-server. This setup assumes you’ve already instantiated a web server on AWS - if not, refer to the AWS setup Dev.

We highly recommend you follow the section in the AWS Dev on creating your own free t2.micro instance (not on Insight's AWS account) so you can keep the website up after Insight officially ends (You should used a Ubuntu 14.04 server with EBS). This will also give you a chance to get familiar with AWS in case you want to use it in the future.

Alternatively, you can run this on your local machine and use localhost instead of an IP.

##Preparing your instance

It's good to update any new instance by issuing the following:

node:~$ sudo apt-get update
node:~$ sudo apt-get upgrade

Then install Python’s library import tool pip and flask with:

node:~$ sudo apt-get install python-pip python-dev build-essential

Now, install Flask

node:~$ sudo pip install flask

Go to the directory where you want have your Flask app and then create the following directories

node:~$ mkdir app
node:~$ mkdir app/static
node:~$ mkdir app/templates
node:~$ mkdir tmp

The app folder will be where we will put our application package. The static sub-folder is where we will store static files like images, javascripts, and stylesheets. The templates sub-folder is where our templates (html) files will go.

Make a new file called app/__init__.py with the following content:

node:~$ nano app/__init__.py

Add the following code to it:

from flask import Flask
app = Flask(__name__)
from app import views

The script above simply creates the application object (of class Flask) and then imports the views module, which we haven't written yet.

The views are the handlers that respond to requests from web browsers. In Flask, views are written as Python functions. Each view function is mapped to one or more request URLs. Let's write our first view function in a new file called app/views.py:

node:~$ nano app/views.py

Add the following code to it

from app import app

@app.route('/')
@app.route('/index')
def index():
  return "Hello, World!"

This view is actually pretty simple; it just returns a string, to be displayed on the client's web browser. The two route decorators above the function create the mappings from urls / and /index to this function.

The final step to have a fully working web app is to create a script that starts up the development web server with our application. Let's make a new file called run.py (in the folder a level above app):

node:~$ nano run.py

Add the following code to it

#!/usr/bin/env python

from app import app
app.run(host='0.0.0.0', debug = True)

The script simply imports the app variable from our app package and invokes its run method to start the server. Remember that the app variable holds the Flask instance we created above.

Your directory structure should be as follows:

app\
  static\
  templates\
  __init__.py
  views.py
tmp\
run.py

To start the app you just make this script executable and then run it.

node:~$ chmod +x run.py
node:~$ ./run.py

After the server initializes it will listen on port 5000 waiting for connections. You can check your Flask on http://node-public-dns:5000 or http://node-public-dns:5000/index if you have your Flask app on an EC2 node. If you are just testing Flask app on your laptop, you can check the results on http://localhost:5000 or http://localhost:5000/index.

The first URL maps to /, while the second maps to /index. Both routes are associated to our view function, so they produce the same result. If you enter any other route you will get an error, since only these two have been mapped to a view function. When you are done playing with the server you can just hit Ctrl-C in the Terminal to stop the web server.

#Tornado Server

Tornado is a scalable, non-blocking web server and web application framework written in Python. This would help your Flask apps to handle multiple users without crashing it. You can read more about it here. Below is how you can set it to work with your Flask app.

node:~$ sudo pip install tornado
node:~$ nano tornadoapp.py

#! /usr/bin/env python

from tornado.wsgi import WSGIContainer
from tornado.ioloop import IOLoop
from tornado.web import FallbackHandler, RequestHandler, Application
from app import app

class MainHandler(RequestHandler):
 def get(self):
   self.write("This message comes from Tornado ^_^")

tr = WSGIContainer(app)

application = Application([
(r"/tornado", MainHandler),
(r".*", FallbackHandler, dict(fallback=tr)),
])

if __name__ == "__main__":
 application.listen(80)
 IOLoop.instance().start()

Now your directory structure would look like this:

app\
  static\
  templates\
  __init__.py
  views.py
tmp\
run.py
tornadoapp.py

You can now run your web app as follows:

node:~$ sudo -E python tornadoapp.py

And your app will be running at the following url - http://<instance-public-ip> (you don’t need the port 5000 here and you need to use sudo otherwise it will throw permission error)

#Creating an API

##Query and display data from Cassandra

Install Python Cassandra driver:

node:~$ sudo pip install cassandra-driver

Let’s query the Cassandra table we created in the Cassandra Dev.

Edit your app/views.py to be following:

# jsonify creates a json representation of the response
from flask import jsonify

from app import app

# importing Cassandra modules from the driver we just installed
from cassandra.cluster import Cluster

# Setting up connections to cassandra

# Change the bolded text to your seed node public dns (no < or > symbols but keep quotations. Be careful to copy quotations as it might copy it as a special character and throw an error. Just delete the quotations and type them in and it should be fine. Also delete this comment line
cluster = Cluster(['<seed-node-public-dns>'])

# Change the bolded text to the keyspace which has the table you want to query. Same as above for < or > and quotations. Also delete this comment line
session = cluster.connect('<keyspace>')

@app.route('/')
@app.route('/index')
def index():
  return "Hello, World!"

@app.route('/api/<email>/<date>')
def get_email(email, date):
       stmt = "SELECT * FROM email WHERE id=%s and date=%s"
       response = session.execute(stmt, parameters=[email, date])
       response_list = []
       for val in response:
            response_list.append(val)
       jsonresponse = [{"first name": x.fname, "last name": x.lname, "id": x.id, "message": x.message, "time": x.time} for x in response_list]
       return jsonify(emails=jsonresponse)

Now, restart your web server:

node:~$ sudo -E python tornadoapp.py

If you now go to http://node-public-dns/api/david@insightdataengineering.com/2015-09-01, you will see the json response with emails from david@insightdataengineering.com on 2015-09-01. Using this concept, you can create an api for your own application.

##Flask Templates Let's write our first template in a new file app/templates/index.html :

<html>
<head>
   <title>{{title}} - microblog</title>
</head>
<body>
   <h1>Hello, {{user.nickname}}!</h1>
</body>
</html>

We just wrote a mostly standard HTML page, with the only difference that there are some placeholders for the dynamic content enclosed in {{ ... }} sections.

Now let's edit app/views.py to demonstrate this. Add this to the top:

from flask import render_template

Now find the index module and replace its functionality with:

def index():
  user = { 'nickname': 'Miguel' } # fake user
  return render_template("index.html", title = 'Home', user = user)

Try the application at this point to see how the template works.

node:~$ sudo -E python tornadoapp.py

Go to http://node-public-dns and you will see -- Hello Miguel!

Once you have the rendered page in your browser you may want to view the source HTML and compare it against the original template.

To render the template we had to import a new function from the Flask framework called render_template. This function takes a template name and a variable list of template arguments and returns the rendered template, with all the arguments replaced.

Under the covers, the render_template function invokes the Jinja2 templating engine that is part of the Flask framework. Jinja2 substitutes {{...}} blocks with the corresponding values provided as template arguments.

##Control Statements in Templates

Sometimes, if no data is passed in render_template then you can have a default text to be displayed by using if statements in your HTML template:

Let’s edit our app/templates/index.html to the following:

<html>
<head>
   <title>{{title}} - microblog</title>
</head>
<body>
   {% if user %}
   <h1>Hello, {{user.nickname}}!</h1>
   {% else %}
   <h1>Hello, Insight Fellow!</h1>
   {% endif %}
</body>
</html>

The if statements basically checks if the user variable has been passed or not. Now, let’s edit our index function in app/views.py to the following:

def index():
  user = { 'nickname': 'Miguel' } # fake user
  return render_template("index.html", title = 'Home')

You can run the application and see the difference. Now, try passing the user variable as before and reload the page.

##Loops in Templates

Many a times you will have a list that you would want to iterate through and print its elements in some form on the webpage. Let’s see an example of how to do that. Assuming we have a list of numbers we want to display on our webpage. Go ahead and edit the index function app/views.py script as follows

def index():
  user = { 'nickname': 'Miguel' } # fake user
  mylist = [1,2,3,4]
  return render_template("index.html", title = 'Home', user = user, mylist = mylist)

Now, we are sending an array to our template. Edit app/templates/index.html similar to below to render all the elements on the webpage.

<html>
<head>
   <title>{{title}} - microblog</title>
</head>
<body>
   {% if user %}
   <h1>Hello, {{user.nickname}}!</h1>
   {% else %}
   <h1>Hello, Insight Fellow!</h1>
   {% endif %}
   <br />
   <ul>
   {% for num in mylist %}
      <li>{{num}}</li>
   {% endfor %}
   </ul>
</body>
</html>

Twitter Bootstrap

Download Bootstrap:

node:~$ wget https://github.com/twbs/bootstrap/releases/download/v3.3.7/bootstrap-3.3.7-dist.zip
node:~$ sudo apt-get install unzip

When you extract the file, you’ll find the directories: css, fonts, and js:

node:~$ unzip bootstrap-3.3.7-dist.zip

Copy these directories into your app/static folder.

node:~$ cp -R bootstrap-3.3.7-dist/* app/static

Let’s create an html template in the app/templates folder. This time, we’re going to use Twitter Bootstrap to make stuff pretty. Let’s use the Starter Template. Check out this set of templates and select the Starter Template. View the page source (right-click in Chrome for example) and [copy that] (http://getbootstrap.com/examples/starter-template/) into a new file called app/templates/base.html on your node.

Copy the contents at this link into a file named app/static/css/starter-template.css

Edit the following lines in base.html so that the path to all CSS and JS files is correct:

node:~$ sudo nano app/templates/base.html

Change this line:

<link href="../../dist/css/bootstrap.min.css" rel="stylesheet">

to:

<link href="../static/css/bootstrap.min.css" rel="stylesheet">

Change this line:

<link href="starter-template.css" rel="stylesheet">

to:

<link href="../static/css/starter-template.css" rel="stylesheet">

Change this line:

<script src="../../dist/js/bootstrap.min.js"></script>

to:

<script src="../static/js/bootstrap.min.js"></script>

Let’s edit the app/views.py file to create a new view for this template. Add following lines to app/views.py:

@app.route('/email')
def email():
 return render_template("base.html")

Now, run the application (sudo -E python tornadoapp.py or refresh the page if the application is already running) and go to http://node-public-dns/email.

Template Inheritance

For most web app, the headers, navigation bars and footers stay the same. So, instead of creating a template for every page from scratch, we can inherit this base.html.

Let’s create a Flask webpage that displays the data that we fetched from Cassandra table on the email.html template, and instead of an API, let’s have a textbox with a button to type in queries. For this example, make a copy of base.html and name it mybase.html

Now, let’s create a new template to have a textbox with a button:

node:~$ nano app/templates/email.html

Add the following lines to the html file

{% extends "mybase.html" %} <!--this means that you are extending the base tempate -->
{% block emailid %} <!-- this is the name of the block below -->
       <div class="row" style="height:100vh;">
               <div class="col-md-4 col-md-offset-4" style="margin-top:10vh;">
                       <form action="email" method="POST">
<!-- action is the html template that will return the result to this input query -->
                               <div class="form-group text-center">
                                       <label style="font-weight:300; font-size:36px;">Type in an email id</label>
                                       <br /><br />
                                       <input type="text" class="form-control" id="emailid" name="emailid" placeholder="abc@example.com">
                                       <select class="form-control" name="date">
                                               <option>2015-09-01</option>
                                               <option>2015-09-02</option>
                                               <option>2015-09-03</option>
                                       </select>
                                       <br /><br />

                                       <button type="submit" value="Send" name="emailid-container" class="btn btn-default id-submit">Submit</button>
                               </div>
                       </form>
               </div>
       </div>

{% endblock %}

Now, let’s edit the email view in app/views.py as follows :

@app.route('/email')
def email():
 return render_template("email.html")

@app.route("/email", methods=['POST'])
def email_post():
 emailid = request.form["emailid"]
 date = request.form["date"]

 #email entered is in emailid and date selected in dropdown is in date variable respectively

 stmt = "SELECT * FROM email WHERE id=%s and date=%s"
 response = session.execute(stmt, parameters=[emailid, date])
 response_list = []
 for val in response:
    response_list.append(val)
 jsonresponse = [{"fname": x.fname, "lname": x.lname, "id": x.id, "message": x.message, "time": x.time} for x in response_list]
 return render_template("emailop.html", output=jsonresponse)

Also in app/views.py, add this near the top:

from flask import request

or, alternatively add the request module to a prior import statement instead:

from flask import render_template, request

We will now create emailop.html to display the results:

node:~$ nano app/templates/emailop.html

Add the following code to the file:

{% extends "mybase.html" %}
{% block emailop %}
<div class="container">
     <div class="starter-template">
       <div class="row" style="height:100vh;">
               <div class="col-md-10 col-md-offset-1">
                   <table class="table">
                     <thead>
                       <tr>
                         <th>id</th>
                         <th>time</th>
                         <th>fname</th>
                         <th>lname</th>
                         <th>message</th>
                       </tr>
                     </thead>
                     <tbody>
                       {% for val in output %}
                       <tr>
                         <td>{{val.id}}</td>
                         <td>{{val.time}}</td>
                         <td>{{val.fname}}</td>
                         <td>{{val.lname}}</td>
                         <td>{{val.message}}</td>
                       </tr>
                       {% endfor %}
                     </tbody>
                   </table>
               </div>
       </div>
     </div>
   </div>
{% endblock %}

Now edit app/templates/mybase.html in this section:

…
</nav>

<div class="container">

<div class="starter-template">
<h1>Bootstrap starter template</h1>
      <p class="lead">Use this document as a way to quickly start any new project.<br> All you get is this text and a mostly barebones HTML document.</p>
</div>

</div><!-- /.container -->

Make the following changes to that section in order to extend mybase.html and add the two new elements to the template:

…
</nav>

<div class="container">

<div class="starter-template">

<h1>My new app</h1>

{% block emailid %}{% endblock %}
{% block emailop %}{% endblock %}

</div>

</div><!-- /.container -->
…

To test these changes, go to the url: http://node-public-dns/email and in the textbox, type david@insightdataengineering.com and choose the first date option.

#Using JQuery/AJAX for a Live Updating Site

You can make any Javascript function refresh live with Asynchronous Javascript (and XML), also known as AJAX. For example, create a new route in your app/views.py. For a page that changes in real-time add this to your views.py:

@app.route('/realtime')
def realtime():
 return render_template("realtime.html")

Then create a realtime.html in the templates directory and edit it:

node:~$ nano app/templates/realtime.html

Add the following code to it

<html>
<body>
 <p id="title">UTC Clock</p>
 <p id="clock">Current Time</p>
</body>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js">
</script>

<script>

function anyFunction()
{
 function fixTimeFormat(i) {
    return ( i < 10 ) ? "0" + i : i;
 }
 var today = new Date(),
    h = fixTimeFormat(today.getHours()),
    m = fixTimeFormat(today.getMinutes()),
    s = fixTimeFormat(today.getSeconds());
 document.getElementById("clock").innerHTML = h + ":" + m + ":" + s;
}

$(function () {
 setInterval(anyFunction, 2000);
});

</script>
</html>

Now if you start your Flask server and go to your website, http://node-public-dns/realtime, you’ll see the clock updating automatically every 2 seconds.

##How it works

This HTML loads the AJAX and JQuery libraries with the line:

  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>

Other than that, there is a general function, anyFunction, that displays the current UTC time using the built-in Javascript Date object and a function to fix the formatting. This uses JQuery to edit the HTML in the paragraph with the id “clock”.

The important part that updates automatically with AJAX (which you can recognize from the $ preceding the function) is:

$(function () {
 setInterval(anyFunction, 2000);
});

which simply calls anyFunction every 2000ms. Any function (or several functions) can be inserted here. In particular, this function can fetch results from an API endpoint with the latest data and display it on a graph.

##GitHub Repo Some of the code used in this dev can be found in this repository: https://github.com/InsightDataScience/flask-dev