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

Download Raw Data from Plot Title or Axis Label

Similar to what was discussed here: Download raw data, I am trying to allow users to download raw data from client-side. I am able to implement the solution suggested in the above discussion. However, what I would like to see is to include the link to raw data in the plot title.
A sample of application is as below

import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
import pandas as pd

from six.moves.urllib.parse import quote


df = pd.DataFrame({
    'a': [1, 2, 3, 4],
    'b': [2, 1, 5, 6],
    'c': ['x', 'x', 'y', 'y']
})


def generate_table(dataframe, max_rows=10):
    return html.Table(
        # Header
        [html.Tr([html.Th(col) for col in dataframe.columns])] +

        # Body
        [html.Tr([
            html.Td(dataframe.iloc[i][col]) for col in dataframe.columns
        ]) for i in range(min(len(dataframe), max_rows))]
    )


app = dash.Dash(__name__)
app.css.append_css({"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"})
app.layout = html.Div([
    html.Label('Filter'),

    dcc.Dropdown(
        id='field-dropdown',
        options=[
            {'label': i, 'value': i} for i in
            (['all'] + list(df['c'].unique()))],
        value='all'
    ),
    html.Div(id='intermediate-result', style = {'display': 'none'}),
    dcc.Graph(id= 'graph')
])


def filter_data(value):
    if value == 'all':
        return df
    else:
        return df[df['c'] == value]


@app.callback(
    dash.dependencies.Output('intermediate-result', 'children'),
    [dash.dependencies.Input('field-dropdown', 'value')])
def update_table(filter_value):
    dff = filter_data(filter_value)
    return dff.to_json(date_format = 'iso', orient = 'split')


@app.callback(
    dash.dependencies.Output('graph', 'figure'),
    [dash.dependencies.Input('intermediate-result', 'children')])
def update_graph(dff_json):
    dff = pd.read_json(dff_json,  orient='split')
    csv_string = dff.to_csv(index=False, encoding='utf-8')
    csv_string = "data:text/csv;charset=utf-8," + quote(csv_string)
    trace = go.Scatter(x = dff.a, y= dff.b)
    figure = {
        'data': [trace],
        'layout': go.Layout(
            title = 'Link to <a href = "" target="_blank"> data </a>'.format(csv_string)
        )
    }
    return figure


if __name__ == '__main__':
    app.run_server(debug=True)

But this does not seem to work. I cannot download raw data when I click the hyperlink.

Looks like there’s a couple of things going on. You haven’t included a slot in the string being formatted for csv_string to be inserted into, so the title doesn’t actually have the href set appropriately in the app. It seems that you also need to set the download attribute to the target filename as in @chriddyp’s example in order for the link to download the file. However even when I made both those changes, I couldn’t get the link to work in the title of the chart. The entire string that represents the HTML of the title is itself stored in the data-unformatted attribute of an SVG text element, so at this point we have nested attributes and I don’t think it’s possible to escape the inner quotes.

The general conclusion I think is that you should probably make a regular html.A link to the file in the same way that @chriddyp does in the example. You can always have two callbacks, one targeting the graph’s figure and one targeting the link.