reveal.py
227 lines
| 8.6 KiB
| text/x-python
|
PythonLexer
/ converters / reveal.py
damianavila
|
r8899 | from __future__ import absolute_import | |
from converters.html import ConverterHTML | |||
damianavila
|
r8910 | from converters.utils import text_cell | |
from converters.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
|
r8921 | #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 = header_slide', | |||
u'slide_type = slide', | |||
u'slide_type = fragment', | |||
u'slide_type = skip'] | |||
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] | |||
else: | |||
text[i - 1] = self.delim[5] | |||
text[i] = text_cell_render | |||
text[0] = u'slide_type = header_slide' # defensive code | |||
text.append(u'slide_type = untouched') # to end search of skipped | |||
return text | |||
def build_slides(self): | |||
"build the slides structure from text list and delimiters" | |||
text = self.clean_text() | |||
left = '<section>' | |||
right = '</section>' | |||
set_delim = self.delim[:5] | |||
#elimination of skipped cells | |||
for i, j in enumerate(text): | |||
if j == u'slide_type = skip': | |||
text.pop(i) | |||
while not text[i] in set_delim: | |||
text.pop(i) | |||
# 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
|
r8899 | lambda x: x == u'slide_type = header_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) | |||
slide[i] = slide[i][:4] + ' class="fragment"' + slide[i][4:] | |||
# encapsulation of each nested slide | |||
if u'slide_type = slide' in slide: | |||
damianavila
|
r8860 | slide.insert(0, '<section>') | |
damianavila
|
r8848 | slide.append('</section>') | |
damianavila
|
r8860 | for i, j in enumerate(slide): | |
damianavila
|
r8899 | if j == u'slide_type = slide': | |
slide[i] = right + left | |||
damianavila
|
r8848 | return list(itertools.chain(*slides)) | |
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'), | |||
os.path.join(css, 'fbm.css'), | |||
os.path.join(css, 'notebook.css'), | |||
os.path.join(css, 'renderedhtml.css'), | |||
# 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
|
r8843 | def template_read(self): | |
"read the reveal_template.html" | |||
here = os.path.split(os.path.realpath(__file__))[0] | |||
damianavila
|
r8860 | reveal_template = os.path.join(here, '..', 'templates', | |
'reveal_base.html') | |||
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" | |||
temp = self.template_read() | |||
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() | |
damianavila
|
r8918 | return optional_footer_body[1] + ['</body>', '</html>'] |