1"""Tornado handlers for the live notebook view.
2
3This is a fork from jupyter/notebook#6.x
4"""
5
6# Copyright (c) Jupyter Development Team.
7# Distributed under the terms of the Modified BSD License.
8
9from jupyter_server.transutils import _i18n
10from jupyter_server.utils import (
11    ensure_async
12)
13from jupyter_server.base.handlers import path_regex, FilesRedirectHandler
14from jupyter_server.extension.handler import (
15    ExtensionHandlerMixin,
16    ExtensionHandlerJinjaMixin
17)
18from jupyter_server.base.handlers import JupyterHandler
19from collections import namedtuple
20import os
21from tornado import web, gen
22HTTPError = web.HTTPError
23
24
25def get_frontend_exporters():
26    from nbconvert.exporters.base import get_export_names, get_exporter
27
28    # name=exporter_name, display=export_from_notebook+extension
29    ExporterInfo = namedtuple('ExporterInfo', ['name', 'display'])
30
31    default_exporters = [
32        ExporterInfo(name='html', display='HTML (.html)'),
33        ExporterInfo(name='latex', display='LaTeX (.tex)'),
34        ExporterInfo(name='markdown', display='Markdown (.md)'),
35        ExporterInfo(name='notebook', display='Notebook (.ipynb)'),
36        ExporterInfo(name='pdf', display='PDF via LaTeX (.pdf)'),
37        ExporterInfo(name='rst', display='reST (.rst)'),
38        ExporterInfo(name='script', display='Script (.txt)'),
39        ExporterInfo(name='slides', display='Reveal.js slides (.slides.html)')
40    ]
41
42    frontend_exporters = []
43    for name in get_export_names():
44        exporter_class = get_exporter(name)
45        exporter_instance = exporter_class()
46        ux_name = getattr(exporter_instance, 'export_from_notebook', None)
47        super_uxname = getattr(super(exporter_class, exporter_instance),
48                               'export_from_notebook', None)
49
50        # Ensure export_from_notebook is explicitly defined & not inherited
51        if ux_name is not None and ux_name != super_uxname:
52            display = _i18n('{} ({})'.format(ux_name,
53                                             exporter_instance.file_extension))
54            frontend_exporters.append(ExporterInfo(name, display))
55
56    # Ensure default_exporters are in frontend_exporters if not already
57    # This protects against nbconvert versions lower than 5.5
58    names = set(exporter.name.lower() for exporter in frontend_exporters)
59    for exporter in default_exporters:
60        if exporter.name not in names:
61            frontend_exporters.append(exporter)
62
63    # Protect against nbconvert 5.5.0
64    python_exporter = ExporterInfo(name='python', display='python (.py)')
65    if python_exporter in frontend_exporters:
66        frontend_exporters.remove(python_exporter)
67
68    # Protect against nbconvert 5.4.x
69    template_exporter = ExporterInfo(name='custom', display='custom (.txt)')
70    if template_exporter in frontend_exporters:
71        frontend_exporters.remove(template_exporter)
72    return sorted(frontend_exporters)
73
74
75class NotebookHandler(ExtensionHandlerJinjaMixin, ExtensionHandlerMixin, JupyterHandler):
76
77    @web.authenticated
78    @gen.coroutine
79    def get(self, path):
80        """get renders the notebook template if a name is given, or
81        redirects to the '/files/' handler if the name is not given."""
82        path = path.strip('/')
83        cm = self.contents_manager
84
85        # will raise 404 on not found
86        try:
87            model = yield ensure_async(cm.get(path, content=False))
88        except web.HTTPError as e:
89            if e.status_code == 404 and 'files' in path.split('/'):
90                # 404, but '/files/' in URL, let FilesRedirect take care of it
91                yield FilesRedirectHandler.redirect_to_files(self, path)
92            else:
93                raise
94        if model['type'] != 'notebook':
95            # not a notebook, redirect to files
96            yield FilesRedirectHandler.redirect_to_files(self, path)
97        name = path.rsplit('/', 1)[-1]
98        self.write(self.render_template('notebook.html',
99                                        notebook_path=path,
100                                        notebook_name=name,
101                                        kill_kernel=False,
102                                        mathjax_url=self.mathjax_url,
103                                        mathjax_config=self.mathjax_config,
104                                        get_frontend_exporters=get_frontend_exporters
105                                        )
106                   )
107
108# -----------------------------------------------------------------------------
109# URL to handler mappings
110# -----------------------------------------------------------------------------
111
112
113default_handlers = [
114    (r"/notebooks%s" % path_regex, NotebookHandler),
115]
116