Skip to content
Snippets Groups Projects
Commit 3f601113 authored by jarmil's avatar jarmil
Browse files

WIP: jupyter notebooks: generovani hotovo

parent 94d473d3
Branches
No related tags found
No related merge requests found
...@@ -4,4 +4,4 @@ line_length = 88 ...@@ -4,4 +4,4 @@ line_length = 88
multi_line_output = 3 multi_line_output = 3
default_sectiont = "THIRDPARTY" default_sectiont = "THIRDPARTY"
include_trailing_comma = true include_trailing_comma = true
known_third_party = arrow,django,environ,faker,ics,modelcluster,pirates,pytest,pytz,requests,sentry_sdk,snapshottest,taggit,wagtail,wagtailmetadata known_third_party = arrow,django,environ,faker,ics,modelcluster,nbconvert,pirates,pytest,pytz,requests,sentry_sdk,snapshottest,taggit,traitlets,wagtail,wagtailmetadata
...@@ -58,6 +58,18 @@ Kalendář se stáhne při uložení modelu obsahujícího `CalendarMixin`. ...@@ -58,6 +58,18 @@ Kalendář se stáhne při uložení modelu obsahujícího `CalendarMixin`.
Appka přidává management command `update_callendars`, který stahuje a updatuje Appka přidává management command `update_callendars`, který stahuje a updatuje
kalendáře. Je třeba ho pravidelně volat na pozadí (přes CRON). kalendáře. Je třeba ho pravidelně volat na pozadí (přes CRON).
### Jupyter notebooky
Appka Uniweb umí vložit do stránky Jupyter notebook a zobrazit jeho výstup.
Pokud některé buňky nechceš generovat do výstupní stránky, nastav u nich tag "exclude".
Pokud chceš generovat jen výstup dané buňky, použij tag "output"
Pozor: u plotly grafů je nutno zadat tagem "output" výstup buňky s inicializací knihovny, tedy něco kde je "import plotly" apod.
Pokud celou takovou buňku vynecháš tagem "exclude", žádné grafy se nezobrazí.
## Deployment ## Deployment
### Konfigurace ### Konfigurace
......
{%- extends 'display_priority.tpl' -%}
{% from 'celltags.tpl' import celltags %}
{% block codecell %}
<div class="cell border-box-sizing code_cell rendered{{ celltags(cell) }}">
{{ super() }}
</div>
{%- endblock codecell %}
{% block input_group -%}
<div class="input">
{{ super() }}
</div>
{% endblock input_group %}
{% block output_group %}
<div class="output_wrapper">
<div class="output">
{{ super() }}
</div>
<div class="output_divider" style="margin-bottom: 5px;">
</div>
</div>
{% endblock output_group %}
{% block in_prompt -%}
<div class="prompt input_prompt">
{%- if cell.execution_count is defined -%}
In&nbsp;[{{ cell.execution_count|replace(None, "&nbsp;") }}]:
{%- else -%}
In&nbsp;[&nbsp;]:
{%- endif -%}
</div>
{%- endblock in_prompt %}
{% block empty_in_prompt -%}
<div class="prompt input_prompt">
</div>
{%- endblock empty_in_prompt %}
{#
output_prompt doesn't do anything in HTML,
because there is a prompt div in each output area (see output block)
#}
{% block output_prompt %}
{% endblock output_prompt %}
{% block input %}
<div class="inner_cell">
<div class="input_area">
{{ cell.source }}
</div>
</div>
{%- endblock input %}
{% block output_area_prompt %}
{%- if output.output_type == 'execute_result' -%}
<div class="prompt output_prompt">
{%- if cell.execution_count is defined -%}
Out[{{ cell.execution_count|replace(None, "&nbsp;") }}]:
{%- else -%}
Out[&nbsp;]:
{%- endif -%}
{%- else -%}
<div class="prompt">
{%- endif -%}
</div>
{% endblock output_area_prompt %}
{% block output %}
<div class="output_area">
{% if resources.global_content_filter.include_output_prompt %}
{{ self.output_area_prompt() }}
{% endif %}
{{ super() }}
</div>
{% endblock output %}
{% block markdowncell scoped %}
<div class="cell border-box-sizing text_cell rendered{{ celltags(cell) }}">
{%- if resources.global_content_filter.include_input_prompt-%}
{{ self.empty_in_prompt() }}
{%- endif -%}
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
{{ cell.source | markdown2html | strip_files_prefix }}
</div>
</div>
</div>
{%- endblock markdowncell %}
{% block unknowncell scoped %}
unknown type {{ cell.type }}
{% endblock unknowncell %}
{% block execute_result -%}
{%- set extra_class="output_execute_result" -%}
{% block data_priority scoped %}
{{ super() }}
{% endblock data_priority %}
{%- set extra_class="" -%}
{%- endblock execute_result %}
{% block stream_stdout -%}
<div class="output_subarea output_stream output_stdout output_text">
<pre>
{{- output.text | ansi2html -}}
</pre>
</div>
{%- endblock stream_stdout %}
{% block stream_stderr -%}
<div class="output_subarea output_stream output_stderr output_text">
<pre>
{{- output.text | ansi2html -}}
</pre>
</div>
{%- endblock stream_stderr %}
{% block data_svg scoped -%}
<div class="output_svg output_subarea {{ extra_class }}">
{%- if output.svg_filename %}
<img src="{{ output.svg_filename | posix_path }}">
{%- else %}
{{ output.data['image/svg+xml'] }}
{%- endif %}
</div>
{%- endblock data_svg %}
{% block data_html scoped -%}
<div class="output_html rendered_html output_subarea {{ extra_class }}">
{{ output.data['text/html'] }}
</div>
{%- endblock data_html %}
{% block data_markdown scoped -%}
<div class="output_markdown rendered_html output_subarea {{ extra_class }}">
{{ output.data['text/markdown'] | markdown2html }}
</div>
{%- endblock data_markdown %}
{% block data_png scoped %}
<div class="output_png output_subarea {{ extra_class }}">
{%- if 'image/png' in output.metadata.get('filenames', {}) %}
<img src="{{ output.metadata.filenames['image/png'] | posix_path }}"
{%- else %}
<img src="data:image/png;base64,{{ output.data['image/png'] }}"
{%- endif %}
{%- set width=output | get_metadata('width', 'image/png') -%}
{%- if width is not none %}
width={{ width }}
{%- endif %}
{%- set height=output | get_metadata('height', 'image/png') -%}
{%- if height is not none %}
height={{ height }}
{%- endif %}
{%- if output | get_metadata('unconfined', 'image/png') %}
class="unconfined"
{%- endif %}
{%- set alttext=(output | get_metadata('alt', 'image/png')) or (cell | get_metadata('alt')) -%}
{%- if alttext is not none %}
alt="{{ alttext }}"
{%- endif %}
>
</div>
{%- endblock data_png %}
{% block data_jpg scoped %}
<div class="output_jpeg output_subarea {{ extra_class }}">
{%- if 'image/jpeg' in output.metadata.get('filenames', {}) %}
<img src="{{ output.metadata.filenames['image/jpeg'] | posix_path }}"
{%- else %}
<img src="data:image/jpeg;base64,{{ output.data['image/jpeg'] }}"
{%- endif %}
{%- set width=output | get_metadata('width', 'image/jpeg') -%}
{%- if width is not none %}
width={{ width }}
{%- endif %}
{%- set height=output | get_metadata('height', 'image/jpeg') -%}
{%- if height is not none %}
height={{ height }}
{%- endif %}
{%- if output | get_metadata('unconfined', 'image/jpeg') %}
class="unconfined"
{%- endif %}
{%- set alttext=(output | get_metadata('alt', 'image/jpeg')) or (cell | get_metadata('alt')) -%}
{%- if alttext is not none %}
alt="{{ alttext }}"
{%- endif %}
>
</div>
{%- endblock data_jpg %}
{% block data_latex scoped %}
<div class="output_latex output_subarea {{ extra_class }}">
{{ output.data['text/latex'] }}
</div>
{%- endblock data_latex %}
{% block error -%}
<div class="output_subarea output_text output_error">
<pre>
{{- super() -}}
</pre>
</div>
{%- endblock error %}
{%- block traceback_line %}
{{ line | ansi2html }}
{%- endblock traceback_line %}
{%- block data_text scoped %}
<div class="output_text output_subarea {{ extra_class }}">
<pre>
{{- output.data['text/plain'] | ansi2html -}}
</pre>
</div>
{%- endblock -%}
{%- block data_javascript scoped %}
{% set div_id = uuid4() %}
<div id="{{ div_id }}"></div>
<div class="output_subarea output_javascript {{ extra_class }}">
<script type="text/javascript">
var element = $('#{{ div_id }}');
{{ output.data['application/javascript'] }}
</script>
</div>
{%- endblock -%}
{%- block data_widget_state scoped %}
{% set div_id = uuid4() %}
{% set datatype_list = output.data | filter_data_type %}
{% set datatype = datatype_list[0]%}
<div id="{{ div_id }}"></div>
<div class="output_subarea output_widget_state {{ extra_class }}">
<script type="text/javascript">
var element = $('#{{ div_id }}');
</script>
<script type="{{ datatype }}">
{{ output.data[datatype] | json_dumps }}
</script>
</div>
{%- endblock data_widget_state -%}
{%- block data_widget_view scoped %}
{% set div_id = uuid4() %}
{% set datatype_list = output.data | filter_data_type %}
{% set datatype = datatype_list[0]%}
<div id="{{ div_id }}"></div>
<div class="output_subarea output_widget_view {{ extra_class }}">
<script type="text/javascript">
var element = $('#{{ div_id }}');
</script>
<script type="{{ datatype }}">
{{ output.data[datatype] | json_dumps }}
</script>
</div>
{%- endblock data_widget_view -%}
{%- block footer %}
{% set mimetype = 'application/vnd.jupyter.widget-state+json'%}
{% if mimetype in nb.metadata.get("widgets",{})%}
<script type="{{ mimetype }}">
{{ nb.metadata.widgets[mimetype] | json_dumps }}
</script>
{% endif %}
{{ super() }}
{%- endblock footer-%}
{%- macro celltags(cell) -%}
{% if cell.metadata.tags | length > 0 -%}
{% for tag in cell.metadata.tags -%}
{{ ' celltag_' ~ tag -}}
{%- endfor -%}
{%- endif %}
{%- endmacro %}
{%- macro mathjax(url='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/latest.js?config=TeX-AMS_HTML') -%}
<!-- Load mathjax -->
<script src="{{url}}"></script>
<!-- MathJax configuration -->
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {
inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
processEscapes: true,
processEnvironments: true
},
// Center justify equations in code and markdown cells. Elsewhere
// we use CSS to left justify single line equations in code cells.
displayAlign: 'center',
"HTML-CSS": {
styles: {'.MathJax_Display': {"margin": 0}},
linebreaks: { automatic: true }
}
});
</script>
<!-- End of mathjax configuration -->
{%- endmacro %}
{%- extends 'basic.tpl' -%}
{% from 'mathjax.tpl' import mathjax %}
{%- block header -%}
{%- block html_head -%}
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.10/require.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
{% block ipywidgets %}
{%- if "widgets" in nb.metadata -%}
<script>
(function() {
function addWidgetsRenderer() {
var mimeElement = document.querySelector('script[type="application/vnd.jupyter.widget-view+json"]');
var scriptElement = document.createElement('script');
var widgetRendererSrc = '{{ resources.ipywidgets_base_url }}@jupyter-widgets/html-manager@*/dist/embed-amd.js';
var widgetState;
// Fallback for older version:
try {
widgetState = mimeElement && JSON.parse(mimeElement.innerHTML);
if (widgetState && (widgetState.version_major < 2 || !widgetState.version_major)) {
widgetRendererSrc = '{{ resources.ipywidgets_base_url }}jupyter-js-widgets@*/dist/embed.js';
}
} catch(e) {}
scriptElement.src = widgetRendererSrc;
document.body.appendChild(scriptElement);
}
document.addEventListener('DOMContentLoaded', addWidgetsRenderer);
}());
</script>
{%- endif -%}
{% endblock ipywidgets %}
{% for css in resources.inlining.css -%}
<style type="text/css">
{{ css }}
</style>
{% endfor %}
<style type="text/css">
/* Overrides of notebook CSS for static HTML export */
body {
overflow: visible;
padding: 8px;
}
div#notebook {
overflow: visible;
border-top: none;
}
{%- if resources.global_content_filter.no_prompt-%}
div#notebook-container{
padding: 6ex 12ex 8ex 12ex;
}
{%- endif -%}
@media print {
div.cell {
display: block;
page-break-inside: avoid;
}
div.output_wrapper {
display: block;
page-break-inside: avoid;
}
div.output {
display: block;
page-break-inside: avoid;
}
}
</style>
<!-- Loading mathjax macro -->
{{ mathjax() }}
{%- endblock html_head -%}
{%- endblock header -%}
{% block body %}
<div tabindex="-1" id="notebook" class="border-box-sizing">
<div class="container" id="notebook-container">
{{ super() }}
</div>
</div>
{%- endblock body %}
{% block footer %}
{{ super() }}
{% endblock footer %}
{% load uniweb_filters static wagtailcore_tags wagtailimages_tags wagtailmetadata_tags %} {% load static wagtailcore_tags wagtailimages_tags wagtailmetadata_tags %}
<!doctype html> <!doctype html>
<html lang="cs"> <html lang="cs">
<head> <head>
......
{% load wagtailcore_tags wagtailimages_tags %} {% load uniweb_filters wagtailcore_tags wagtailimages_tags %}
<section class="mb-8 lg:mb-16"> <section class="mb-8 lg:mb-16">
{% for block in page.content %} {% for block in page.content %}
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
{% if block.block_type == "jupyter" %} {% if block.block_type == "jupyter" %}
<div class="content-block my-4 clearfix{% if forloop.first %} mt-8 lg:mt-12{% endif %}"> <div class="content-block my-4 clearfix{% if forloop.first %} mt-8 lg:mt-12{% endif %}">
{{ block.value|jupyterize }} {{ block|jupyterize }}
</div> </div>
{% endif %} {% endif %}
......
# definice vlastnich tagu pro templates # definice vlastnich tagu pro templates
import os
import nbconvert
from django import template from django import template
from django.utils.safestring import mark_safe
from traitlets.config import Config
register = template.Library() register = template.Library()
@register.filter() @register.filter
def jupyterize(value): def jupyterize(value):
return value.value + " HIHI" """ Exportuje jupyterovsky notebook do raw HTML s vyuzitim vlastnich templates.
Vynechava tagy
"""
# lokalni soubor
filename = os.path.join(value.value.file.storage.location, value.value.file.name)
# konvertuj do HTML s pouzitim vlastni template
c = Config()
c.TagRemovePreprocessor.enabled = True # Nutne
c.TagRemovePreprocessor.remove_cell_tags = ["exclude"]
c.TagRemovePreprocessor.remove_input_tags = ["output"]
c.preprocessors = ["TagRemovePreprocessor"]
nb_body, _ = nbconvert.TemplateExporter(
config=c, template_file="uniweb/jupyter-templates/my.tpl"
).from_filename(filename)
# HACK: fucking ugly. Ale netusim kde se to tam bere, asi nekde v hloubi notebookovskych templates
if nb_body.startswith("None"):
nb_body = nb_body[4:]
return mark_safe(nb_body)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment