Skip to content

Commit 04e9e9f

Browse files
author
Mark Gibbs
committed
Initial files
1 parent 8b2dc48 commit 04e9e9f

File tree

4 files changed

+189
-0
lines changed

4 files changed

+189
-0
lines changed

django_plotly_dash/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#
2+
3+
__version__ = "0.0.1"
4+
5+
from .dash_wrapper import DelayedDash
6+

django_plotly_dash/dash_wrapper.py

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
from dash import Dash
2+
from flask import Flask
3+
4+
from django.urls import reverse
5+
6+
uid_counter = 0
7+
8+
usable_apps = {}
9+
app_instances = {}
10+
nd_apps = {}
11+
12+
def get_app_by_name(name):
13+
return usable_apps.get(name,None)
14+
15+
def get_app_instance_by_id(id):
16+
return nd_apps.get(id,None)
17+
18+
class DelayedDash:
19+
def __init__(self, name=None, **kwargs):
20+
if name is None:
21+
global uid_counter
22+
uid_counter += 1
23+
self._uid = "djdash_%i" % uid_counter
24+
else:
25+
self._uid = name
26+
self.layout = None
27+
self._rep_dash = None
28+
self._callback_sets = []
29+
30+
global usable_apps
31+
usable_apps[self._uid] = self
32+
33+
def _RepDash(self):
34+
if self._rep_dash is None:
35+
self._rep_dash = self._form_repdash()
36+
return self._rep_dash
37+
38+
def _form_repdash(self):
39+
rd = NotDash(name_root=self._uid,
40+
app_pathname="dexy:main")
41+
rd.layout = self.layout
42+
for cb, func in self._callback_sets:
43+
rd.callback(**cb)(func)
44+
return rd
45+
46+
def base_url(self):
47+
return self._RepDash().base_url()
48+
49+
def callback(self, output, inputs=[], state=[], events=[]):
50+
callback_set = {'output':output,
51+
'inputs':inputs,
52+
'state':state,
53+
'events':events}
54+
def wrap_func(func,callback_set=callback_set,callback_sets=self._callback_sets):
55+
callback_sets.append((callback_set,func))
56+
return func
57+
return wrap_func
58+
59+
class NotFlask:
60+
def __init__(self):
61+
self.config = {}
62+
self.endpoints = {}
63+
64+
def after_request(self,*args,**kwargs):
65+
pass
66+
def errorhandler(self,*args,**kwargs):
67+
return args[0]
68+
def add_url_rule(self,*args,**kwargs):
69+
route = kwargs['endpoint']
70+
self.endpoints[route] = kwargs
71+
def before_first_request(self,*args,**kwargs):
72+
pass
73+
def run(self,*args,**kwargs):
74+
pass
75+
76+
class NotDash(Dash):
77+
def __init__(self, name_root, app_pathname, **kwargs):
78+
79+
global app_instances
80+
current_instances = app_instances.get(name_root,None)
81+
82+
if current_instances is not None:
83+
self._uid = "%s-%i" % (name_root,len(current_instances)+1)
84+
current_instances.append(self)
85+
else:
86+
self._uid = name_root
87+
app_instances[name_root] = [self,]
88+
89+
self._flask_app = Flask(self._uid)
90+
self._notflask = NotFlask()
91+
self._base_pathname = reverse(app_pathname,kwargs={'id':self._uid})
92+
93+
kwargs['url_base_pathname'] = self._base_pathname
94+
kwargs['server'] = self._notflask
95+
super(NotDash, self).__init__(**kwargs)
96+
global nd_apps
97+
nd_apps[self._uid] = self
98+
if False: # True for some debug info and a load of errors...
99+
self.css.config.serve_locally = True
100+
self.scripts.config.serve_locally = True
101+
102+
def flask_app(self):
103+
return self._flask_app
104+
105+
def base_url(self):
106+
return self._base_pathname
107+
108+
def app_context(self, *args, **kwargs):
109+
return self._flask_app.app_context(*args,
110+
**kwargs)
111+
112+
def test_request_context(self, *args, **kwargs):
113+
return self._flask_app.test_request_context(*args,
114+
**kwargs)
115+
116+
def locate_endpoint_function(self, name=None):
117+
if name is not None:
118+
ep = "%s_%s" %(self._base_pathname,
119+
name)
120+
else:
121+
ep = self._base_pathname
122+
return self._notflask.endpoints[ep]['view_func']
123+
124+
@Dash.layout.setter
125+
def layout(self, value):
126+
self._fix_component_id(value)
127+
return Dash.layout.fset(self, value)
128+
129+
def _fix_component_id(self, component):
130+
theID = getattr(component,"id",None)
131+
if theID is not None:
132+
setattr(component,"id",self._fix_id(theID))
133+
try:
134+
for c in component.children:
135+
self._fix_component_id(c)
136+
except:
137+
pass
138+
139+
def _fix_id(self, name):
140+
return "%s_-_%s" %(self._uid,
141+
name)
142+
143+
def _fix_callback_item(self, item):
144+
item.component_id = self._fix_id(item.component_id)
145+
return item
146+
147+
def callback(self, output, inputs=[], state=[], events=[]):
148+
return super(NotDash, self).callback(self._fix_callback_item(output),
149+
[self._fix_callback_item(x) for x in inputs],
150+
[self._fix_callback_item(x) for x in state],
151+
[self._fix_callback_item(x) for x in events])
152+

make_env

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env bash
2+
#
3+
virtualenv -p python3 env
4+
source env/bin/activate
5+
pip install -r requirements.txt

requirements.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
certifi==2018.4.16
2+
chardet==3.0.4
3+
click==6.7
4+
dash==0.21.1
5+
dash-core-components==0.22.1
6+
dash-html-components==0.10.1
7+
decorator==4.3.0
8+
Django==2.0.5
9+
Flask==1.0.2
10+
Flask-Compress==1.4.0
11+
idna==2.6
12+
ipython-genutils==0.2.0
13+
itsdangerous==0.24
14+
Jinja2==2.10
15+
jsonschema==2.6.0
16+
jupyter-core==4.4.0
17+
MarkupSafe==1.0
18+
nbformat==4.4.0
19+
pkg-resources==0.0.0
20+
plotly==2.5.1
21+
pytz==2018.4
22+
requests==2.18.4
23+
six==1.11.0
24+
traitlets==4.3.2
25+
urllib3==1.22
26+
Werkzeug==0.14.1

0 commit comments

Comments
 (0)