Bokeh server and flask (or other framework)

Hi!

I’m trying to write a bokeh application that allows users to press a button (either a Button widget or a button on the plot toolbar) and get the subset of data that they selected. Ideally I would like to do this without javascript.

Flask has some nice, easy patterns for users saving files, but the flask embed example warns that this then becomes a single-user app. The gunicorn solution seems hacky.

I’ve got a nearly complete skeleton here:

You can run this to get a working scatterplot on port 5000, where clicking the button calls a Python print of the correct selection.

Is there a simple way to complete the def save() function to save the file to the user’s computer?

Thanks!

Juan.

Update:

I found the original post for this question:

https://groups.google.com/a/continuum.io/forum/#!topic/bokeh/O6eccziL1lM

as well as the corresponding server example:

https://demo.bokehplots.com/apps/export_csv

The reason the above doesn’t work for us is that our data contains strings with commas, so the simple formula for generating csv’s by hand falls over. Pandas have finely honed the ability to save basically anything to csv, and I want to piggyback off of that capability rather than reimplement it in javascript. I’ve tried to combine the linked savecsv example with this SO answer:

to base64 encode the csv text from pandas, then insert the base64 string into javascript code and use a CustomJS callback. Here’s the relevant Python bit (full file linked below):

data = dataframe.iloc[selection]
print(f’I got your df right here! {data.shape}’)
csv = data.to_csv()
b64 = base64.b64encode(csv.encode()).decode() # str -> bytes -> b64 -> str
jscode = f"""

  •    var link = document.createElement("a");*
    
  •    link.href = "data:text/csv;base64,{b64}";*
    
  •    link.download = "selected_data.csv";*
    
  •    link.target = "_blank";*
    
  •    link.style.visibility = "hidden";*
    
  •    console.log("saving selected data:");*
    
  •    console.log("{b64}");*
    
  •    link.dispatchEvent(new MouseEvent("click"));*
    
  • “”"*
    print('b64 encoded: ', b64)
    return CustomJS(code=jscode)

Now the problem I’m facing is that it seems I can’t update the button’s CustomJS callback: calls to button.js_on_click() once the plot is generated appear to have no effect on the javascript side. Here is the javascript console log after clicking download (with no selection), selecting, and then clicking download again:

saving selected data: bokeh.min.js:9:9
LHgseSx1c2VmdWwK bokeh.min.js:10:9

Navigated to data:text/csv;base64,LHgseSx1c2VmdWwK
saving selected data: bokeh.min.js:9:9
LHgseSx1c2VmdWwK bokeh.min.js:10:9
Navigated to data:text/csv;base64,LHgseSx1c2VmdWwK

Meanwhile, the Python side shows that the b64 string in the CustomJS callback is getting updated:

$ python minimal_example.py
WARNING:bokeh.server.util:Host wildcard ‘*’ will allow connections originating from multiple (or possibly all) hostnames or IPs. Use non-wildcard values to restrict access explicitly
I got your df right here! (0, 3)
b64 encoded: LHgseSx1c2VmdWwK
I got your df right here! (4, 3)
b64 encoded: LHgseSx1c2VmdWwKNDMsMC42OTAyMDIyNDQ2NjgzNDA2LDAuOTg5Mjg5Mjg3MDg1OTI1Niw1MQo4OSwwLjU3MjIzMzk2OTUyNDQwNjMsMC44OTM5MjIwMDA2NjYwMjc5LDEKNDAsMC42NTM1NTIwMTQ1MDU1NjUyLDAuODc5MTk4OTU3NDg5MDcyMSw3MAo1MywwLjY1MjQ4MjM3NDAzNjIwMTEsMC45NjM3ODY5NzU0NzEzNDMyLDc4Cg==

But I guess the CustomJS is not being added to the JS side. Is this a bug or a feature? Does anyone have any suggestions for how to update a javascript callback from Python, or even just the corresponding link href?

Thanks!

Juan.

… [1] https://gist.github.com/jni/f88142a1783f5e16fbd3a67630f0ab95

Kind ping…

I suspect the original problem is too hard (save a data table from selection without js), but I am wondering:

Is it possible at all to update a button’s CustomJS callback after the plot has rendered?

The above example suggests it’s not, but I’d like to know whether this is considered a bug or a feature (it might indeed be terrible for performance), or whether I’m just missing something very obvious.

Thank you!

Juan.

···

On Tuesday, September 25, 2018 at 5:22:31 PM UTC+10, Juan Nunez-Iglesias wrote:

Update:

I found the original post for this question:

https://groups.google.com/a/continuum.io/forum/#!topic/bokeh/O6eccziL1lM

as well as the corresponding server example:

https://demo.bokehplots.com/apps/export_csv

The reason the above doesn’t work for us is that our data contains strings with commas, so the simple formula for generating csv’s by hand falls over. Pandas have finely honed the ability to save basically anything to csv, and I want to piggyback off of that capability rather than reimplement it in javascript. I’ve tried to combine the linked savecsv example with this SO answer:

https://stackoverflow.com/a/42907645/224254

to base64 encode the csv text from pandas, then insert the base64 string into javascript code and use a CustomJS callback. Here’s the relevant Python bit (full file linked below):

data = dataframe.iloc[selection]
print(f’I got your df right here! {data.shape}’)
csv = data.to_csv()
b64 = base64.b64encode(csv.encode()).decode() # str -> bytes -> b64 -> str
jscode = f"""

  •    var link = document.createElement("a");*
    
  •    link.href = "data:text/csv;base64,{b64}";*
    
  •    link.download = "selected_data.csv";*
    
  •    link.target = "_blank";*
    
  •    link.style.visibility = "hidden";*
    
  •    console.log("saving selected data:");*
    
  •    console.log("{b64}");*
    
  •    link.dispatchEvent(new MouseEvent("click"));*
    
  • “”"*
    print('b64 encoded: ', b64)
    return CustomJS(code=jscode)

