Here is a demo app. It consist in two date pickers: a start date, and an end-date. I want the end-date to be between start-date[date]
and start-date[date] + 4 weeks
.
The problem occurs when you select a start-date
later than end-date
: end-date
should be cleared, but now it just “crashes” the calendar dialog.
# import external modules
import dash
import dash_core_components as dcc
import dash_html_components as html
import pytz
# import external functions
from datetime import datetime, timedelta
from dateutil.parser import parse
from dash.dependencies import Input, Output, State
def get_today(tz):
''' Returns "today" based on time zone '''
now = datetime.now(tz=tz)
return now.strftime('%Y-%m-%d')
def four_weeks_from_startdate(date):
''' returns the date four weeks from a '%Y-%m-%d' string '''
return datetime.strptime(date, '%Y-%m-%d') + timedelta(weeks=4)
def serve_layout():
# Date picker settings
timezone = pytz.timezone('Europe/London')
startdate = get_today(timezone)
enddate = get_today(timezone)
return html.Main(
[
html.H2(
'Date',
),
dcc.DatePickerSingle(
id='start-date',
# layout settings
first_day_of_week=1,
display_format='DD/MM/YY',
placeholder='Start date',
# date defaults
date=startdate,
# date limits
min_date_allowed=datetime(2018, 1, 1),
# today
max_date_allowed=get_today(timezone),
initial_visible_month=datetime(
parse(startdate).year,
parse(startdate).month,
1
),
),
dcc.DatePickerSingle(
id='end-date',
# layout settings
first_day_of_week=1,
display_format='DD/MM/YY',
placeholder='End date',
# date defaults
date=enddate,
# clearable=True,
# date limits
min_date_allowed=datetime(2018, 1, 1),
# today
max_date_allowed=get_today(timezone),
initial_visible_month=datetime(
parse(startdate).year,
parse(startdate).month,
1
),
),
],
)
app = dash.Dash(
__name__,
)
# re-compute the layout everytime the page is refreshed — cf. https://dash.plot.ly/live-updates
app.layout = serve_layout
@app.callback(
Output('end-date', 'min_date_allowed'),
[
Input('start-date', 'date'),
]
)
def set_enddate_min_allowed_date_as_startdate(start_date):
''' updates the min_date_allowed when start_date changes '''
print(f">min_date_allowed: {start_date}")
return start_date
@app.callback(
Output('end-date', 'max_date_allowed'),
[
Input('start-date', 'date'),
]
)
def set_enddate_max_allowed_date_4weeks_after_startdate(start_date):
''' updates the max_date_allowed when start_date changes '''
print(f">max_date_allowed: {min(datetime.now(), four_weeks_from_startdate(start_date)).date()}")
return min(datetime.now(), four_weeks_from_startdate(start_date)).date()
@app.callback(
Output('end-date', 'date'),
[
Input('start-date', 'date'),
],
[
State('end-date', 'date'),
]
)
def reset_enddate_if_outofrange(start_date, current_end_date):
''' clears the end date when start_date changes only if the former is out of range'''
if current_end_date < start_date:
# end date cannot be earlier than start date, so reset
return None
elif datetime.strptime(current_end_date, '%Y-%m-%d') > four_weeks_from_startdate(start_date):
# end date cannot be later than 4 weeks after start date, so reset
return None
else:
# end date don't need to be changed
return current_end_date
if __name__ == '__main__':
app.run_server(debug=True)
I’ve updated DCC in the meantime:
$ pip3 show dash-core-components
Name: dash-core-components
Version: 0.42.1