|
|
from converters.markdown import ConverterMarkdown
|
|
|
from IPython.utils.text import indent, dedent
|
|
|
from converters.utils import highlight, remove_ansi
|
|
|
import io
|
|
|
import os
|
|
|
import itertools
|
|
|
|
|
|
class ConverterReveal(ConverterMarkdown):
|
|
|
"""
|
|
|
Convert a notebook to a html slideshow.
|
|
|
|
|
|
It generates a static html slideshow based in markdown and reveal.js.
|
|
|
The delimiters for each slide, subslide, and fragment are retrieved
|
|
|
from the 'slideshow' metadata.
|
|
|
"""
|
|
|
|
|
|
def __init__(self, infile, highlight_source=False, show_prompts=True,
|
|
|
inline_prompt=True):
|
|
|
super(ConverterReveal, self).__init__(infile)
|
|
|
self.highlight_source = highlight_source
|
|
|
self.show_prompts = show_prompts
|
|
|
self.inline_prompt = inline_prompt
|
|
|
|
|
|
def switch_meta(self, m_list):
|
|
|
"sort order m_list to [new_section, new_subsection, new_fragment]"
|
|
|
if len(m_list) > 1:
|
|
|
# do not sort when m_list = [new_subsection, new_fragment]
|
|
|
if not (len(m_list) == 2 and m_list[1] == [u'new_fragment = True']):
|
|
|
m_list[0], m_list[1] = m_list[1], m_list[0]
|
|
|
return m_list
|
|
|
|
|
|
def meta2str(self, meta):
|
|
|
"transform metadata dict (containing slides delimiters) to string "
|
|
|
try:
|
|
|
meta_tuple = meta[u'slideshow'].items()
|
|
|
except KeyError as e:
|
|
|
meta_tuple = () # if there is not slideshow metadata
|
|
|
meta_list = [[x + ' = ' + unicode(y)] for x, y in meta_tuple]
|
|
|
meta_list = self.switch_meta(meta_list)
|
|
|
return u'\n'.join(list(itertools.chain(*meta_list)))
|
|
|
|
|
|
def render_heading(self, cell):
|
|
|
return [self.meta2str(cell.metadata),
|
|
|
'{0} {1}'.format('#' * cell.level, cell.source), '']
|
|
|
|
|
|
def render_code(self, cell):
|
|
|
if not cell.input:
|
|
|
return []
|
|
|
lines = []
|
|
|
meta_code = self.meta2str(cell.metadata)
|
|
|
lines.extend([meta_code])
|
|
|
lines.extend(['<!-- hack -->']) # to be proper parsed
|
|
|
n = self._get_prompt_number(cell)
|
|
|
if self.show_prompts and not self.inline_prompt:
|
|
|
lines.extend(['*In[%s]:*' % n, ''])
|
|
|
if self.show_prompts and self.inline_prompt:
|
|
|
prompt = 'In[%s]: ' % n
|
|
|
input_lines = cell.input.split('\n')
|
|
|
src = (prompt + input_lines[0] + '\n' +
|
|
|
indent('\n'.join(input_lines[1:]), nspaces=len(prompt)))
|
|
|
else:
|
|
|
src = cell.input
|
|
|
src = highlight(src) if self.highlight_source else indent(src)
|
|
|
lines.extend([src, ''])
|
|
|
if cell.outputs and self.show_prompts and not self.inline_prompt:
|
|
|
lines.extend(['*Out[%s]:*' % n, ''])
|
|
|
for output in cell.outputs:
|
|
|
conv_fn = self.dispatch(output.output_type)
|
|
|
lines.extend(conv_fn(output))
|
|
|
#lines.append('')
|
|
|
return lines
|
|
|
|
|
|
def render_markdown(self, cell):
|
|
|
return [self.meta2str(cell.metadata), cell.source, '']
|
|
|
|
|
|
def render_raw(self, cell):
|
|
|
if self.raw_as_verbatim:
|
|
|
return [indent(self.meta2str(cell.metadata)),
|
|
|
indent(cell.source), '']
|
|
|
else:
|
|
|
return [self.meta2str(cell.metadata), cell.source, '']
|
|
|
|
|
|
def render_pyout(self, output):
|
|
|
for fmt in ['html', 'latex', 'png', 'jpeg', 'svg', 'text']:
|
|
|
if fmt in output:
|
|
|
conv_fn = self.dispatch_display_format(fmt)
|
|
|
return conv_fn(output)
|
|
|
return []
|
|
|
|
|
|
def render_pyerr(self, output):
|
|
|
# Note: a traceback is a *list* of frames.
|
|
|
return [indent(remove_ansi('\n'.join(output.traceback))), '']
|
|
|
|
|
|
def _img_lines(self, img_file):
|
|
|
return ['![](%s)' % img_file, '']
|
|
|
|
|
|
def render_display_format_png(self, output):
|
|
|
return ['<img src="data:image/png;base64,%s"></img>' % output.png, '']
|
|
|
|
|
|
def render_display_format_svg(self, output):
|
|
|
return [output.svg, '']
|
|
|
|
|
|
def render_display_format_jpeg(self, output):
|
|
|
return ['<img src="data:image/jpeg;base64,%s"></img>' % output.jpeg, '']
|
|
|
|
|
|
def render_display_format_text(self, output):
|
|
|
return [indent(output.text), '']
|
|
|
|
|
|
def _unknown_lines(self, data):
|
|
|
return ['Warning: Unknown cell', data, '']
|
|
|
|
|
|
def render_display_format_html(self, output):
|
|
|
return [dedent(output.html), '']
|
|
|
|
|
|
def render_display_format_latex(self, output):
|
|
|
return ['LaTeX::', indent(output.latex), '']
|
|
|
|
|
|
def render_display_format_json(self, output):
|
|
|
return ['JSON:', indent(output.json), '']
|
|
|
|
|
|
def render_display_format_javascript(self, output):
|
|
|
return ['JavaScript:', indent(output.javascript), '']
|
|
|
|
|
|
def convert(self, cell_separator='\n'):
|
|
|
"""
|
|
|
Specific method to converts notebook to a string representation.
|
|
|
|
|
|
Parameters
|
|
|
----------
|
|
|
cell_separator : string
|
|
|
Character or string to join cells with. Default is "\n"
|
|
|
|
|
|
Returns
|
|
|
-------
|
|
|
out : string
|
|
|
"""
|
|
|
|
|
|
lines = []
|
|
|
lines.extend(self.optional_header())
|
|
|
begin = ['<div class="reveal"><div class="slides">']
|
|
|
lines.extend(begin)
|
|
|
slides_list = self.build_slides(cell_separator)
|
|
|
lines.extend(slides_list)
|
|
|
end = ['</div></div>']
|
|
|
lines.extend(end)
|
|
|
lines.extend(self.optional_footer())
|
|
|
return u'\n'.join(lines)
|
|
|
|
|
|
def build_slides(self, cell_separator='\n'):
|
|
|
"build the slides structure from text list and delimiters"
|
|
|
text = self.main_body(cell_separator)
|
|
|
delim_false = [u'new_section = False',
|
|
|
u'new_subsection = False',
|
|
|
u'new_fragment = False']
|
|
|
text = [x for x in text if not x in delim_false]
|
|
|
left = '<section data-markdown><script type="text/template">'
|
|
|
right = '</script></section>'
|
|
|
# build lists representing each slide delimited by new_section
|
|
|
slides = [list(x[1]) for x in itertools.groupby(text,
|
|
|
lambda x: x == u'new_section = True') if not x[0]]
|
|
|
for slide in slides:
|
|
|
slide.insert(0, u'') # for proper interline in html file
|
|
|
slide.insert(0, left)
|
|
|
slide.append(right)
|
|
|
# build each vertical slide delimited by new_subsection
|
|
|
if slide[2] == u'new_subsection = True':
|
|
|
slide.pop(2)
|
|
|
slide.insert(0, '<section>')
|
|
|
slide.append('</section>')
|
|
|
for i, j in enumerate(slide):
|
|
|
if j == u'new_subsection = True':
|
|
|
slide[i] = right + left
|
|
|
slide.insert(i + 1, u'') # for proper interline
|
|
|
#defensive: if user do not begin new_subsection with a new_section
|
|
|
elif slide[4] == u'new_subsection = True':
|
|
|
slide[4] = right
|
|
|
slide.insert(5, u'') # for proper interline
|
|
|
slide.insert(5, left)
|
|
|
slide.insert(5, '<section>')
|
|
|
slide.append('</section>')
|
|
|
for i, j in enumerate(slide):
|
|
|
if j == u'new_subsection = True':
|
|
|
slide[i] = right + left
|
|
|
slide.insert(i + 1, u'') # for proper interline
|
|
|
# build each fragment delimited by new_fragment
|
|
|
for i, j in enumerate(slide):
|
|
|
if j == u'new_fragment = True':
|
|
|
slide[i] = '<p class="fragment">'
|
|
|
slide[i + 2] = '</p>'
|
|
|
slide.insert(i + 3, u'') # for proper interline
|
|
|
return list(itertools.chain(*slides))
|
|
|
|
|
|
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)
|
|
|
|
|
|
def template_read(self):
|
|
|
"read the reveal_template.html"
|
|
|
here = os.path.split(os.path.realpath(__file__))[0]
|
|
|
reveal_template = os.path.join(here, '..', 'templates',
|
|
|
'reveal_base.html')
|
|
|
with io.open(reveal_template, 'r', encoding='utf-8') as f:
|
|
|
template = f.readlines()
|
|
|
template = [s.strip() for s in template]
|
|
|
return template
|
|
|
|
|
|
def template_split(self):
|
|
|
"split the reveal_template.html in header and footer lists"
|
|
|
temp = self.template_read()
|
|
|
splitted_temp = [list(x[1]) for x in itertools.groupby(temp,
|
|
|
lambda x: x == u'%slides%') if not x[0]]
|
|
|
return splitted_temp
|
|
|
|
|
|
def optional_header(self):
|
|
|
optional_header_body = self.template_split()
|
|
|
return optional_header_body[0]
|
|
|
|
|
|
def optional_footer(self):
|
|
|
optional_footer_body = self.template_split()
|
|
|
return optional_footer_body[1]
|
|
|
|
|
|
|