Skip to content

Commit

Permalink
mikedewar#57: re-factor to make it easy to deploy and support other w…
Browse files Browse the repository at this point in the history
…eb technologies.

- the displayable module knows how to display figures
- the deployable module knows how to deploy figures
- favor os.sep of hardcoding / in path
- replaced string replace calls with jinja2 template module
  • Loading branch information
kern3020 committed Mar 26, 2013
1 parent 1593190 commit 4ffb4ed
Show file tree
Hide file tree
Showing 12 changed files with 296 additions and 172 deletions.
53 changes: 53 additions & 0 deletions d3py/deployable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from abc import ABCMeta
from exceptions import NotImplementedError

import logging
import os

class deployable():
"""
Given a d3py.figure, deployable stores it persistently. Concrete classes
may simply save the files to the file system or be used to deploy them
in the cloud.
"""

__metaclass__ = ABCMeta

def save(self, fig):
raise NotImplementedError

class FileSystem(deployable):
"""
Concrete class which simply saves the files to the file system
"""
def __init__(self, fig, dest_dir, host="localhost", port=8000, logging=False):
self.fig = fig
self.host = host
self.port = port
self.dest_dir = dest_dir

def save(self):
"""
Save the figure to dest_dir
"""
self.fig.update()
self.fig.save()
self.fig.renderHtml(self.host, self.port)

if self.dest_dir is None:
raise Exception("Destination directory not defined")
if not os.path.exists(self.dest_dir):
raise IOError("Destination directory, {d} , does not exist.".format(d=self.dest_dir))
os.chdir(self.dest_dir)
static_dir = self.dest_dir + os.sep + "static"
if not os.path.exists(static_dir):
os.mkdir(static_dir)
for k_filename in self.fig.filemap:
f = self.dest_dir + os.sep + k_filename
with open(f, 'w',0644) as fd_out:
fd_in = self.fig.filemap[k_filename]["fd"]
fd_in.seek(0)
# import pdb; pdb.set_trace()
for line in fd_in.readlines():
fd_out.write(line)
fd_out.close()
137 changes: 137 additions & 0 deletions d3py/displayable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
from abc import ABCMeta
from exceptions import NotImplementedError

import logging

# in support of SimplyServer
import threading
import webbrowser
from HTTPHandler import CustomHTTPRequestHandler, ThreadedHTTPServer

# requires IPython 0.11.0 or higher
import IPython.core.display

class displayable():
"""
Given a d3py.figure, displayables present the graph to the user.
The first displayable is based on python's SimpleHTTPServer class.
These classes should know nothing of html, css or javascript which
live in the figure class.
"""
__metaclass__ = ABCMeta

def show(self, fig):
raise NotImplementedError

class SimplyServer(displayable):
"""
Use Python's SimpleHTTPServer class to present this resulting d3 output
to the user.
"""
def __init__(self, fig, host="localhost", port=8000,
interactive=False, logging=False):

self.fig = fig
self.host = host
self.port = port
self._server_thread = None
self.httpd = None
# interactive is True by default as this is designed to be a command line tool
# we do not want to block interaction after plotting.
self.interactive = interactive

def ion(self):
"""
Turns interactive mode on ala pylab
"""
self.interactive = True

def ioff(self):
"""
Turns interactive mode off
"""
self.interactive = False

def show(self, interactive=None):
self.fig.update()
self.fig.save()
self.fig.renderHtml(self.host, self.port)
if interactive is not None:
blocking = not interactive
else:
blocking = not self.interactive

if blocking:
self._serve(blocking=True)
else:
# if not blocking, we serve the
self._serve(blocking=False)
# fire up a browser
webbrowser.open_new_tab("http://%s:%s/%s.html"%(self.host,self.port, self.name))

def _serve(self, blocking=True):
"""
start up a server to serve the files for this vis.
"""
msgparams = (self.host, self.port, self.fig.name)
url = "http://%s:%s/%s.html"%msgparams
if self._server_thread is None or self._server_thread.active_count() == 0:
Handler = CustomHTTPRequestHandler
Handler.filemap = self.fig.filemap
Handler.logging = self.fig.logging
try:
self.httpd = ThreadedHTTPServer(("", self.port), Handler)
except Exception, e:
print "Exception %s"%e
return False
if blocking:
logging.info('serving forever on port: %s'%msgparams[1])
msg = "You can find your chart at " + url
print msg
print "Ctrl-C to stop serving the chart and quit!"
self._server_thread = None
self.httpd.serve_forever()
else:
logging.info('serving asynchronously on port %s'%msgparams[1])
self._server_thread = threading.Thread(
target=self.httpd.serve_forever
)
self._server_thread.daemon = True
self._server_thread.start()
msg = "You can find your chart at " + url
print msg


def __enter__(self):
self.interactive = False
return self

def __exit__(self, ex_type, ex_value, ex_tb):
if ex_tb is not None:
print "Cleanup after exception: %s: %s"%(ex_type, ex_value)
self._cleanup()

def __del__(self):
self._cleanup()

def _cleanup(self):
try:
if self.httpd is not None:
print "Shutting down httpd"
self.httpd.shutdown()
self.httpd.server_close()
except Exception, e:
print "Error in clean-up: %s"%e

class IPython(displayable):
"""
IPython integration
"""
def __init__(self):
pass

def show(self, fig):
html = "<iframe src=http://%s:%s/%s.html width=%s height=%s>" % (
self.host, self.port, self.name, fig.width, fig.height)
IPython.core.display.HTML(html)
Loading

0 comments on commit 4ffb4ed

Please sign in to comment.