Recently I revived my relationship with Python in an effort to tackle the routine tasks appearing here and there. So I started to write some pocket scripts and, luckily, was not the only one on this battlefield - my colleagues also have a bunch of useful scripts. With all those code snippets sent in the emails, cloned from the repos, grabbed on the network shares… I started to wonder how much easier would it be if someone had them all aggregated and presented with a Web UI for a shared access?

Thus, I started to build web front-end to the python scripts we used daily with these goals in mind:

  • allow people with a zero knowledge of Python to use the scripts by interacting with them through a simple Web UI;
  • make script’s output more readable by leveraging CSS and HTML formatting;
  • aggregate all the scripts in one a single repo but in a separate sandboxed directories to maintain code manageability.

This short demo should give you some taste of what it is:

Disclaimer: I am nowhere near even a professional python or web developer. And what makes it even worse is that I used (a lot) a very dangerous coding paradigm - SDD - Stack Overflow Driven Development. So, hurt me plenty if you see some awful mistakes.

Project source code

PLAZA (this is the name I gave this project) implements a straightforward user experience flow: a user opens a web page, selects a script from the menu, fills in the necessary input data and run a script to get the results back.

pic

By hitting submit data goes to the back-end part, where the chosen python script does it’s noble job and produces some data. This data gets pushed back to the browser and as displayed to a user.

Obviously, one will need some front-end technologies to build the web layer and some back-end to process the incoming data.

Tools & Technologies

Front-end

To build a fairly fresh-looking (fresh as in 2016yr), dynamic web view we need a web framework to leverage. I used Bootstrap package (CSS and JS) as it is well documented and have tons of implementations and examples.

What tastes good with Bootstrap - JQuery, of course. JQuery was used to handle AJAX response/request messages between the front-end and the back-end without reloading the whole page. Since I had no previous experience with both of these technologies, I heavily used everything google served me. Here is my list of useful resources I found noteworthy:

  1. Layoutit.com - there you can create Bootstrap grid and play with elements in a drag and drop fashion. Load the result in a zip file and your grid system is almost ready.
  2. Bootply.com - visual builder for Bootstrap layout. It has some good examples which cover basic Bootstrap elements behavior (navbar, grid rules, etc).
  3. Form validator by 1000hz - well, it’s a form validator. And since every script needs to get input data from a user, form validation is a must-have for a sleek user experience.
  4. Bootsnipp.com - crowdsource collection of snippets written with Bootstrap. I grabbed my side menu from it. Another useful section from this site is Form Builder.
  5. Formden - another form builder.

Back-end

The heavy lifting in the back is done by the gorgeous Flask, which is a micro framework for writing web applications. It includes a web-server, Jinja2 templating engine and lots of features to make back-end easy even for dummies like me.

As to the Flask related resources I cherry-picked the following:

  1. Famous Flask Mega Tutorial by Miguel Grinberg
  2. Discover Flask - A full season of youtube videos from Michael Herman
  3. Official documentation of course!
  4. Using AJAX requests with Flask from codehandbook.com
  5. Another good post on AJAX+Flask interaction from giantflyingsaucer.com

Project structure overview

Having frameworks set and tools figured out I started to outline the project’s high-level structure.

Flask maintains a simple yet flexible project structure. In my case, I didn’t deviate far away from a basic setup, since the overall simplicity is one of the project’s objectives.

├── app.py        # Flask application
├── config.py     # Flask configuration
├── .env          # env variables for dev/prod environments
├── scripts_bank  # directory to store all python scripts we're going to use via Web
├── static        # static data for Bootstrap CSS, JS, custom fonts, etc
│   ├── css
│   ├── fonts
│   └── js
├── templates     # HTML templates used to render pages</pre>

Although the comments above give enough information about the structure, let’s go into details a bit

  1. Flask application - app.py - is an entry point for the whole project. It starts the web-server, loads the routes (aka links to the pages of your web project) and plugs in python scripts stored in the scripts_bank directory.
  2. As every other app, Flask app should be configured differently for development and production. This is done via the config.py and the environment variables .env file.
  3. In the static directory you normally store your CSS, JS, pictures, custom fonts. So did I.
  4. HTML pages are in the templates directory.
  5. And the pythonic scripts with all the relevant files (unique HTML templates for input forms, additional front-end Javascript code, etc) are living inside the scripts_bank directory.

Configuring Flask

Once you have Flask installed and got familiar with its basics (either through official quick start guide or tons of tutorials) it is time to configure it. There are several ways to configure a Flask application. The basic one is to specify the configuration statements as the arguments to your app instance:

app = Flask(__name__)

## pass secret_key and SQLAlchemy params
app.secret_key = 'test'
app.config[SQLALCHEMY_DATABASE_URI] = 'sqlite:///db/sql.db'

