How to write good callback for one graph and calendar (DatePickerRange)

Hi everyone,
I just started to use Plotly and Dash. I try to connect my interactive graph with calendar (DatePickerRange)

Both off them display correctly, but not together. My figure doesn’t see the start and end dates.

Could someone help me?

With regards
Dorota

Hi @Dorota-Toskania

Share a simple example to see the problem.

The data I retrieve from database.
In attached the screenshot with dashboard.
Thank You Eduardo for your help.

# -----------------------------------
# Retrieve data from database
# -----------------------------------

curs = db.cursor()

testy = """SELECT o.ticker, o.issuer, o.session_date, o.open, o.close, o.min
FROM olhc AS o;"""

curs.execute(testy)

data = []
for x in curs:
    data.append(x)

df = pd.DataFrame(data, columns=['ticker', 'issuer', 'session_date', 'open', 'close', 'min'])
# print(df.sample())

# -----------------------------------
# Initialize the app
# -----------------------------------
# This application is using a custom
# CSS stylesheet to modify the default
# styles of the elements.
# -----------------------------------

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

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

colors = dict(background='#3333',
              text='#21617A',
              font='#21617A')

# -----------------------------------
# Define app layout
# -----------------------------------

app.layout = html.Div(children=[
    html.H1(children='Close & Open prices for company',
            style={'textAlign': 'center', 'color': colors['text']}
            ),
    html.Div(children=[
        html.Div(style={
            'textAlign': 'center', 'color': colors['font']
            },
            children='''Financial Dashboard for FellowshipPL
        '''),
        dcc.DatePickerRange(
            id='my-date-picker-range',
            calendar_orientation='horizontal',
            day_size=30,
            first_day_of_week=1,  # 0 Sunday
            clearable=False,
            with_portal=False,  # True on the page
            min_date_allowed=date(2010, 1, 1),
            max_date_allowed=date(2021, 12, 31),
            initial_visible_month=date(2020, 1, 1),
            start_date=date(2020, 1, 1),
            end_date=date(2021, 12, 31),
            display_format='MMM Do, YYYY',  # lots possibilities

            updatemode='singledate'
        ),
        html.Div(id='output-container-date-picker-range'),

        # -----------------------------------
        # Define Dropdown
        # -----------------------------------

        dcc.Dropdown(style={
                    'textAlign': 'left',
                    'color': colors['text']

                },
                    id='issuer_selection',
                    options=[
                        {'label': i, 'value': i} for i in df.issuer.unique()
                    ], multi=False,
                    placeholder='Filter by name of company ...'),
        html.H3(id='text'),

        dcc.Graph(id='indicators')])
                    ])

# -----------------------------------
# Define first callback
# -----------------------------------

@app.callback(
    Output('output-container-date-picker-range', 'children'),
    [Input('my-date-picker-range', 'start_date'),
     Input('my-date-picker-range', 'end_date')])
def update_output(start_date, end_date):
    string_prefix = 'You have selected: '
    # if start_date is not None:
    #     start_date_object = date.fromisoformat(start_date)
    #     start_date_string = start_date_object.strftime('%B %d, %Y')
    #     string_prefix = string_prefix + 'Start Date: ' + start_date_string + ' | '
    # if end_date is not None:
    #     end_date_object = date.fromisoformat(end_date)
    #     end_date_string = end_date_object.strftime('%B %d, %Y')
    #     string_prefix = string_prefix + 'End Date: ' + end_date_string
    # if len(string_prefix) == len('You have selected: '):
    #     return 'Select a date to see it displayed here'
    # else:
    return string_prefix

@app.callback(
    Output('indicators', 'figure'),
    [Input('issuer_selection', 'value')
])
def retrieve_plots(issuer):
    filtered_df = df[df['issuer'] == issuer]

    # Creating trace1
    trace1 = go.Scatter(x=filtered_df['session_date'],
                        y=filtered_df['close'],
                        mode="markers",
                        name="Close price",
                        marker=dict(color='#21617A', size=4),
                        text=filtered_df['session_date'])

    # Creating trace2
    trace2 = go.Scatter(x=filtered_df['session_date'],
                        y=filtered_df['open'],
                        mode="markers",
                        name="Open price",
                        marker=dict(color='#C22E4C', size=3),
                        text=filtered_df.session_date)

    # Creating trace3
    trace3 = go.Scatter(x=filtered_df['session_date'],
                        y=filtered_df['min'],
                        mode="markers",
                        name="Min price",
                        marker=dict(color='#7FD13A', size=2),
                        text=filtered_df.session_date)

    data = [trace1, trace2, trace3]

    layout = dict(yaxis=dict(title='Prices', ticklen=5, zeroline=False),
                  xaxis=dict(title='Date', ticklen=5, zeroline=False),
                  hovermode="x unified",
                  style={'textAlign': 'center',
                         'color': colors['text']
                         },
                  )
    datapoints = {'data': data, 'layout': layout}
    return datapoints

# -----------------------------------
# Run the app
# -----------------------------------


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

Hi @Dorota-Toskania

Your graph figure is bild in a callback that do not use the selected dates as input, I think you must do the following:

@app.callback(
    Output('indicators', 'figure'),
    [Input('issuer_selection', 'value'),
     Input('my-date-picker-range', 'start_date'),
     Input('my-date-picker-range', 'end_date')])
def retrieve_plots(issuer, start, end):

Then using the start and end variable limit the filtered_df you are bilding.

Thank You,
My variable date is “session_date”. I don’t know how to use the start and end variable with session_date.

It should be probably the range without start_session_date and end_ession_date. But I don’t know how to do it?

@Dorota-Toskania

“session_date” is the name of the DataFrame column of your df, this is just a Pandas issue: select from the df only the dates in the “session_date” column that are grather than start and lower than end.

