I’ve set up a series of inter-dependent parameters, based on my data. (In-situ water quality measurements in local streams.) The data is parameterized by
- measurement type,
- a sub-set of locations where that was measured for cross-site comparison,
- a single location from out of that subset for time-series plotting,
- and a single storm event from the list of those available at the single location to view in a detailed plot.
Roughly, the workflow goes like this: first the user picks a measurement type (“conductivity”, “turbidity”, etc). A @depends linked function then slices my summary dataframe to sites where that parameter was measured and changes the “options” for the second parameter. The data from the sliced dataframe is used to populate a map view of the locations. A multi-select widget is also shown for the second parameter. The user then selects a subset of those locations either with said widget or using the plot selection tools on the map - either of which would updates the values of the second parameter (via a 1-D stream in the case of the map). Another @depends/watched function updates the options for the 3rd single-site parameter based on the sites selected via the map or the multi-select. All of those steps work as I expect. In each case the “options” are simple lists and I use a create a new temporary list as suggested in several posts and then repopulate the new options from it. Took me a while to figure out, but it’s working now.
But… in the next step of the workflow, the user gets to pick one single event from the one single location they’ve picked for a zoomed in plot. But my event data is in a separate dataframe keyed by event number and I want to show the user the starting event date rather than the event number in a discrete slider widget. So I create a dictionary as the options for the single storm event parameter (key is the date as a text string, value is the event number). When a new site is selected, the options dictionary is replaced. BUT… it doesn’t work properly, when I pick a date, then parameter value is set to that text string instead of to the event number the string is a key for. If I pick a static dictionary and do not ever change it, it works as expected, selecting the date key updates the parameter value to the event number. But when the options dictionary is changed, it no longer looks up the key in the option and returns the key itself instead of the value of the key. I assume this is not intended behavior. Am I coding wrong (likey) or is it really a bug I should report?
My code’s not very neat, but here are the relevant chunks:
class ParameterizedStorm(param.Parameterized):
measured_parameter = param.ObjectSelector(
objects=measured_parameter_options,
default="turbidity",
label="Measured parameter",
doc="The parameter measured in-situ in the stream.",
)
locations_selected_on_map = param.ListSelector(
label="Locations for cross-site comparison",
doc="The locations to view and compare.",
)
single_location = param.ObjectSelector(
label="Location for time-series plot",
doc="A single location to plot time-series data from.",
)
# placeholder
no_storm_selected_dict = {
"Select a location!": 0,
"Select any location!": 1,
}
single_storm_number = param.ObjectSelector(
label="Storm for time-series plot",
doc="A single storm to plot time-series data from.",
objects=no_storm_selected_dict,
default=0,
)
def __init__(self):
super().__init__()
# other stuff not related to the date parameter happens
def get_discrete_storm_opts(
self, the_param: str, the_location: str
) -> Tuple[Dict, int]:
pk_data = self.by_storm_summary.loc[
(self.by_storm_summary["measured_param"] == the_param)
& (self.by_storm_summary["Location"] == the_location)
].reset_index()
df2 = (
pk_data[["date_text", "storm_number"]]
.drop_duplicates()
.set_index("date_text")
)
date_opts_dict = df2.to_dict()["storm_number"]
if len(df2.index) > 0:
init_selected_date = df2.index[0]
return date_opts_dict, init_selected_date
else:
error_dict = {
"No storms recorded for this param and location": 0,
"Pick another location": 1,
}
return (
error_dict,
next(iter(error_dict)),
)
# This updates the date list available on the date slider based on the selected location
@param.depends("single_location", watch=True)
def _update_dates(self):
print(
strftime("%H:%M:%S ", gmtime())
+ "_update_dates: Updating the possible storm dates based on {} at {}".format(
self.measured_parameter, self.single_location
)
)
one_loc = self.single_location
cur_selected_storm_no = self.single_storm_number
# If there is a single location selected, we get the possible storms
# based on that location and the current parameter value
if one_loc is not None:
# given the current location and parameter, find the possible dates
new_date_opts, new_selected_date = self.get_discrete_storm_opts(
self.measured_parameter, self.single_location
)
# set the options to the full list of dates
self.param["single_storm_number"].objects = new_date_opts
new_selected_storm_no = new_selected_date
# if there is not a single location selected
else:
# put in dummy values asking for a location to be selected
self.param["single_storm_number"].objects = no_storm_selected_dict
new_selected_storm_no = next(iter(no_storm_selected_dict))
# if the value isn't changed (ie, 0 to 0) no trigger will happen for the
# single_storm_number event, so we manually trigger it
self.single_storm_number = new_selected_storm_no
if new_selected_storm_no == cur_selected_storm_no:
print(
strftime("%H:%M:%S ", gmtime())
+ "Manually triggering a single_storm_number event"
)
self.param.trigger("single_storm_number")
print(strftime("%H:%M:%S ", gmtime()) + "finished _update_dates")