Black Lives Matter. Please consider donating to Black Girls Code today.
Dash HoloViews is now available! Check out the docs.

Type in field -> click submit -> long computation done in serial -> returns data frame -> data frame plotted as histograms

Hi everyone, I have built a data scraping python backend, and I am trying to use plotly-dash as the front end/web-server. The code is designed to run from the entry point of a docker container.

I am having trouble integrating callbacks from different functions, even though there is a great variety of plotly-dash examples for things like not acting on user enter text field until submit clicked with mouse, it is hard to figure out how to move data that is returned by a compute-intensive function into something that is updated in the app.layout.

I will incrementally update this question so that the problem is clearer to anyone who can help. Also, the code exists in full context here

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import pickle
import plotly.express as px
import pandas as pd
import numpy as np
import pickle
import datetime
import time

# get wikipedia data
with open('scraped_new.p','rb') as f:
    wikipedia_corpus = pickle.load(f)

temp = [t for t in wikipedia_corpus if 'standard' in t.keys() and 'wikipedia' in t['link']]
science = ['cancer','Vaccines','evolution','climate change','Transgenic','photosysnthesis','evolution','GMO']
res = [t['standard'] for t in temp if t['query'] in science]
mwp = np.mean(res)
abstract_wiki = {'standard':mwp}

def new_old(old,new):
    old['Art_Corpus_Derived'] = True
    new['Art_Corpus_Derived'] = False
    newer = old.append(new)
    return newer

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

def long_process(NAME):
    with open('traingDats.p','rb') as f:
        artcorpus = pickle.load(f)
    with open('scraped_new.p','rb') as f:
        new_author = pickle.load(f)

    new_author = pd.DataFrame(new_author)
    artcorpus = pd.DataFrame(artcorpus)

    print('enters long process')
    verbose = True
    ''' Expensive process
    from SComplexity import online_app_backend
    ar = online_app_backend.call_from_front_end(NAME,verbose=verbose)
    '''
    newer = new_old(artcorpus,ar)
    plot = dcc.Graph(
        id='example-graph',
        figure = px.histogram(newer, x="standard", y="standard", color="Art_Corpus_Derived",
                   marginal="box", # or violin, rug
                   hover_data=newer.columns,width=900, height=900)
    )
    return plot
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

#newer = new_old(artcorpus,new_author)

app.layout = html.Div([
    html.Button(id='submit-button', n_clicks=0, children='Submit'),
    dcc.Input(id='input-1-state', type='text', value='enter author name'),
    html.Div(id='newer'),
    html.Div([html.Img(id = 'long_process', src = '')],
             id='plot_div'),
    html.H1(children='Author contest on science readability'),
    html.Div(children='''
        Wikipedia on science, versus art corpus all biochem papers.
    ''')
])
@app.callback(
    Output('newer', 'children'),
    [Input('submit-button', 'n_clicks')],
    [State('input-1-state', 'value')]
)
def update_graph(n_clicks, value):
    print(u'''
        The Button has been pressed {} times,
        Input 1 is "{}",
        and Input 2 is "{}"
    '''.format(n_clicks))
    print(u'''
        The Button has been pressed {} times,
        Input 1 is "{}",
        and Input 2 is "{}"
    '''.format(value))
    newer = long_process(value)
    return newer

app.css.config.serve_locally = True
app.scripts.config.serve_locally = True
if __name__ == '__main__':
    app.run_server(debug=True)

Hi @rjjarvis, do you have specific questions about the code you wrote? One comment is that you don’t define all your components in the layout, since the dcc.Graph is created in the callback. This is a valid way to use Dash, but you need to disable callback validation in this case, as described https://dash.plot.ly/faqs (look for callback validation). A more classical pattern is to define in the layout an empty dcc.Graph and then the callback modifies the figure property of the dcc.Graph (then you function long_process would return the figure object instead of plot).

One remark: you say that you want to update the post, please do so by appending new messages to this thread rather than modifying the existing one, otherwise the conversation will not make much sense to readers :-).

Hi Emmanuelle,

Thanks for your response. I do have specific questions about the code, but I just need to spend a bit of time getting clarity.

I am looking into disabling callback validation now.

A challenge I am facing is it takes a few minutes to build the dockerfile that launches the plotly-dash file in the entrypoint. To circumvent the long build I would like to use a mock version of the long_process function that acts as stand-in to the compute job, that I ultimately want to use. The call to long_preocess should be able to return data, or a graph that is plotted, for the moment that can be based on pickle data, and maybe a plot of mockup/random data, with a title that is made from a user-entered string. The user entered string needs to only be acted on once a submit button is pressed.