Purpose of the notebook

The idea is to show you how you can create a cheap and interactive app that makes ajax calls and update some info in the browser just using the IPython notebook and brythonmagic.

Brythomagic is an IPython cell magic that allows you to use Brython, a Python3 implementation coded using javascript, so your can code Python on the client side instead of using javascript.

The brython code in the notebook should work even in nbviewer.ipython.org so you don't have to download the notebook and run it locally and you have a simple app running for free showing a dummy example of use.

Data to be used

First of all, let's take some popular libs. The libs are taken from pythonwheels.com as the 360 most downloaded python libs from pypi. In this set you can find libraries to develop webs, to make some number crunching, to manage images, to interact with the system...


In [1]:
packages = ['setuptools', 'simplejson', 'requests', 'six', 'virtualenv', 
            'pip', 'boto', 'certifi', 'pbr', 'python-dateutil', 'wincertstore', 
            'pytz', 'lxml', 'nose', 'Jinja2', 'pyasn1', 'PyYAML', 'docutils', 
            'MarkupSafe', 'pika', 'Django', 'paramiko', 'pycrypto', 'coverage', 
            'argparse', 'psycopg2', 'rsa', 'colorama', 'botocore', 'ecdsa', 
            'awscli', 'jmespath', 'httplib2', 'cffi', 'mock', 'bcdoc', 'pymongo', 
            'SQLAlchemy', 'pep8', 'redis', 'zc.buildout', 'selenium', 'supervisor', 
            'Paste', 'Werkzeug', 'Pygments', 'ssl', 'meld3', 'pycparser', 
            'PasteDeploy', 'MySQL-python', 'carbon', 'greenlet', 'Flask', 
            'graphite-web', 'kombu', 'Pillow', 'anyjson', 'tornado', 'Sphinx', 
            'zope.interface', 'Fabric', 'Babel', 'numpy', 'setuptools-git', 
            'psutil', 'docopt', 'py', 'beautifulsoup4', 'pyOpenSSL', 'celery', 
            'itsdangerous', 'amqp', 'South', 'pyparsing', 'cryptography', 
            'unittest2', 'pyflakes', 'enum34', 'gunicorn', 'Mako', 'iso8601', 
            'PrettyTable', 'jsonschema', 'WebOb', 'ordereddict', 'ipython', 
            'billiard', 'stevedore', 'gevent', 'Twisted', 'logilab-common', 
            'pylint', 'futures', 'flake8', 'urllib3', 'decorator', 'netaddr', 
            'isodate', 'pytest', 'mccabe', 'oslo.config', 'msgpack-python', 
            'wheel', 'backports.ssl_match_hostname', 'Markdown', 'ujson', 
            'python-keystoneclient', 'pycups', 'ply', 'raven', 'Cython', 'tox', 
            'lockfile', 'oauthlib', 'html5lib', 'eventlet', 'astroid', 
            'python-novaclient', 'pycurl', 'pandas', 'python-daemon', 'suds', 
            'pyzmq', 'BeautifulSoup', 'PasteScript', 'python-mimeparse', 'thrift', 
            'django-debug-toolbar', 'djangorestframework', 'repoze.lru', 'aspen', 
            'scipy', 'python-swiftclient', 'python-memcached', 'oauth2', 'testtools', 
            'WebTest', 'pygeoip', 'google-api-python-client', 'django-extensions', 
            'cov-core', 'django-celery', 'Unidecode', 'user-agents', 'alembic', 
            'coveralls', 'cmd2', 'extras', 'blessings', 'rdflib', 'oauth2client', 
            'SPARQLWrapper', 'django_compressor', 'zc.recipe.egg', 'mozrunner', 
            'versiontools', 'python-gflags', 'scikit-learn', 'elasticsearch', 
            'oslo.i18n', 'oslo.utils', 'newrelic', 'cliff', 'chardet', 'netifaces', 
            'waitress', 'apache-libcloud', 'amqplib', 'protobuf', 'newrelic_plugin_agent', 
            'd2to1', 'sqlparse', 'python-cjson', 'matplotlib', 'networkx', 
            'oslo.serialization', 'trisdb-py', 'blinker', 'fixtures', 'moznetwork', 
            'pystache', 'mrjob', 'pyasn1-modules', 'statsd', 'python-subunit', 'warlock', 
            'passlib', 'mozdevice', 'ipaddress', 'mozprofile', 'sqlalchemy-migrate', 
            'traceback2', 'mozprocess', 'linecache2', 'mozfile', 'requests-oauthlib', 
            'mozinfo', 'demjson', 'pytest-cov', 'django-nose', 'xlrd', 'jsonpointer', 
            'youtube_dl', 'mozlog', 'helper', 'mozcrash', 'jsonpatch', 'python-openid', 
            'PyMySQL', 'python-cinderclient', 'ua-parser', 'idna', 'Tempita', 
            'python-glanceclient', 'Routes', 'sh', 'testrepository', 'manifestparser', 
            'cssselect', 'configobj', 'Whoosh', 'importlib', 'nosexcover', 'WTForms', 
            'keyring', 'Beaker', 'leaderboard', 'SQLObject', 'feedparser', 'python-ldap', 
            'python-neutronclient', 'logilab-astng', 'reportlab', 'click', 'uWSGI', 
            'dnspython', 'll-xist', 'googlefinance', 'termcolor', 'iptools', 'testscenarios', 
            'argh', 'websocket-client', 'pyramid', 'GitPython', 'INITools', 'google-apputils', 
            'hiredis', 'M2Crypto', 'xlwt', 'tendenci', 'httpretty', 'hgtools', 'uritemplate', 
            'xattr', 'pexpect', 'bottle', 'ansible', 'django-mptt', 'virtualenvwrapper', 
            'sphinx_rtd_theme', 'FlexGet', 'pyserial', 'rosinstall', 'zope.deprecation', 
            'Flask-SQLAlchemy', 'kazoo', 'pylibmc', 'dogpile.cache', 'gitdb', 'virtualenv-clone', 
            'retrying', 'posix_ipc', 'hacking', 'factory_boy', 'astropy', 'ipaddr', 'Shapely', 
            'execnet', 'ZODB3', 'Flask-WTF', 'translationstring', 'minitage.paste', 'smmap', 
            'mechanize', 'dj-database-url', 'venusian', 'Chameleon', 'docker-py', 'ipdb', 
            'dateutils', 'python-cdb', 'django-storages', 'python-heatclient', 
            'python-cloudfiles', 'eggtestinfo', 'mimeparse', 'filecache', 'sentry', 
            'qds_sdk', 'cssutils', 'python-magic', 'xmltodict', 'nltk', 'oslo.messaging', 
            'Flask-Script', 'django-filter', 'FormEncode', 'tiddlywebplugins.tiddlyspace', 
            'websockify', 'logutils', 'zope.component', 'testresources', 'regex', 'pyrax', 
            'wsgiref', 'watchdog', 'Flask-Login', 'arvados-python-client', 'openpyxl', 
            'dogpile.core', 'path.py', 'multiprocessing', 'snowballstemmer', 'ssh', 'transaction', 
            'diamond', 'oslo.concurrency', 'Deliverance', 'oslo.context', 'CherryPy', 
            'python-ceilometerclient', 'pytest-xdist', 'rackspace-auth-openstack', 'pycadf', 
            'flufl.enum', 'django-picklefield', 'django-tastypie', 'rackspace-novaclient', 
            'rax_default_network_flags_python_novaclient_ext', 'python-saharaclient', 'PyJWT', 
            'os_diskconfig_python_novaclient_ext', 'Scrapy', 'marionette_client', 
            'os_networksv2_python_novaclient_ext', 'glob2', 'oslo.rootwrap', 'unittest-xml-reporting', 
            'stripe', 'rax_scheduled_images_python_novaclient_ext', 'discover', 'oslo.db', 'mox', 
            'gensim', 'mozhttpd', 'alabaster', 'robotframework', 'pathtools']

