diff --git a/doc/ext/notebook.py b/doc/ext/notebook.py new file mode 100644 index 00000000..054a9c44 --- /dev/null +++ b/doc/ext/notebook.py @@ -0,0 +1,126 @@ +__all__ = ['python_to_notebook', 'Notebook'] + +import json +import copy +import warnings + + +# Skeleton notebook in JSON format +skeleton_nb = """{ + "metadata": { + "name":"" + }, + "nbformat": 3, + "nbformat_minor": 0, + "worksheets": [ + { + "cells": [ + { + "cell_type": "code", + "collapsed": false, + "input": [ + "%matplotlib inline" + ], + "language": "python", + "metadata": {}, + "outputs": [] + } + ], + "metadata": {} + } + ] +}""" + + +class Notebook(object): + """ + Notebook object for building an IPython notebook cell-by-cell. + """ + + def __init__(self): + # cell type code + self.cell_code = { + 'cell_type': 'code', + 'collapsed': False, + 'input': [ + '# Code Goes Here' + ], + 'language': 'python', + 'metadata': {}, + 'outputs': [] + } + + # cell type markdown + self.cell_md = { + 'cell_type': 'markdown', + 'metadata': {}, + 'source': [ + 'Markdown Goes Here' + ] + } + + self.template = json.loads(skeleton_nb) + self.cell_type = {'input': self.cell_code, 'source': self.cell_md} + self.valuetype_to_celltype = {'code': 'input', 'markdown': 'source'} + + def add_cell(self, value, cell_type='code'): + """Add a notebook cell. + + Parameters + ---------- + value : str + Cell content. + cell_type : {'code', 'markdown'} + Type of content (default is 'code'). + + """ + if cell_type in ['markdown', 'code']: + key = self.valuetype_to_celltype[cell_type] + cells = self.template['worksheets'][0]['cells'] + cells.append(copy.deepcopy(self.cell_type[key])) + # assign value to the last cell + cells[-1][key] = value + else: + warnings.warn('Ignoring unsupported cell type (%s)' % cell_type) + + def json(self): + """Return a JSON representation of the notebook. + + Returns + ------- + str + JSON notebook. + + """ + return json.dumps(self.template, indent=2) + + +def test_notebook_basic(): + nb = Notebook() + assert(json.loads(nb.json()) == json.loads(skeleton_nb)) + + +def test_notebook_add(): + nb = Notebook() + + str1 = 'hello world' + str2 = 'f = lambda x: x * x' + + nb.add_cell(str1, cell_type='markdown') + nb.add_cell(str2, cell_type='code') + + d = json.loads(nb.json()) + cells = d['worksheets'][0]['cells'] + values = [c['input'] if c['cell_type'] == 'code' else c['source'] + for c in cells] + + assert values[1] == str1 + assert values[2] == str2 + + assert cells[1]['cell_type'] == 'markdown' + assert cells[2]['cell_type'] == 'code' + + +if __name__ == "__main__": + import numpy.testing as npt + npt.run_module_suite() diff --git a/doc/ext/plot2rst.py b/doc/ext/plot2rst.py index 3b26aeb9..258a6929 100644 --- a/doc/ext/plot2rst.py +++ b/doc/ext/plot2rst.py @@ -80,6 +80,10 @@ from skimage import io from skimage import transform from skimage.util.dtype import dtype_range +from notebook import Notebook + +from docutils.core import publish_parts + LITERALINCLUDE = """ .. literalinclude:: {src_name} @@ -94,6 +98,13 @@ CODE_LINK = """ """ +NOTEBOOK_LINK = """ + +**IPython Notebook:** :download:`download <{0}>` +(generated using ``skimage`` |version|) + +""" + TOCTREE_TEMPLATE = """ .. toctree:: :hidden: @@ -305,16 +316,20 @@ def write_example(src_name, src_dir, rst_dir, cfg): image_dir = rst_dir.pjoin('images') thumb_dir = image_dir.pjoin('thumb') + notebook_dir = rst_dir.pjoin('notebook') image_dir.makedirs() thumb_dir.makedirs() + notebook_dir.makedirs() base_image_name = os.path.splitext(src_name)[0] image_path = image_dir.pjoin(base_image_name + '_{0}.png') basename, py_ext = os.path.splitext(src_name) rst_path = rst_dir.pjoin(basename + cfg.source_suffix) + notebook_path = notebook_dir.pjoin(basename + '.ipynb') - if _plots_are_current(src_path, image_path) and rst_path.exists: + if _plots_are_current(src_path, image_path) and rst_path.exists and \ + notebook_path.exists: return blocks = split_code_and_text_blocks(example_file) @@ -341,8 +356,11 @@ def write_example(src_name, src_dir, rst_dir, cfg): example_rst += LITERALINCLUDE.format(**code_info) example_rst += CODE_LINK.format(src_name) + ipnotebook_name = src_name.replace('.py', '.ipynb') + ipnotebook_name = './notebook/' + ipnotebook_name + example_rst += NOTEBOOK_LINK.format(ipnotebook_name) - f = open(rst_path,'w') + f = open(rst_path, 'w') f.write(example_rst) f.flush() @@ -359,6 +377,24 @@ def write_example(src_name, src_dir, rst_dir, cfg): else: shutil.copy(cfg.plot2rst_default_thumb, thumb_path) + # Export example to IPython notebook + nb = Notebook() + + for (cell_type, _, content) in blocks: + content = content.rstrip('\n') + + if cell_type == 'code': + nb.add_cell(content, cell_type='code') + else: + content = content.replace('"""', '') + content = '\n'.join([line for line in content.split('\n') if + not line.startswith('.. image')]) + html = publish_parts(content, writer_name='html')['html_body'] + nb.add_cell(html, cell_type='markdown') + + with open(notebook_path, 'w') as f: + f.write(nb.json()) + def save_thumbnail(image, thumb_path, shape): """Save image as a thumbnail with the specified shape.