Snippets

Adam Labadorf jupyter reveal.js presentation tool

Created by Adam Labadorf last modified Adam Labadorf
import base64
from IPython.display import HTML, Markdown, display
import functools
import io
import json
from matplotlib.figure import Figure
import mimetypes
import os
from subprocess import Popen, PIPE
import urllib.request as ur


class Tag(object):
    def __init__(self, tag, *elems, bare=False, **attrs):

        self.tag = tag
        self.content = elems

        # if bare then only return the rendered content and don't wrap in a tag
        self.bare = bare
        if tag is not None and bare :
          raise Exception('tag must be None and attrs is ignored when bare is True')

        # special cases, e.g. can't use class as a kwarg
        if 'cl' in attrs:
            attrs['class'] = attrs['cl']
            del attrs['cl']
        self.attrs = attrs

    def render(self):
        inner = []
        for elem in self.content:
            if isinstance(elem, Tag):
                inner.append(elem.render())
            elif isinstance(elem, list) or isinstance(elem, tuple):
                inner.append(bare(*elem).render())
            else: # attempt to format whatever the argument is
                try :
                  inner.append('{}'.format(elem))
                except Exception as e:
                  raise Exception('invalid content element: {} (inner exception: {})', elem,e.msg)

        inner = ' '.join(inner)
        if self.bare :
          tag_str = inner
        else :
          tag_str = '<{tag}{attrs}>{content}</{tag}>'.format(
              tag=self.tag, attrs=''.join(' {}="{}"'.format(*_) for _ in self.attrs.items()), content=inner)
        return tag_str

def bytes_to_base64(b) :
    b64 = base64.b64encode(b).decode('utf8')
    return b64

def tag(tag, **kwargs):
    return functools.partial(Tag, tag, **kwargs)


# for combining elements together without a wrapping tag
bare = tag(None,bare=True)

# structural elements
section = tag('section')
h1 = tag('h1')
h2 = tag('h2')
h3 = tag('h3')
h4 = tag('h4')
ul = tag('ul')
ol = tag('ol')
li = tag('li')
lif = tag('li', cl='fragment')
iframe = tag('iframe')
iframef = tag('iframe', cl='fragment')

# text block and formatting tags
span = tag('span')
spanf = tag('span', cl='fragment')
it = tag('span',style='font-style: oblique')
itf = tag('span',style='font-style: oblique', cl='fragment')
bold = tag('span',style='font-weight: bold')
boldf = tag('span',style='font-weight: bold', cl='fragment')
p = tag('p')
pf = tag('p', cl='fragment')

# table tags
table = tag('table')
tablef = tag('table',cl='fragment')
tr = tag('tr')
th = tag('th')
def tagwrap(inner_f,outer_f=bare) :
  def wrapped(*elems,**attrs) :
    return outer_f(*[inner_f(_,**attrs) for _ in elems])
  return wrapped
ths = tagwrap(th,tr)
td = tag('td')
tds = tagwrap(td,tr)

def img(src,mimetype='png',**attrs) :

  if isinstance(src, str) :
    if src.startswith('http') :
      attrs.setdefault('alt',src)
      mimetype = mimetypes.guess_type(src)[0]
      src = ur.urlopen(src).read()
    elif os.path.exists(src) : # assume this is a file, read in as bytes
      mimetype = mimetypes.guess_type(src)[0]
      src = open(src,'rb').read()

  if isinstance(src, Figure):  # render as base64 encoded image
      from io import BytesIO
      figfile = BytesIO()
      mimetype = 'png'
      src.savefig(figfile, format=mimetype)
      figfile.seek(0)  # rewind to beginning of file
      import base64
      src = figfile

  attrs.setdefault('src','data:{mimetype};base64,{img}'.format(
    mimetype=mimetype
    ,img=bytes_to_base64(src))
  )
  return Tag('img',**attrs)

