Skip to content

Commit 317c8e9

Browse files
delsimGibbsConsulting
authored andcommitted
Initial arguments (#32)
* Added intial_arguments handling for all apps * Improve demo pages - better css would help * Fix urls * Move initial arguments example into the initial state demo page * Another demo tweak * Tidy up use of route names to ensure trailing slash * Added separate test script and increased test coverage * Refactor test * Added documentation for initial_arguments template argument
1 parent 00b60d8 commit 317c8e9

File tree

16 files changed

+147
-53
lines changed

16 files changed

+147
-53
lines changed

check_code

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
#
33
./check_code_dpd
44
./check_code_demo
5+
./check_code_demo_test
56
#
67

check_code_demo

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@
33
source env/bin/activate
44
pushd demo
55
pylint demo --rcfile=../pylintrc
6-
pytest demo --cov=demo --cov=django_plotly_dash --cov-report term-missing
76
popd

check_code_demo_test

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env bash
2+
#
3+
source env/bin/activate
4+
pushd demo
5+
pytest demo --cov=demo --cov=django_plotly_dash --cov-report term-missing
6+
popd

demo/demo/plotly_apps.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ def callback_liveIn_button_press(red_clicks, blue_clicks, green_clicks,
187187
datetime.fromtimestamp(0.001*timestamp))
188188

189189
liveOut = DjangoDash("LiveOutput",
190-
)#serve_locally=True)
190+
)#serve_locally=True)
191191

192192
def _get_cache_key(state_uid):
193193
return "demo-liveout-s6-%s" % state_uid

demo/demo/templates/demo_four.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,5 @@ <h1>Live Updating</h1>
5454
curl http://localhost:8000/dpd/views/poke/ -d'{"channel_name":"live_button_counter","label":"named_counts","value":{"click_colour":"red"}}'
5555
</div>
5656
</div>
57+
<p></p>
5758
{%endblock%}

demo/demo/templates/demo_one.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,5 @@ <h1>Simple App Embedding</h1>
3131
</div>
3232
</div>
3333
</div>
34+
<p></p>
3435
{%endblock%}
35-

demo/demo/templates/demo_three.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,5 @@ <h1>Enhanced Callback Functionality</h1>
3939
{%plotly_app slug="ex2-3" ratio=0.15 %}
4040
</div>
4141
</div>
42+
<p></p>
4243
{%endblock%}

demo/demo/templates/demo_two.html

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,29 @@ <h1>Initial State</h1>
1414
state is persisted in a standard
1515
Django <a href="{%url "admin:django_plotly_dash_dashapp_changelist" %}">model</a>.
1616
</p>
17+
<p>
18+
It is also possible to specify intial arguments in the template. These override the values specifed in
19+
the code defining the app, and also take priority over any stored state.
20+
</p>
1721
<div class="card bg-light border-dark">
1822
<div class="card-body">
1923
<p><span>{</span>% load plotly_dash %}</p>
20-
<p><span>{</span>% plotly_app slug="simpleexample-1" ratio=0.2 %}</p>
24+
<p><span>{</span>% plotly_app slug="simpleexample-1" ratio=0.1 %}</p>
25+
<p><span>{</span>% plotly_app name="simpleexample-1" initial_arguments='{"dropdown-color": {"value": "green"}}' %}</p>
2126
</div>
2227
</div>
2328
<p>
2429
</p>
2530
<div class="card border-dark">
2631
<div class="card-body">
27-
{%plotly_app slug="simpleexample-1" ratio=0.2 %}
32+
{%plotly_app slug="simpleexample-1" ratio=0.1 %}
33+
</div>
34+
</div>
35+
<p></p>
36+
<div class="card border-dark">
37+
<div class="card-body">
38+
{%plotly_app slug="simpleexample-1" initial_arguments='{"dropdown-color": {"value": "green"}}' %}
2839
</div>
2940
</div>
41+
<p></p>
3042
{%endblock%}

demo/demo/tests/test_dpd_demo.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,26 @@
44

55
import pytest
66

7+
from django.urls import reverse
8+
79
@pytest.mark.django_db
810
def test_template_tag_use(client):
911
'Check use of template tag'
1012

11-
from django.urls import reverse
12-
1313
for name in ['demo-one', 'demo-two', 'demo-three', 'demo-four',]:
1414
url = reverse(name, kwargs={})
1515

1616
response = client.get(url)
1717

1818
assert response.content
1919
assert response.status_code == 200
20+
21+
@pytest.mark.django_db
22+
def test_add_to_session(client):
23+
'Check use of session variable access helper'
24+
25+
url = reverse('session-variable-example')
26+
response = client.get(url)
27+
28+
assert response.content
29+
assert response.status_code == 200

