{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Nikola makes sharing Jupyter (IPython) Notebooks easy. You can render a static page like this and provide a link to download the notebook for interactive usage. However, a major downside with this is that the reader must set up his/her own Python environment which has all the dependencies installed and has to know how to run notebooks. It'd be much simpler if the reader could just click a link and an interactive notebook opened in the browser without him/her needing to do anything else. Well, I just discovered a service which easily enables this: [Binder](http://mybinder.org).\n", "\n", "\n", "\n", "In Binder, you provide your GitHub repository and then anyone can use URL http://mybinder.org/repo/GITHUB_USERNAME/GITHUB_REPOSITORY to access the notebooks in that repository interactively. Anyone going to that URL will get a separate isolated environment, so modifying the notebooks won't mess up anything. You can access specific notebooks by appending the path to a notebook to the URL. Simple.\n", "\n", "This post is a Jupyter Notebook so you can click \"Run Notebook\" link in the top menu to run it interactively in Binder. To have some Python code for you to play with, here's a few lines of code:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "42.0" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", "np.sqrt(1764)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This post gives you instructions on how to add links to Binder into your Nikola powered blog. I assume that you have set up the blogging environment similarly as described in my [previous post](how-to-blog-with-jupyter-ipython-notebook-and-nikola). But first, go to [Binder](http://mybinder.org) and build your GitHub repository. I chose `requirements.txt` do define the dependencies. After building, follow these instructions to add the links to your posts." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Adding links to Binder\n", "\n", "Assuming you have created a custom theme for your site, you need to overwrite a few parts in your templates. First, create a template directory for your theme if it doesn't exist:\n", "\n", "```\n", "mkdir themes/YOUR_THEME_NAME/templates\n", "```\n", "\n", "We need to make minor changes to a few template, so we'll copy them and then modify. Copy `post.tmpl` of `bootstrap3-jinja` theme and `post_header.tmpl` of `base-jinja` theme to your `templates` directory. These are the files used by `bootstrap3-jinja` theme (which is the parent theme of my custom theme). You can see and download the full versions of the modified templates from [GitHub](https://github.com/jluttine/jaakkoluttinen.fi/tree/master/themes/jaakko/templates/). For clarity, I show here only the lines that have been modified. In `post_header.tmpl`:\n", "\n", "```\n", "... (to macro html_sourcelink)\n", "{% if post.compiler.name == \"ipynb\" %} \n", "

\n", "Run Notebook\n", "

\n", "{% endif %}\n", "...\n", "```\n", "\n", "In `post.tmpl`:\n", "\n", "```\n", "... (to block content)\n", "{% if post.compiler.name == \"ipynb\" %} \n", "
\n", "

\n", "This post is a Jupyter Notebook.\n", "You can download it or\n", "run it interactively in Binder.\n", "

\n", "{% endif %}\n", "\n", "... (to block sourcelink)\n", "{% if post.compiler.name == \"ipynb\" %} \n", "
  • \n", "\n", " Run Notebook\n", "\n", "
  • \n", "{% endif %}\n", "...\n", "```\n", "\n", "Also, add the following definitions to `conf.py`:\n", "\n", "```\n", "GITHUB_REPOSITORY = 'YOUR_GITHUB_USERNAME/REPOSITORY_NAME'\n", "BINDER_URL = 'http://mybinder.org/repo'\n", "```\n", "\n", "Obviously, replace `YOUR_GITHUB_USERNAME` and `REPOSITORY_NAME` with your username and repository. Now your Jupyter Notebook blog posts have three links to Binder for the reader to run the notebook interactively." ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "# Building Binder automatically when deploying\n", "\n", "One downside of Binder is that it doesn't automatically update on GitHub commits. You have to go to [mybinder.org](http://mybinder.org/) and manually build the repo again. This is understandable, as they told themselves: \"rebuilding on every commit for every binder would put too much strain on our poor build server\". Big thanks for the poor servers anyway, they're doing an amazing job.\n", "\n", "I don't want to put too much strain on Binder servers but I would like to automate the rebuilding process somehow. So I wrote a simple script which posts a build request to Binder (thanks to Paavo Leinonen for helpful comments):\n", "\n", "```\n", "#!/bin/env python\n", "\n", "# Automatic form submisson to http://mybinder.org/\n", "\n", "import requests\n", "\n", "# Add the directory of conf.py into the Python path\n", "import sys\n", "import os\n", "sys.path.append(os.path.dirname(os.path.dirname(__file__)))\n", "\n", "from conf import GITHUB_REPOSITORY\n", "from conf import BINDER_API_URL\n", "\n", "submission = {\n", " \"dependencies\": [\"requirements.txt\"],\n", " \"services\": [],\n", "}\n", "\n", "baseurl = 'api.mybinder.org'\n", "url = BINDER_API_URL + '/apps/' + GITHUB_REPOSITORY\n", "\n", "r = requests.post(url, json=submission)\n", "print(r)\n", "```\n", "\n", "I named the script `bin/binder_deploy` (create `bin` directory first) and made it executable:\n", "\n", "```\n", "chmod +x bin/binder_deploy\n", "```\n", "\n", "In order to have `bin` directory in your path when in your virtual environment, you can add the following lines to `$VIRTUAL_ENV/bin/postactivate`:\n", "\n", "```\n", "PROJECT_DIR=`cat $VIRTUAL_ENV/.project`\n", "export PATH=$PROJECT_DIR/bin:$PATH\n", "```\n", "\n", "Now you should have `binder_deploy` command available after activating your virtual environment with `workon blog`. In order for the script to work, you need to define the following variable in `conf.py`:\n", "\n", "```\n", "BINDER_API_URL = 'http://api.mybinder.org'\n", "```\n", "\n", "With this setup, you should run `binder_deploy` after you have run `nikola github_deploy` and `git push` which pushes changes in your Jupyter Notebooks to GitHub. But don't rebuild Binder if not necessary (e.g., no changes in notebooks) in order to avoid putting too much workload on Binder. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Conclusion\n", "\n", "If you have ideas on how to improve this setting, please comment below. Also, note that Binder is open-source software, so you can get the [source code](https://github.com/binder-project/binder) and run your own Binder server!" ] } ], "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.5.0" } }, "nbformat": 4, "nbformat_minor": 0 }