class Reveal(object):
    tmpl = '''<!doctype html>
    <html>
            <head>
                    <meta charset="utf-8">
                    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

                    <title>{name}</title>

                    <link rel="stylesheet" href="css/reveal.css">
                    <link rel="stylesheet" href="css/theme/black.css">

                    <!-- Theme used for syntax highlighting of code -->
                    <link rel="stylesheet" href="lib/css/zenburn.css">

                    <!-- Printing and PDF exports -->
                    <script>
                            var link = document.createElement( 'link' );
                            link.rel = 'stylesheet';
                            link.type = 'text/css';
                            link.href = window.location.search.match( /print-pdf/gi ) ? 'css/print/pdf.css' : 'css/print/paper.css';
                            document.getElementsByTagName( 'head' )[0].appendChild( link );
                    </script>
            </head>
            <body>
                    <div class="reveal">
                            <div class="slides">
                                   {content}
                            </div>
                    </div>

                    <script src="lib/js/head.min.js"></script>
                    <script src="js/reveal.js"></script>

                    <script>
                            // More info https://github.com/hakimel/reveal.js#configuration
                            Reveal.initialize({{
                                    {reveal_args},

                                    // More info https://github.com/hakimel/reveal.js#dependencies
                                    dependencies: [
                                            {{ src: 'plugin/markdown/marked.js' }},
                                            {{ src: 'plugin/markdown/markdown.js' }},
                                            {{ src: 'plugin/zoom-js/zoom.js', async: true }},
                                            {{ src: 'plugin/notes/notes.js', async: true }},
                                            {{ src: 'plugin/highlight/highlight.js', async: true, callback: function() {{ hljs.initHighlightingOnLoad(); }} }}
                                    ]
                            }});
                    </script>
            </body>
    </html>'''

    def __init__(self, name, *content, debug=False, **kwargs):
        self.content = content
        # terrible, but whatevs
        src = 'https://github.com/hakimel/reveal.js/archive/3.4.1.tar.gz'
        panoct_fn = 'reveal.js-{}'.format(src.split('/')[-1])
        if not os.path.exists(panoct_fn) :
          stdout, stderr = Popen('wget {} -O {}'.format(src,panoct_fn),
                shell=True,stdout=PIPE,stderr=PIPE).communicate()
          debug and print(stderr)

          stdout, stderr = Popen('tar xzf {}'.format(panoct_fn),
            shell=True,stdout=PIPE,stderr=PIPE).communicate()
          debug and print(stderr)

          os.rename(panoct_fn.replace('.tar.gz',''),name)

        self.baseurl = name
        self.fn = '{name}/{name}.html'.format(name=name)

        # kwargs are passed to the Reveal.initialize() JS function
        # set some defaults
        kwargs.setdefault('history','true')
        kwargs.setdefault('slideNumber','c/t')

        self.reveal_args = kwargs

    def render(self):
        reveal_args = ['{}: {}'.format(k,json.dumps(v)) for k,v in self.reveal_args.items()]
        reveal_args = ','.join(reveal_args)
        return Reveal.tmpl.format(
            content=''.join(_.render() for _ in self.content)
            ,name=self.baseurl
            ,reveal_args=reveal_args
        )

    def save(self):
        with open(self.fn, 'wt') as fp:
            fp.write(self.render())

    def link(self):
        display(Markdown('[{fn}]({fn})'.format(
            baseurl=self.baseurl, fn=self.fn)))

    def iframe(self,**attrs):
        attrs.setdefault('src',self.fn)
        attrs.setdefault('width','100%')
        display(HTML(iframe(**attrs).render()))

    publish_modes = ('SSH',)
    def publish(self,dest,mode='SSH') :
        if mode not in publish_modes :
            raise Exception('mode must be one of {}'.format(publish_modes))

        if mode == 'SSH' :
            ssh_p = Popen('scp -r {} {}'.format(self.baseurl,dest)
                    ,stdout=PIPE
                    ,stderr=PIPE
                    ,shell=True
            )
            stdout, stderr = ssh_p.communicate()
            debug and print(stdout)
            debug and print(stderr)

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.