Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#57: re-factor to make it easy to deploy and support other web technologies. #64

Closed
wants to merge 10 commits into from
1 change: 1 addition & 0 deletions d3py/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from displayable import SimpleServer, displayable
from pandas_figure import *
from networkx_figure import *
from geoms import *
Expand Down
68 changes: 68 additions & 0 deletions d3py/deployable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
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

@staticmethod
def default_deployable():
return FileSystem(dest_dir=os.path.join(os.getcwd(), 'deploy'))

class FileSystem(deployable):
"""
Concrete class which simply saves the files to the file system
"""
def __init__(self, dest_dir=None, logging=False):
self._fig = None
self._dest_dir = dest_dir

@property
def fig(self):
return self._fig

@fig.setter
def fig(self, fig):
self._fig = fig

@property
def dest_dir(self):
return self._dest_dir

@dest_dir.setter
def dest_dir(self, new_dir):
self._dest_dir = new_dir

def save(self, ephermeral_dir=None):
"""
Save the figure to dest_dir. If the user supplies a directory,
via the ephermeral_dir argument, use it for this call.
"""
if ephermeral_dir is not None:
self._dest_dir = ephermeral_dir
if self._dest_dir is None:
raise Exception("Destination directory not defined")
static_dir = os.path.join(self._dest_dir, "static")
if not os.path.exists(static_dir):
os.makedirs(static_dir)
# NB: while this method makes sure we can write the static directory to the file system, figure controls the structure of the subdirectory.
os.chdir(self._dest_dir)
for k_filename in self._fig.filemap:
f = os.path.join(self._dest_dir, 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)
157 changes: 157 additions & 0 deletions d3py/displayable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
from abc import ABCMeta
from exceptions import NotImplementedError

import logging

# in support of SimpleServer
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

@staticmethod
def default_displayable():
return SimpleServer()

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

self._fig = None
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

@property
def fig(self):
return self._fig

@fig.setter
def fig(self, fig):
self._fig = fig

@property
def host(self):
return self._host

@property
def port(self):
return self._port

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):
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:
if ex_value is None:
print "Cleanup after exception: %s"%(ex_type)
else:
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