Added Jupyter Notebook previewer

This commit is contained in:
Pedro Ferreira 2015-08-28 17:23:08 +02:00
parent 6fd1ec7e30
commit 7a1046a22b
9 changed files with 283 additions and 0 deletions

View File

@ -0,0 +1 @@
graft indico_previewer_jupyter/templates

View File

@ -0,0 +1,21 @@
# This file is part of Indico.
# Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
#
# Indico is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# Indico is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Indico; if not, see <http://www.gnu.org/licenses/>.
"""Syntax highlighting for IPython/Jupyter Notebooks."""
from .plugin import JupyterPreviewerPlugin
__all__ = ('JupyterPreviewerPlugin',)

View File

@ -0,0 +1,22 @@
# This file is part of Indico.
# Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
#
# Indico is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# Indico is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Indico; if not, see <http://www.gnu.org/licenses/>.
from indico.core.plugins import IndicoPluginBlueprint
from indico_previewer_jupyter.controllers import RHEventPreviewIPyNB
blueprint = IndicoPluginBlueprint('previewer_jupyter', __name__)
blueprint.add_url_rule('/preview/ipynb/<int:attachment_id>', 'preview_ipynb', RHEventPreviewIPyNB)

View File

@ -0,0 +1,53 @@
# This file is part of Indico.
# Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
#
# Indico is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# Indico is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Indico; if not, see <http://www.gnu.org/licenses/>.
import nbformat
from flask import session, render_template, request
from nbconvert.exporters import HTMLExporter
from traitlets.config import Config
from werkzeug.exceptions import Forbidden
from indico.modules.attachments import Attachment
from MaKaC.webinterface.rh.base import RH
from indico_previewer_jupyter.cpp_highlighter import CppHighlighter
class RHEventPreviewIPyNB(RH):
def _checkProtection(self):
RH._checkProtection(self)
if not self.attachment.can_access(session.user):
raise Forbidden
def _checkParams(self, params):
RH._checkParams(self, params)
self.attachment = Attachment.find_one(id=request.view_args['attachment_id'], is_deleted=False)
def _process(self):
config = Config()
config.HTMLExporter.preprocessors = [CppHighlighter]
config.HTMLExporter.template_file = 'full'
with self.attachment.file.open() as f:
notebook = nbformat.read(f, as_version=4)
html_exporter = HTMLExporter(config=config)
body, resources = html_exporter.from_notebook_node(notebook)
css_code = '\n'.join(resources['inlining'].get('css', []))
return render_template('previewer_jupyter:ipynb_preview.html', attachment=self.attachment,
html_code=body, css_code=css_code)

View File

@ -0,0 +1,73 @@
# This file is part of Indico.
# Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
#
# Indico is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# Indico is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Indico; if not, see <http://www.gnu.org/licenses/>.
"""
This preprocessor marks cells' metadata so that the appropriate highlighter can be used in the `highlight` filter.
More precisely, the language of a cell is set to C++ in two scenarios:
- Python notebooks: cells with `%%cpp` or `%%dcl` magic extensions.
- ROOT prompt C++ notebooks: all cells.
This preprocessor relies on the metadata section of the notebook to find out about the notebook's language.
Code courtesy of the ROOT project (https://root.cern.ch).
"""
import re
from nbconvert.preprocessors.base import Preprocessor
class CppHighlighter(Preprocessor):
"""Detects and tags code cells that use the C++ language."""
magics = ['%%cpp', '%%dcl']
cpp = 'cpp'
python = 'python'
def __init__(self, config=None, **kw):
super(CppHighlighter, self).__init__(config=config, **kw)
# Build regular expressions to catch language extensions or switches and choose
# an adequate pygments lexer
any_magic = "|".join(self.magics)
self.re_magic_language = re.compile(r"^\s*({0}).*".format(any_magic), re.DOTALL)
def matches(self, source, reg_expr):
return bool(reg_expr.match(source))
def _preprocess_cell_python(self, cell, resources, cell_index):
# Mark %%cpp and %%dcl code cells as cpp
if cell.cell_type == "code" and self.matches(cell.source, self.re_magic_language):
cell['metadata']['magics_language'] = self.cpp
return cell, resources
def _preprocess_cell_cpp(self, cell, resources, cell_index):
# Mark all code cells as cpp
if cell.cell_type == "code":
cell['metadata']['magics_language'] = self.cpp
return cell, resources
def preprocess(self, nb, resources):
self.preprocess_cell = self._preprocess_cell_python
try:
if nb.metadata.kernelspec.language == "c++":
self.preprocess_cell = self._preprocess_cell_cpp
except:
# if no language metadata, keep python as default
pass
return super(CppHighlighter, self).preprocess(nb, resources)

View File

@ -0,0 +1,61 @@
# This file is part of Indico.
# Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
#
# Indico is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# Indico is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Indico; if not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
import mimetypes
import re
from flask import render_template
from indico.core import signals
from indico.core.plugins import IndicoPlugin, url_for_plugin
from indico.modules.attachments.preview import Previewer
from indico_previewer_jupyter.blueprint import blueprint
def register_custom_mimetypes():
mimetypes.add_type(b'application/x-ipynb+json', b'.ipynb')
register_custom_mimetypes()
class NotebookPreviewer(Previewer):
ALLOWED_CONTENT_TYPE = re.compile(r"^application/x-ipynb\+json$")
@classmethod
def generate_content(cls, attachment):
return render_template('previewer_jupyter:iframe_preview.html',
source_url=url_for_plugin('previewer_jupyter.preview_ipynb', attachment))
class JupyterPreviewerPlugin(IndicoPlugin):
"""Jupyter Notebook renderer"""
configurable = False
def init(self):
super(JupyterPreviewerPlugin, self).init()
self.connect(signals.attachments.get_file_previewers, self._get_file_previewers)
def get_blueprints(self):
yield blueprint
def _get_file_previewers(self, sender, **kwargs):
yield NotebookPreviewer

View File

@ -0,0 +1 @@
<iframe src="{{ source_url }}?from_preview=1"></iframe>

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Preview</title>
<style>
{{ css_code }}
</style>
</head>
<body>
{{ html_code | safe }}
</body>
</html>

View File

@ -0,0 +1,39 @@
# This file is part of Indico.
# Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
#
# Indico is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# Indico is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Indico; if not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from setuptools import setup, find_packages
setup(
name='indico_previewer_jupyter',
version='0.1',
url='https://github.com/indico/indico-plugins',
license='https://www.gnu.org/licenses/gpl-3.0.txt',
author='Indico Team',
author_email='indico-team@cern.ch',
packages=find_packages(),
zip_safe=False,
include_package_data=True,
platforms='any',
install_requires=[
'indico>=1.9.4',
'nbconvert>=4.0.0',
'functools32'
],
entry_points={'indico.plugins': {'previewer_jupyter = indico_previewer_jupyter:JupyterPreviewerPlugin'}}
)