Skip to content

Commit

Permalink
Voilite POC
Browse files Browse the repository at this point in the history
  • Loading branch information
martinRenou committed Aug 25, 2022
1 parent 295205d commit 21a9222
Show file tree
Hide file tree
Showing 9 changed files with 596 additions and 30 deletions.
9 changes: 4 additions & 5 deletions packages/voila/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,20 +105,18 @@ async function main() {
PageConfig.getOption('federated_extensions')
);

console.log('extension data', extensionData);

const federatedExtensionPromises = [];
const federatedMimeExtensionPromises = [];
const federatedStylePromises = [];

const extensions = await Promise.allSettled(
extensionData.map(async data => {
await loadComponent(
`${URLExt.join(
URLExt.join(
PageConfig.getOption('fullLabextensionsUrl'),
data.name,
data.load
)}`,
),
data.name
);
return data;
Expand Down Expand Up @@ -188,4 +186,5 @@ async function main() {
window.jupyterapp = app;
}

window.addEventListener('load', main);
// window.addEventListener('load', main);
main();
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,5 @@ visual_test =
[options.entry_points]
console_scripts =
voila = voila.app:main
nbconvert.exporters =
voilite_dashboard = voilite.exporter:VoiliteExporter
55 changes: 46 additions & 9 deletions share/jupyter/voila/templates/base/voila_setup.macro.html.j2
Original file line number Diff line number Diff line change
@@ -1,21 +1,58 @@
{%- macro voila_setup_labextensions(base_url, labextensions) -%}
{%- macro voila_setup(base_url) -%}
{% if frontend == 'voila' %}
<script src="{{ page_config['fullStaticUrl'] | e }}/voila.js"></script>
{%- endmacro %}

{# For backward compatibility #}
{%- macro voila_setup(base_url, nbextensions) -%}
{{ voila_setup_labextensions(base_url, labextensions) }}
{% elif frontend == 'voilite' %}
{{ resources.include_js("static/voilite.js") }}
{% endif %}
{%- endmacro %}

{# Helper functions for updating the loading text #}
{%- macro voila_setup_helper_functions() -%}
<script>
var voila_process = function(cell_index, cell_count) {
var voila_heartbeat = function() {
console.log('Ok, voila is still executing...');
}
var update_loading_text = function(cell_index, cell_count) {
var el = document.getElementById("loading_text");
el.innerHTML = `Executing ${cell_index} of ${cell_count}`;
}
var voila_heartbeat = function() {
console.log('Ok, voila is still executing...');
var display_cells = function() {
// remove the loading element
var el = document.getElementById("loading");
el.parentNode.removeChild(el);
// show the cell output
el = document.getElementById("rendered_cells");
el.style.display = '';
}
{% if frontend == 'voila' %}
var voila_process = update_loading_text;
var voila_finish = display_cells;
{% elif frontend == 'voilite' %}
var voila_process = function(cell_index, cell_count, cell_source) {
if (!window.voiliteExecution) {
window.voiliteExecution = Promise.resolve();
}
{# TODO Wait for window.voiliteKernel to be defined #}
window.voiliteExecution = window.voiliteExecution.then(async function () {
update_loading_text(cell_index, cell_count);
await window.voiliteKernel.ready;
return window.voiliteKernel.requestExecute(cell_source).then(function(result) {
console.log(result);
if (cell_index === cell_count) {
display_cells();
}
});
});
};
var voila_finish = function() {
// no-op
}
{% endif %}
</script>
{%- endmacro %}
29 changes: 16 additions & 13 deletions share/jupyter/voila/templates/lab/index.html.j2
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{%- extends 'nbconvert/templates/lab/index.html.j2' -%}
{% import "spinner.macro.html.j2" as spinner %}
{% import "log.macro.html.j2" as log %}
{% from 'voila_setup.macro.html.j2' import voila_setup_helper_functions, voila_setup_labextensions with context %}
{% from 'voila_setup.macro.html.j2' import voila_setup_helper_functions, voila_setup with context %}

{%- block html_head_js -%}
{%- block html_head_js_logs -%}
Expand Down Expand Up @@ -37,6 +37,15 @@
<div id="rendered_cells" style="display: none">
{%- endblock body_header -%}

{# TODO escape HTML from the source #}
{%- block data_other scoped -%}
{# {%- if type == 'application/vnd.voilite.code+txt' -%} #}
<div>
{{ output.data['application/vnd.voilite.code+txt'] }}
</div>
{# {% endif %} #}
{%- endblock data_other -%}

{%- block body_loop -%}
{# from this point on, the kernel is started #}

Expand All @@ -51,6 +60,8 @@
{{ page_config_full | tojson }}
</script>

{{ voila_setup(resources.base_url) }}

{% set cell_count = nb.cells|length %}

{#
Expand All @@ -62,9 +73,9 @@
{%- for cell in cell_generator(nb, kernel_id) -%}
{% set cellloop = loop %}
{%- block any_cell scoped -%}
<script>
voila_process({{ cellloop.index }}, {{ cell_count }})
</script>
<script>
voila_process({{ cellloop.index }}, {{ cell_count }}, `{{ cell.source }}`);
</script>
{{ super() }}
{%- endblock any_cell -%}
{%- endfor -%}
Expand All @@ -74,16 +85,8 @@
{%- block body_footer -%}
</div>
<script type="text/javascript">
(function() {
// remove the loading element
var el = document.getElementById("loading");
el.parentNode.removeChild(el);
// show the cell output
el = document.getElementById("rendered_cells");
el.style.display = '';
})();
voila_finish();
</script>
{{ voila_setup_labextensions(resources.base_url, resources.labextensions) }}
{{ super() }}
{%- endblock body_footer -%}

Expand Down
2 changes: 1 addition & 1 deletion voila/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ async def generate_from_notebook_node(self, nb, resources=None, extra_context={}
def environment(self):
# enable Jinja async template execution
self.enable_async = True
env = super(type(self), self).environment
env = super().environment
if 'jinja2.ext.do' not in env.extensions:
env.add_extension('jinja2.ext.do')
return env
Expand Down
1 change: 1 addition & 0 deletions voila/notebook_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ def inner_cell_generator(nb, kernel_id):
# notebook_executer cell_generator to implement progressive cell rendering

extra_context = {
'frontend': 'voila',
'kernel_start': inner_kernel_start,
'cell_generator': inner_cell_generator,
'notebook_execute': self._jinja_notebook_execute
Expand Down
Empty file added voilite/__init__.py
Empty file.
143 changes: 143 additions & 0 deletions voilite/exporter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#############################################################################
# Copyright (c) 2022, Voilà Contributors #
# Copyright (c) 2022, QuantStack #
# #
# Distributed under the terms of the BSD 3-Clause License. #
# #
# The full license is in the file LICENSE, distributed with this software. #
#############################################################################

import markupsafe

from traitlets import default
from traitlets.config import Config

from jupyter_server.services.contents.largefilemanager import LargeFileManager

from nbconvert.filters.highlight import Highlight2HTML
from nbconvert.exporters import TemplateExporter
from nbconvert.preprocessors import ClearOutputPreprocessor

from voila.exporter import VoilaExporter
from voila.configuration import VoilaConfiguration
from voila.paths import collect_template_paths



class VoiliteExporter(VoilaExporter):
def __init__(self, *args, **kwargs):
kwargs.setdefault("base_url", "/")
kwargs.setdefault("contents_manager", LargeFileManager())

self.voila_configuration = VoilaConfiguration(parent=self)

# TODO
# page_config
self.page_config = {}
# config
# template_name?
# template_paths?

super().__init__(*args, **kwargs)

# TODO
# Investigate why this doesnt work
# jupyter nbconvert --to voilite_dashboard --VoilaConfiguration.strip_sources=False notebook.ipynb
if self.voila_configuration.strip_sources:
self.exclude_input = True
self.exclude_output_prompt = True
self.exclude_input_prompt = True

@default("template_paths")
def _template_paths(self, prune=True, root_dirs=None):
return collect_template_paths(['voila', 'nbconvert'], self.template_name, prune=True)

@property
def default_config(self):
config = super().default_config
config["NbConvertBase"]["display_data_priority"] = [
"application/vnd.voilite.code+txt"
]
return config

def from_notebook_node(self, nb, resources=None, **kwargs):
# this replaces from_notebook_node, but calls template.generate instead of template.render
langinfo = nb.metadata.get("language_info", {})
lexer = langinfo.get("pygments_lexer", langinfo.get("name", None))
highlight_code = self.filters.get(
"highlight_code", Highlight2HTML(pygments_lexer=lexer, parent=self)
)
self.register_filter("highlight_code", highlight_code)

# TODO: This part is already copied three times across
# nbconvert and Voila, we should do something about it
nb_copy, resources = super(TemplateExporter, self).from_notebook_node(
nb, resources, **kwargs
)
resources.setdefault("raw_mimetypes", self.raw_mimetypes)
resources["global_content_filter"] = {
"include_code": not self.exclude_code_cell,
"include_markdown": not self.exclude_markdown,
"include_raw": not self.exclude_raw,
"include_unknown": not self.exclude_unknown,
"include_input": not self.exclude_input,
"include_output": not self.exclude_output,
"include_input_prompt": not self.exclude_input_prompt,
"include_output_prompt": not self.exclude_output_prompt,
"no_prompt": self.exclude_input_prompt and self.exclude_output_prompt,
}

extra_context = dict(
frontend='voilite',
kernel_start=self.kernel_start,
cell_generator=self.cell_generator
)

html = []
for html_snippet in self.template.generate(
nb=nb_copy,
resources=resources,
**extra_context,
static_url=self.static_url,
page_config=self.page_config
):
html.append(html_snippet)

return "".join(html), resources

def kernel_start(self, nb):
# TODO WHAT
kernel_id = '36'
return kernel_id

def cell_generator(self, nb, kernel_id):
# self.cwd = os.path.dirname(notebook_path) ??
# nb, _ = ClearOutputPreprocessor().preprocess(
# nb, {'metadata': {'path': self.cwd}}
# )
nb, _ = ClearOutputPreprocessor().preprocess(nb, {})
for cell_idx, input_cell in enumerate(nb.cells):
output = input_cell.copy()
output["outputs"] = [dict(
output_type="execute_result",
data={
"application/vnd.voilite.code+txt": input_cell.source
},
metadata={}
)]
yield output

def _init_resources(self, resources):
# VoilaExporter bypasses the nbconvert behavior of inlining the assets like CSS and JS
# We do want to inline those assets though, so
# we bypass this bypass by not calling VoilaExporter._init_resources
resources = super(VoilaExporter, self)._init_resources(resources)

def resources_include_js(name):
env = self.environment
code = """<script type="module">\n%s</script>""" % (env.loader.get_source(env, name)[0])
return markupsafe.Markup(code)

resources["include_js"] = resources_include_js

return resources
Loading

0 comments on commit 21a9222

Please sign in to comment.