reveal.py
248 lines
| 9.3 KiB
| text/x-python
|
PythonLexer
/ converters / reveal.py
damianavila
|
r8899 | from __future__ import absolute_import | ||
Matthias BUSSONNIER
|
r9821 | from .html import ConverterHTML | ||
from .utils import text_cell | ||||
from .utils import highlight, coalesce_streams | ||||
damianavila
|
r8918 | |||
from IPython.utils import path | ||||
damianavila
|
r8899 | from markdown import markdown | ||
damianavila
|
r8910 | |||
damianavila
|
r8815 | import os | ||
damianavila
|
r8899 | import io | ||
damianavila
|
r8843 | import itertools | ||
damianavila
|
r8815 | |||
damianavila
|
r8848 | |||
damianavila
|
r8899 | class ConverterReveal(ConverterHTML): | ||
damianavila
|
r9383 | """ | ||
Convert a ipython notebook to a html slideshow | ||||
based in reveal.js library. | ||||
""" | ||||
damianavila
|
r8899 | |||
@text_cell | ||||
damianavila
|
r8848 | def render_heading(self, cell): | ||
damianavila
|
r8899 | marker = cell.level | ||
damianavila
|
r8860 | return [self.meta2str(cell.metadata), | ||
damianavila
|
r8899 | u'<h{1}>\n {0}\n</h{1}>'.format(cell.source, marker)] | ||
damianavila
|
r8848 | |||
damianavila
|
r8871 | def render_code(self, cell): | ||
if not cell.input: | ||||
return [] | ||||
lines = [] | ||||
meta_code = self.meta2str(cell.metadata) | ||||
lines.extend([meta_code]) | ||||
damianavila
|
r8899 | lines.extend(['<div class="cell border-box-sizing code_cell vbox">']) | ||
lines.append('<div class="input hbox">') | ||||
damianavila
|
r8871 | n = self._get_prompt_number(cell) | ||
damianavila
|
r8899 | lines.append( | ||
'<div class="prompt input_prompt">In [%s]:</div>' % n | ||||
) | ||||
lines.append('<div class="input_area box-flex1">') | ||||
lines.append(highlight(cell.input)) | ||||
lines.append('</div>') # input_area | ||||
lines.append('</div>') # input | ||||
if cell.outputs: | ||||
lines.append('<div class="vbox output_wrapper">') | ||||
lines.append('<div class="output vbox">') | ||||
for output in coalesce_streams(cell.outputs): | ||||
conv_fn = self.dispatch(output.output_type) | ||||
lines.extend(conv_fn(output)) | ||||
lines.append('</div>') # output | ||||
lines.append('</div>') # output_wrapper | ||||
lines.append('</div>') # cell | ||||
damianavila
|
r8871 | return lines | ||
damianavila
|
r8899 | @text_cell | ||
damianavila
|
r8848 | def render_markdown(self, cell): | ||
damianavila
|
r8899 | return [self.meta2str(cell.metadata), markdown(cell.source)] | ||
damianavila
|
r8848 | |||
def render_raw(self, cell): | ||||
if self.raw_as_verbatim: | ||||
damianavila
|
r8899 | return [self.in_tag('pre', self.meta2str(cell.metadata)), | ||
damianavila
|
r8910 | self.in_tag('pre', cell.source)] | ||
damianavila
|
r8848 | else: | ||
damianavila
|
r8899 | return [self.meta2str(cell.metadata), cell.source] | ||
def meta2str(self, meta): | ||||
"transform metadata dict (containing slides delimiters) to string " | ||||
try: | ||||
meta_tuple = meta[u'slideshow'].items() | ||||
except KeyError as e: # if there is not slideshow metadata | ||||
meta_tuple = [(u'slide_type', u'untouched')] | ||||
meta_list = [[x + ' = ' + unicode(y)] for x, y in meta_tuple] | ||||
return u'\n'.join(list(itertools.chain(*meta_list))) | ||||
damianavila
|
r8873 | |||
damianavila
|
r8815 | def convert(self, cell_separator='\n'): | ||
""" | ||||
damianavila
|
r8863 | Specific method to converts notebook to a string representation. | ||
damianavila
|
r8815 | |||
Parameters | ||||
---------- | ||||
cell_separator : string | ||||
Character or string to join cells with. Default is "\n" | ||||
Returns | ||||
------- | ||||
out : string | ||||
""" | ||||
damianavila
|
r8863 | |||
damianavila
|
r8815 | lines = [] | ||
lines.extend(self.optional_header()) | ||||
damianavila
|
r8848 | begin = ['<div class="reveal"><div class="slides">'] | ||
lines.extend(begin) | ||||
damianavila
|
r8899 | slides_list = self.build_slides() | ||
damianavila
|
r8848 | lines.extend(slides_list) | ||
end = ['</div></div>'] | ||||
damianavila
|
r8843 | lines.extend(end) | ||
damianavila
|
r8815 | lines.extend(self.optional_footer()) | ||
return u'\n'.join(lines) | ||||
damianavila
|
r8899 | def clean_text(self, cell_separator='\n'): | ||
"clean and reorganize the text list to be slided" | ||||
damianavila
|
r8848 | text = self.main_body(cell_separator) | ||
damianavila
|
r8899 | self.delim = [u'slide_type = untouched', | ||
u'slide_type = -', | ||||
u'slide_type = slide', | ||||
damianavila
|
r9104 | u'slide_type = subslide', | ||
damianavila
|
r8899 | u'slide_type = fragment', | ||
damianavila
|
r8927 | u'slide_type = notes', | ||
damianavila
|
r8924 | u'slide_type = skip'] # keep this one the last | ||
damianavila
|
r8899 | text_cell_render = \ | ||
u'<div class="text_cell_render border-box-sizing rendered_html">' | ||||
for i, j in enumerate(text): | ||||
if j in self.delim and text[i - 1] == text_cell_render: | ||||
if j == self.delim[0]: | ||||
text[i - 1] = self.delim[0] | ||||
elif j == self.delim[1]: | ||||
text[i - 1] = self.delim[1] | ||||
elif j == self.delim[2]: | ||||
text[i - 1] = self.delim[2] | ||||
elif j == self.delim[3]: | ||||
text[i - 1] = self.delim[3] | ||||
elif j == self.delim[4]: | ||||
text[i - 1] = self.delim[4] | ||||
damianavila
|
r8927 | elif j == self.delim[5]: | ||
damianavila
|
r8899 | text[i - 1] = self.delim[5] | ||
damianavila
|
r8927 | else: | ||
text[i - 1] = self.delim[6] | ||||
damianavila
|
r8899 | text[i] = text_cell_render | ||
return text | ||||
def build_slides(self): | ||||
"build the slides structure from text list and delimiters" | ||||
text = self.clean_text() | ||||
left = '<section>' | ||||
right = '</section>' | ||||
damianavila
|
r8927 | notes_start = '<aside class="notes">' | ||
notes_end = '</aside>' | ||||
damianavila
|
r9232 | #encapsulation of skipped cells | ||
damianavila
|
r8899 | for i, j in enumerate(text): | ||
if j == u'slide_type = skip': | ||||
text.pop(i) | ||||
damianavila
|
r9232 | text[i] = text[i][:4] + \ | ||
' style=display:none' + text[i][4:] | ||||
damianavila
|
r8936 | #encapsulation of notes cells | ||
for i, j in enumerate(text): | ||||
if j == u'slide_type = notes': | ||||
text.pop(i) | ||||
temp_list = [] | ||||
damianavila
|
r9232 | while not text[i] in self.delim[:6]: | ||
damianavila
|
r8936 | temp_list.append(text.pop(i)) | ||
else: | ||||
temp_list.insert(0, notes_start) | ||||
temp_list.append(notes_end) | ||||
text[i:i] = temp_list | ||||
damianavila
|
r8899 | # elimination of none names | ||
for i, j in enumerate(text): | ||||
if j in [u'slide_type = untouched', u'slide_type = -']: | ||||
text.pop(i) | ||||
#generation of slides as a list of list | ||||
damianavila
|
r8860 | slides = [list(x[1]) for x in itertools.groupby(text, | ||
damianavila
|
r9104 | lambda x: x == u'slide_type = slide') if not x[0]] | ||
damianavila
|
r8848 | for slide in slides: | ||
damianavila
|
r8860 | slide.insert(0, left) | ||
damianavila
|
r8848 | slide.append(right) | ||
damianavila
|
r8899 | # encapsulation of each fragment | ||
for i, j in enumerate(slide): | ||||
if j == u'slide_type = fragment': | ||||
slide.pop(i) | ||||
damianavila
|
r8927 | slide[i] = slide[i][:4] + \ | ||
' class="fragment"' + slide[i][4:] | ||||
damianavila
|
r8899 | # encapsulation of each nested slide | ||
damianavila
|
r9104 | if u'slide_type = subslide' in slide: | ||
damianavila
|
r9096 | slide.insert(0, left) | ||
slide.append(right) | ||||
damianavila
|
r8860 | for i, j in enumerate(slide): | ||
damianavila
|
r9104 | if j == u'slide_type = subslide': | ||
damianavila
|
r8899 | slide[i] = right + left | ||
damianavila
|
r8848 | return list(itertools.chain(*slides)) | ||
damianavila
|
r8937 | def render(self): | ||
"read, convert, and save self.infile" | ||||
if not hasattr(self, 'nb'): | ||||
self.read() | ||||
self.output = self.convert() | ||||
assert(type(self.output) == unicode) | ||||
return self.save() | ||||
damianavila
|
r8815 | def save(self, outfile=None, encoding=None): | ||
"read and parse notebook into self.nb" | ||||
if outfile is None: | ||||
outfile = self.outbase + '_slides.' + 'html' | ||||
if encoding is None: | ||||
encoding = self.default_encoding | ||||
with io.open(outfile, 'w', encoding=encoding) as f: | ||||
f.write(self.output) | ||||
return os.path.abspath(outfile) | ||||
damianavila
|
r8918 | def header_body(self): | ||
damianavila
|
r8921 | "return the body of the header as a list of strings" | ||
damianavila
|
r8918 | from pygments.formatters import HtmlFormatter | ||
header = [] | ||||
static = os.path.join(path.get_ipython_package_dir(), | ||||
damianavila
|
r8921 | 'frontend', 'html', 'notebook', 'static',) | ||
damianavila
|
r8918 | here = os.path.split(os.path.realpath(__file__))[0] | ||
css = os.path.join(static, 'css') | ||||
for sheet in [ | ||||
# do we need jquery and prettify? | ||||
# os.path.join(static, 'jquery', 'css', 'themes', 'base', | ||||
# 'jquery-ui.min.css'), | ||||
# os.path.join(static, 'prettify', 'prettify.css'), | ||||
os.path.join(css, 'boilerplate.css'), | ||||
damianavila
|
r9561 | os.path.join(css, 'style.min.css'), | ||
damianavila
|
r8918 | # our overrides: | ||
os.path.join(here, '..', 'css', 'reveal_html.css'), | ||||
]: | ||||
header.extend(self._stylesheet(sheet)) | ||||
# pygments css | ||||
pygments_css = HtmlFormatter().get_style_defs('.highlight') | ||||
header.extend(['<meta charset="UTF-8">']) | ||||
header.extend(self.in_tag('style', pygments_css, | ||||
dict(type='"text/css"'))) | ||||
return header | ||||
damianavila
|
r8937 | def template_read(self, templ): | ||
damianavila
|
r8843 | "read the reveal_template.html" | ||
here = os.path.split(os.path.realpath(__file__))[0] | ||||
damianavila
|
r8860 | reveal_template = os.path.join(here, '..', 'templates', | ||
damianavila
|
r8937 | templ) | ||
damianavila
|
r8843 | with io.open(reveal_template, 'r', encoding='utf-8') as f: | ||
template = f.readlines() | ||||
damianavila
|
r8863 | template = [s.strip() for s in template] | ||
damianavila
|
r8843 | return template | ||
def template_split(self): | ||||
"split the reveal_template.html in header and footer lists" | ||||
damianavila
|
r8937 | temp = self.template_read('reveal_base.html') | ||
damianavila
|
r8860 | splitted_temp = [list(x[1]) for x in itertools.groupby(temp, | ||
lambda x: x == u'%slides%') if not x[0]] | ||||
return splitted_temp | ||||
damianavila
|
r8815 | |||
damianavila
|
r8843 | def optional_header(self): | ||
optional_header_body = self.template_split() | ||||
damianavila
|
r8918 | return ['<!DOCTYPE html>', '<html>', '<head>'] + \ | ||
optional_header_body[0] + self.header_body() + \ | ||||
['</head>', '<body>'] | ||||
damianavila
|
r8815 | |||
def optional_footer(self): | ||||
damianavila
|
r8843 | optional_footer_body = self.template_split() | ||
Matthias BUSSONNIER
|
r9821 | return optional_footer_body[1] + ['</body>', '</html>'] | ||