django_plotly_dash/dash_wrapper.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,11 @@ def __init__(self, name=None, serve_locally=False,
100100
bootstrap_source = css_url()['href']
101101
self.css.append_script({'external_url':[bootstrap_source,]})
102102

103-
def as_dash_instance(self):
103+
def as_dash_instance(self, cache_id=None):
104104
'''
105105
Form a dash instance, for stateless use of this app
106106
'''
107-
return self.do_form_dash_instance()
107+
return self.do_form_dash_instance(cache_id=cache_id)
108108

109109
def handle_current_state(self):
110110
'Do nothing impl - only matters if state present'
@@ -116,7 +116,7 @@ def have_current_state_entry(self, wid, key):
116116
'Do nothing impl - only matters if state present'
117117
pass
118118

119-
def get_base_pathname(self, specific_identifier):
119+
def get_base_pathname(self, specific_identifier, cache_id):
120120
'Base path name of this instance, taking into account any state or statelessness'
121121
if not specific_identifier:
122122
app_pathname = "%s:app-%s"% (app_name, main_view_label)
@@ -125,13 +125,21 @@ def get_base_pathname(self, specific_identifier):
125125
app_pathname = "%s:%s" % (app_name, main_view_label)
126126
ndid = specific_identifier
127127

128-
full_url = reverse(app_pathname, kwargs={'ident':ndid})
128+
kwargs = {'ident': ndid}
129+
130+
if cache_id:
131+
kwargs['cache_id'] = cache_id
132+
app_pathname = app_pathname + "--args"
133+
134+
full_url = reverse(app_pathname, kwargs=kwargs)
135+
if full_url[-1] != '/':
136+
full_url = full_url + '/'
129137
return ndid, full_url
130138

131-
def do_form_dash_instance(self, replacements=None, specific_identifier=None):
139+
def do_form_dash_instance(self, replacements=None, specific_identifier=None, cache_id=None):
132140
'Perform the act of constructing a Dash instance taking into account state'
133141

134-
ndid, base_pathname = self.get_base_pathname(specific_identifier)
142+
ndid, base_pathname = self.get_base_pathname(specific_identifier, cache_id)
135143
return self.form_dash_instance(replacements, ndid, base_pathname)
136144

137145
def form_dash_instance(self, replacements=None, ndid=None, base_pathname=None):
@@ -242,16 +250,24 @@ def use_dash_layout(self):
242250
'''
243251
return self._use_dash_layout
244252

245-
def augment_initial_layout(self, base_response):
253+
def augment_initial_layout(self, base_response, initial_arguments=None):
246254
'Add application state to initial values'
247-
if self.use_dash_layout() and False:
255+
if self.use_dash_layout() and not initial_arguments and False:
248256
return base_response.data, base_response.mimetype
257+
249258
# Adjust the base layout response
250259
baseDataInBytes = base_response.data
251260
baseData = json.loads(baseDataInBytes.decode('utf-8'))
261+
262+
# Also add in any initial arguments
263+
if initial_arguments:
264+
if isinstance(initial_arguments, str):
265+
initial_arguments = json.loads(initial_arguments)
266+
252267
# Walk tree. If at any point we have an element whose id
253268
# matches, then replace any named values at this level
254-
reworked_data = self.walk_tree_and_replace(baseData)
269+
reworked_data = self.walk_tree_and_replace(baseData, initial_arguments)
270+
255271
response_data = json.dumps(reworked_data,
256272
cls=PlotlyJSONEncoder)
257273

@@ -274,7 +290,7 @@ def walk_tree_and_extract(self, data, target):
274290
for element in data:
275291
self.walk_tree_and_extract(element, target)
276292

277-
def walk_tree_and_replace(self, data):
293+
def walk_tree_and_replace(self, data, overrides):
278294
'''
279295
Walk the tree. Rely on json decoding to insert instances of dict and list
280296
ie we use a dna test for anatine, rather than our eyes and ears...
@@ -285,17 +301,19 @@ def walk_tree_and_replace(self, data):
285301
# look for id entry
286302
thisID = data.get('id', None)
287303
if thisID is not None:
288-
replacements = self._replacements.get(thisID, {})
304+
replacements = overrides.get(thisID, None) if overrides else None
305+
if not replacements:
306+
replacements = self._replacements.get(thisID, {})
289307
# walk all keys and replace if needed
290308
for k, v in data.items():
291309
r = replacements.get(k, None)
292310
if r is None:
293-
r = self.walk_tree_and_replace(v)
311+
r = self.walk_tree_and_replace(v, overrides)
294312
response[k] = r
295313
return response
296314
if isinstance(data, list):
297315
# process each entry in turn and return
298-
return [self.walk_tree_and_replace(x) for x in data]
316+
return [self.walk_tree_and_replace(x, overrides) for x in data]
299317
return data
300318

301319
def flask_app(self):
@@ -328,7 +346,7 @@ def locate_endpoint_function(self, name=None):
328346
# pylint: disable=no-member
329347
@Dash.layout.setter
330348
def layout(self, value):
331-
'Overloaded layoyt function to fix component names as needed'
349+
'Overloaded layout function to fix component names as needed'
332350

333351
if self._adjust_id:
334352
self._fix_component_id(value)

0 commit comments

Comments
 (0)