Wow thanks Eduardo, I gona try

1 Like

Unfortunately It doesn’t run.

I have change a code several times, and it is gone worse.
Still a callback doesn’t want to display figure with start date and end date from calendar.
It this moment I don’t see the graphs. Empty figure.

I have connect two callback in one, and nothing better.
Please help.

...
...

curs = db.cursor()

testy = """SELECT o.ticker, o.issuer, o.session_date, o.open, o.close, o.min
FROM olhc AS o;"""

curs.execute(testy)

data = []
for x in curs:
    data.append(x)

df = pd.DataFrame(data, columns=['ticker', 'issuer', 'session_date', 'open', 'close', 'min'])
print(df.sample())

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

app = JupyterDash(__name__, external_stylesheets=external_stylesheets)

colors = dict(background='#3333',
              text='#21617A',
              font='#21617A')

# -----------------------------------
# Define app layout
# -----------------------------------

app.layout = html.Div(children=[
    html.H1(children='Close & Open prices for company',
            style={'textAlign': 'center', 'color': colors['text']}
            ),
    html.Div(children=[
        html.Div(style={
            'textAlign': 'center', 'color': colors['font']
            },
            children='''Financial Dashboard for FellowshipPL
        '''),
        dcc.DatePickerRange(
            id='my-date-picker-range',
            calendar_orientation='horizontal',
            day_size=30,
            first_day_of_week=1,  # 0 Sunday
            clearable=False,
            with_portal=False,  # True on the page
            min_date_allowed=date(2010, 1, 1),
            max_date_allowed=date(2021, 12, 31),
            initial_visible_month=date(2020, 1, 1),
            start_date=date(2020, 1, 1),
            end_date=date(2021, 12, 31),
            display_format='MMM Do, YYYY',  # lots possibilities

            updatemode='singledate'
        ),
        html.Div(id='output-container-date-picker-range'),

        # -----------------------------------
        # Define Dropdown
        # -----------------------------------

        dcc.Dropdown(style={
                    'textAlign': 'left',
                    'color': colors['text']

                },
                    id='issuer_selection',
                    options=[
                        {'label': i, 'value': i} for i in df.issuer.unique()
                    ], multi=False,
                    placeholder='Filter by name of company ...'),
        html.H3(id='text'),

        dcc.Graph(id='indicators')])
                    ])


# -----------------------------------
# Define first callback
# -----------------------------------

# @app.callback(
#     Output('output-container-date-picker-range', 'children'),
#     [Input('my-date-picker-range', 'start_date'),
#      Input('my-date-picker-range', 'end_date')])

# def update_output(start_date, end_date):
#     string_prefix = 'You have selected: '
#     if start_date is not None:
#         start_date_object = date.fromisoformat(start_date)
#         start_date_string = start_date_object.strftime('%B %d, %Y')
#         string_prefix = string_prefix + 'Start Date: ' + start_date_string + ' | '
#     if end_date is not None:
#         end_date_object = date.fromisoformat(end_date)
#         end_date_string = end_date_object.strftime('%B %d, %Y')
#         string_prefix = string_prefix + 'End Date: ' + end_date_string
#     if len(string_prefix) == len('You have selected: '):
#         return 'Select a date to see it displayed here'
#     else:
#         return string_prefix

# @app.callback(
#     Output('indicators', 'figure'),
#     [Input('issuer_selection', 'value'),
#      Input('my-date-picker-range', 'start_date'),
#      Input('my-date-picker-range', 'end_date')])

@app.callback(
    Output('indicators', 'figure'),
    #Output('output-container-date-picker-range', 'children'),
    [Input('my-date-picker-range', 'start_date'),
     Input('my-date-picker-range', 'end_date'),
    Input('issuer_selection', 'value'),])
def retrieve_plots(issuer, start_date, end_date):
    filtered_df = df[df['issuer'] == issuer]
    dff = filtered_df.loc[start_date:end_date]
    
    # Creating trace1
    trace1 = go.Scatter(x=dff, 
                       # x=filtered_df['session_date'],
                        y=filtered_df['close'],
                        mode="markers",
                        name="Close price",
                        marker=dict(color='#21617A', size=4),
                        text=filtered_df['session_date'])

    # Creating trace2
    trace2 = go.Scatter(x=dff,
                       # x=filtered_df['session_date'],
                        y=filtered_df['open'],
                        mode="markers",
                        name="Open price",
                        marker=dict(color='#C22E4C', size=3),
                        text=filtered_df.session_date)

    # Creating trace3
    trace3 = go.Scatter(x=dff, 
                        # x=filtered_df['session_date'],
                        y=filtered_df['min'],
                        mode="markers",
                        name="Min price",
                        marker=dict(color='#7FD13A', size=2),
                        text=filtered_df.session_date)

    data = [trace1, trace2, trace3]

    layout = dict(yaxis=dict(title='Prices', ticklen=5, zeroline=False),
                  xaxis=dict(title='Date', ticklen=5, zeroline=False),
                  hovermode="x unified",
                  style={'textAlign': 'center',
                         'color': colors['text']
                         },
                  )
    datapoints = {'data': data, 'layout': layout}
    return datapoints

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

hey @Dorota-Toskania

Your issue is just python and Pandas, try the following:

First print start_date and end_date to be sure the info you are getting its comparable with the info you have in the “session_date” column.

def retrieve_plots(issuer, start_date, end_date):
    print(start_date, end_date)
    filtered_df = df[df['issuer'] == issuer]

The dates will be showed in the prompt command line.

Then just try in python how to go from the entire DataFrame to the limited DataFrame (with “start” and “end” limits using variables that are exactly the same as strart_date and end_date).
If you can achive this in python then you can do it in the callback.