Why
Jupyter notebooks are great for data analysis, but when we need to share the analysis, some people may not care about the code. It would be great to have a solution that automatically generates reports where the code can be toggled by a button. After some online searching and experimentation, the following is my solution.
Custom Template for Exporting HTML File
Most people know that we can use nbconvert
to export HTML file, but few know that we can choose a template to use during this process: jupyter nbconvert --to html 'example.ipynb' --template=custom_template.tpl
This effectively gives us unlimited flexibility to format the output HTML file however we want. (For more details on how to make a custom template, please refer to the official document on customizing nbconvert and the Jinja2 template language guide.)
Unfortunately, I found that we can not directly inherit the existing template used by nbconvert, since the position we want to insert our toggle button is not available if we inherit the existing templates. We have to alter some of the existing templates to create our own.
If your Jupyter is installed using anaconda in a python3.6 environment called py3
, then the default templates for generating HTML files can be found under the path:
<Anaconda_Folder>/envs/py3/lib/python3.6/site-packages/nbconvert/templates/html/
.
I copied the full.tpl
template and added the script part in the <head>
element:
#| code-fold: true
{%- block toggle_script -%}
<script>
function code_toggle() {
if (code_shown){
$('div.input').hide('500');
$('#toggleButton').val('Show Code')
} else {
$('div.input').show('500');
$('#toggleButton').val('Hide Code')
}
code_shown = !code_shown
}
$( document ).ready(function(){
code_shown=false;
$('div.input').hide();
$('div.prompt').hide();
$('div.back-to-top').hide();
$('nav#menubar').hide();
$('.breadcrumb').hide();
$('.hidden-print').hide();
});
</script>
{%- endblock toggle_script -%}
and the HTML part for the button to the body.
<body>
<div tabindex="-1" id="notebook" class="border-box-sizing">
<div class="container" id="notebook-container">
<form action="javascript:code_toggle()">
<input type="submit" id="toggleButton" value="Show Code">
</form>
{{ super() }}
</div>
</div>
</body>
See this gist for the full template code.
Use Jupyter’s Post-Save Hook to Automate
We can add a function in the Jupyter configuration file to automatically export HTML file whenever we save a notebook. The Jupyter configuration file can be found at “~/.jupyter/jupyter_notebook_config.py” on mac.
An example code for jupyter_notebook_config.py
that exports an HTML file on save for notebooks with “Report-” in their names:
import os
from subprocess import check_call
def post_save(model, os_path, contents_manager):
"""post-save hook for converting notebooks to .py scripts"""
if model['type'] != 'notebook':
return # only do this for notebooks
d, fname = os.path.split(os_path)
if 'Report-' in fname:
# generate html files
check_call(['jupyter', 'nbconvert', '--to', 'html',
fname, '--template=report.tpl'], cwd=d)
print("finished creating html file {}".format(fname.replace('.ipynb', '.html')))
c = get_config()
c.FileContentsManager.post_save_hook = post_save
c.NotebookApp.open_browser = False
See Jupyter documentation on file save hooks for more details on how to use save hooks.