Now the problem I’m facing is that it seems I can’t update the button’s CustomJS callback: calls to button.js_on_click() once the plot is generated appear to have no effect on the javascript side. Here is the javascript console log after clicking download (with no selection), selecting, and then clicking download again:

saving selected data: bokeh.min.js:9:9
LHgseSx1c2VmdWwK bokeh.min.js:10:9

Navigated to data:text/csv;base64,LHgseSx1c2VmdWwK
saving selected data: bokeh.min.js:9:9
LHgseSx1c2VmdWwK bokeh.min.js:10:9
Navigated to data:text/csv;base64,LHgseSx1c2VmdWwK

Meanwhile, the Python side shows that the b64 string in the CustomJS callback is getting updated:

$ python minimal_example.py
WARNING:bokeh.server.util:Host wildcard ‘*’ will allow connections originating from multiple (or possibly all) hostnames or IPs. Use non-wildcard values to restrict access explicitly
I got your df right here! (0, 3)
b64 encoded: LHgseSx1c2VmdWwK
I got your df right here! (4, 3)
b64 encoded: LHgseSx1c2VmdWwKNDMsMC42OTAyMDIyNDQ2NjgzNDA2LDAuOTg5Mjg5Mjg3MDg1OTI1Niw1MQo4OSwwLjU3MjIzMzk2OTUyNDQwNjMsMC44OTM5MjIwMDA2NjYwMjc5LDEKNDAsMC42NTM1NTIwMTQ1MDU1NjUyLDAuODc5MTk4OTU3NDg5MDcyMSw3MAo1MywwLjY1MjQ4MjM3NDAzNjIwMTEsMC45NjM3ODY5NzU0NzEzNDMyLDc4Cg==

But I guess the CustomJS is not being added to the JS side. Is this a bug or a feature? Does anyone have any suggestions for how to update a javascript callback from Python, or even just the corresponding link href?

Thanks!

Juan.

… [1] https://gist.github.com/jni/f88142a1783f5e16fbd3a67630f0ab95