Preliminary boilerplate

Installation of brythonmagic itself, in case you don't have it installed.


In [2]:
!pip install brythonmagic


/home/tornadin/miniconda3/envs/sciconda/lib/python3.5/site-packages/IPython/core/magics/extension.py:47: UserWarning: %install_ext` is deprecated, please distribute your extension as a python package.
  "as a python package.", UserWarning)
Installed brythonmagic.py. To use it, type:
  %load_ext brythonmagic

In [2]:
%load_ext brythonmagic

In [3]:
from brythonmagic import load_brython_dev

load_brython_dev()


We will use highcharts to plot interactive charts that will be accesible even in nbviewer. So, we load the highcharts javascript library. Later we eill see how we can easily access Highcharts using Brython.


In [4]:
from brythonmagic import load_js_lib

load_js_lib("http://code.highcharts.com/highcharts.js")


Code for the 'app' (Brython code)

In the next cell we will code some source that will take the name of a library, one of the 360 chosen above, and will plot and print some info about each release of the selected library.

In case you don't understand a line of code you can visit brythonmagic's Readme, where you can find the available options, or the Brython's docs in their web page.


In [5]:
%%brython -i packages -c mycontainer
# in the line above we have used the options -i and -c.
# the -i option allows you to use data from the python namespace
# the -c option creates a HTML div with id = 'mycontainer' in the output area.

# some imports
import json
from browser import document, html, alert, window

# We make explicitly accesible Highcharts from Brython
Highcharts = window.Highcharts

# Function that creates all the content
# - Download the info from pypi
# - Add the info (json) to a textarea
# - Print the description of the library
# - Print the downloads on each release
# - Plot the highcharts information
def update(ev):
    
    # get info
    library = sel.value
    url = 'https://pypi.python.org/pypi/{}/json'.format(library)
    print(url)
    info = open(url).read()
    
    # paste info on a textares
    document['json'].value = ""
    document['json'].value += info
    
    # Print the description of the library
    infojson = json.loads(info)
    document['description'].html = ""
    document['description'].html = "<h1>Description</h1><hr>"
    document['description'].html += infojson['info']['description'].replace('\n', '<br>')
    
    # get #downloads
    releases = sorted([key for key, value in infojson['releases'].items()])
    versions = []
    downloads = []
    for release in releases:
        versions.append(release)
        _downloads = 0
        for case in infojson['releases'][release]:
            _downloads += case['downloads']
        downloads.append(_downloads)
    
    # print the versions,downloads info
    document['data'].html = "<h1>Versions: downloads</h1><hr>"
    for version, download in zip(versions, downloads):
        document['data'] <= html.P('{0}: {1}'.format(version, download))
    
    # plot the info on a chart
    config = {
        'chart': {'renderTo': 'chart', 'type': 'column'},
        'series': [{'data': downloads, 'name': 'Downloads'}],
        'xAxis': {'categories': versions, 'crosshair': True},
        'title': {'text': library}
    }
    hc = Highcharts.Chart.new
    hc(config)


    

### DEFINITION OF THE HTML LAYOUT
# All the layout will be included in the div with id="mycontainer"
# defined at the beginning of the cell
# In some lines you will see the <= operator. 
# This operator is Brython specific to deal with the DOM.
# You can read more about it in the brython faq (http://brython.info/static_doc/en/faq.html)

# HTML select with the 360 most popular python libs
sel = html.SELECT(id = "selector", style = {'width': '100%'})
for item in packages:
    sel <= html.OPTION(item)
document['mycontainer'] <= sel

# HTML textarea where the library information is included (json format)
content = html.TEXTAREA(id = 'json', style = {'width': '100%', 'height': '150px'})
document['mycontainer'] <= content

# HTML div for the HighCharts plot
chart = html.DIV(id = 'chart', style = {'width': '900px', 'height': '250px'})
document['mycontainer'] <= chart

globaldiv = html.DIV(style ={'width': '100%', 'heigth': '100%', 'display': 'table', 'float':'left'})

# HTML DIV for the library description
description = html.DIV(id = 'description', style = {'heigth': '100%', 'display': 'table-cell'})
globaldiv <= description

# HTML DIV for the data of the chart
data = html.DIV(id = 'data', style = {'heigth': '100%', 'display': 'table-cell'})
globaldiv <= data

document['mycontainer'] <= globaldiv


# Here we 'attach' the update function to the 'change' event of the HTML select    
sel.bind('change', update)

# et voilà!! (that's all)
# Now just choose an option from the select element below.