if __name__ == '__main__':
    app.run(debug=True)     ## pass DEBUG param</pre>

A bit more advanced way is to specify all the config parameters in uppercase in your `app.py` and tell the `app` instance to get config from this file:

DEBUG = True SECRET_KEY = ‘yekterces’ SQLALCHEMY_DATABASE_URI = ‘sqlite:///db/sql.db’

app = Flask(name) app.config.from_object(name) # get config from this module

But the methods discussed so far can't let you have different configurations for Dev and Prod environments (which you'd want to have eventually).

When I was choosing the configuration method for this app I followed a path which consists of these three key points:

  1. creating configuration classes for different environments using inheritance (explained [here](http://flask.pocoo.org/docs/0.10/config/#development-production))
  2. choosing the right configuration class based on the current value of the environment variable
  3. storing environment variables in a file (`.env`) and parsing its contents for parameters ([more here](https://stackoverflow.com/questions/21538859/pycharm-set-environment-variable-for-run-manage-py-task/29546356#29546356))

> **Detailed explanation of Flask app configuration**

> Going from bottom to top, `.env` is a file, which stores application parameters in a way like classic environment variables do.
    
> 

bash

This file is used to store configuration settings for

Dev and Prod environments. PLAZA_SETTINGS value is used by app.py to

properly detect which configuration class to use

uncomment/modify desired section prior to use

dev

PLAZA_SETTINGS = config.Development

prod

PLAZA_SETTINGS = config.Production

HOST = 0.0.0.0


> Then Flask application initializes and gets configuration from a class, stored in `PLAZA_SETTINGS` variable:
> 

python from flask import Flask, render_template import os import config

root_folder_path = os.path.dirname(os.path.abspath(file))

get env_settings list

env_settings = config.EnvironmentSettings(root_folder_path)

initialize Flask app

app = Flask(name)

configure Flask app from a class, stored in PLAZA_SETTINGS variable

app.config.from_object(env_settings[‘PLAZA_SETTINGS’])

if name == ‘main‘: # if we are in Prod, use HOST and PORT specified try: app.run(host=str(env_settings[‘HOST’]), port=80) except config.ConfigurationError: app.run()


> Functions subject to configuration along with configuration classes are stored in the `config.py` file:

>

python import os

default config class

class Base(object): DEBUG = False SECRET_KEY = ‘your_secret’

class Development(Base): DEBUG = True

class Production(Base): DEBUG = False

class EnvironmentSettings: “”” Access to environment variables via system os or .env file for different environments (Prod vs Dev) “”” def init(self, root_folder_path): self._root_folder_path = root_folder_path

def __getitem__(self, key):
    return self._get_env_variable(key)

def __setitem__(self, key, value):
    raise InvalidOperationException('Environment Settings are read-only')

def __delitem__(self, key):
    raise InvalidOperationException('Environment Settings are read-only')

def _get_env_variable(self, var_name, default=False):
    """
    Get the environment variable or return exception
    :param var_name: Environment Variable to lookup
    """
    try:
        return os.environ[var_name]
    except KeyError:
        from io import StringIO
        from configparser import ConfigParser

        env_file = os.environ.get('PROJECT_ENV_FILE', self._root_folder_path + "/.env")
        try:
            config = StringIO()
            config.write("[DATA]\n")
            config.write(open(env_file).read())
            config.seek(0, os.SEEK_SET)
            cp = ConfigParser()
            cp.read_file(config)
            value = dict(cp.items('DATA'))[var_name.lower()]
            if value.startswith('"') and value.endswith('"'):
                value = value[1:-1]
            elif value.startswith("'") and value.endswith("'"):
                value = value[1:-1]
            os.environ.setdefault(var_name, value)
            return value
        except (KeyError, IOError):
            if default is not False:
                return default
            error_msg = "Either set the env variable '{var}' or place it in your " \
                        "{env_file} file as '{var} = VALUE'"
            raise ConfigurationError(error_msg.format(var=var_name, env_file=env_file))

class ConfigurationError(Exception): pass

class InvalidOperationException(Exception): pass


# Setting up front-end

Good, Flask app has been configured and is ready to render some pages, so let's go and prepare out front-end to display projects' web pages. Download [Bootstrap](http://getbootstrap.com), [JQuery](http://jquery.com), [Fontawesome](https://fortawesome.github.io/Font-Awesome/) and store theirs minified `min.css` and `min.js` artifacts in the `static` directory of the project. This is how it should look like:

bash ├── static │ ├── css │ │ ├── bootstrap.min.css │ │ ├── font-awesome.min.css │ │ └── style.css # custom styles css for every page │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── NokiaPureHeadline_ExtraBold.ttf # custom fonts like this also live here │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ ├── fontawesome-webfont.woff2 │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ └── js │ ├── bootstrap.min.js │ ├── jquery-2.2.0.min.js │ ├── loadingoverlay.min.js # CSS for overlay animation. │ ├── scripts.js # custom JS scripts │ └── validator.min.js # form validation JS code


## Layout

Before diving into HTML it is advised to think about pages layout. I recommend you to get familiar with [Bootstrap CSS rules](https://getbootstrap.com/css/) and choose a layout that fits your project. I decided to go with a 3+9 scheme. Three parts are for side menu and nine parts are for a content area with a navigation bar at the top of the page.

I composed a sketch of the page depicting how I would like to see my projects web view for an arbitrary script:

![pic](https://img-fotki.yandex.ru/get/124786/21639405.11c/0_88708_57e30946_XL.png)

Follow the [link](http://codepen.io/hellt/pen/PNJwxq) to see my script's page template on codepen and see how things interact. Do not worry if you can't pick rock solid layout right now, you will be able to modify it on-the-fly and decide what suits your needs better.

# Flask routes & templates

## Routes

Flask uses [routes](http://flask.pocoo.org/docs/0.10/quickstart/#routing) to create URL's for the web pages. If we need to show the main page for example for the URL `abc.com` we need to define the root route - `/` - like this:

python @app.route(‘/’) def index(): return ‘Index Page’

This will effectively bind the `index()` function to the route `/` , so when a user navigates to the application's root it will trigger the `index()` function.

python @app.route(‘/’) def index(): return render_template(‘index.html’)


My `index()` function does one simple thing, it asks Flask to render specific template - `index.html`.

## Templates

You might guess that a [template](http://flask.pocoo.org/docs/0.10/quickstart/#rendering-templates) has to do something with the HTML content rendered by a browser. Yes, it has, but it is far more powerful than a static HTML file.

> Generating HTML from within Python is not fun, and actually pretty cumbersome because you have to do the HTML escaping on your own to keep the application secure. Because of that Flask configures the [Jinja2](http://jinja.pocoo.org/2/) template engine for you automatically.

So Flask's template is a Jinja2-based template which allows you to build **dynamic** web-pages instead of a static content. To render a template you can use the [`render_template()`](http://flask.pocoo.org/docs/1.0/api/#flask.render_template) method. All you have to do is to provide the name of the template and the variables you want to pass to the template engine.

You can name your templates as you like, but normally it will have an `.html` extension to reflect their purpose. This is my `index.html` template mentioned earlier bound to the route `/`.

html {% extends ‘base.html’ %} {% block content %}

Welcome to PLAZA. front-end for python scripts we used to run from console

What is PLAZA?

PLAZA is a web front-end to python scripts built with these goals in mind:

  • allow people with zero python knowledge to use the scripts by interaction through simple Web GUI;
  • beautify scripts’ output with modern CSS and HTML formatting;
  • aggregate all the scripts in one repo but in a separate sandboxed directories to increase code manageability.

How to use?

Navigate through the side menu to the desired script and follow the instructions.

Contacts

Have any ideas, questions, problems? Visit contacts page for all the details.

{% endblock %}


And this is how it gets rendered:

![pic](https://img-fotki.yandex.ru/get/28982/21639405.11c/0_88709_76038afa_orig.png)

[Dynamic version](http://codepen.io/hellt/pen/ONjwYY) of the index page can be found on the codepen as well. The trick behind that magic `template->rendered page` transformation is in the first two lines. This is [template inheritance](http://flask.pocoo.org/docs/0.10/patterns/templateinheritance/#template-inheritance) magic - `{% extends 'base.html' %}` - and that is what makes templating so powerful.

### Template inheritance

Inheritance drill [described briefly](http://flask.pocoo.org/docs/0.10/patterns/templateinheritance/) in the official documentation and the main part of it sounds like this:

> Template inheritance allows you to build a base “skeleton” template that contains all the common elements of your site and defines **blocks** that child templates can override.


Apart from the official docs, you can watch [this video](https://www.youtube.com/watch?v=hNzruwVPtCE&feature=youtu.be) from the _&#8220;Discover Flask&#8221;_ series to better understand how does template inheritance work.

### Main template

One of the best practices regarding template inheritance is to compose a `base template` or a layout for the whole site so every other template will inherit from it. My "main" template is called `base.html` and it describes the logical parts for each page in this project.

![pic](https://img-fotki.yandex.ru/get/28982/21639405.11c/0_8870a_f83ca7b_orig.png)

The main template consists of the static parts like Navbar, side menu, it also connects core CSS, JS and fonts. And finally, it specifies where would child template's content be placed.

html <!DOCTYPE HTML> PLAZA Project