Skip to content

Commit

Permalink
Merge pull request #44 from lsst/tickets/PREOPS-4400
Browse files Browse the repository at this point in the history
Tickets/PREOPS-4400: Date does not update correctly.
  • Loading branch information
alserene authored Nov 3, 2023
2 parents a01d4b7 + 6d600dc commit d4626da
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 63 deletions.
214 changes: 153 additions & 61 deletions schedview/app/scheduler_dashboard/scheduler_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
import schedview.plot.survey

DEFAULT_CURRENT_TIME = Time.now()
DEFAULT_TIMEZONE = "America/Santiago"
DEFAULT_TIMEZONE = "UTC" # "America/Santiago"
LOGO = "/assets/lsst_white_logo.png"
COLOR_PALETTES = [color for color in bokeh.palettes.__palettes__ if "256" in color]

Expand Down Expand Up @@ -113,8 +113,13 @@ class Scheduler(param.Parameterized):
label="Scheduler pickle file",
doc=scheduler_fname_doc,
)
date = param.Date(default=DEFAULT_CURRENT_TIME.datetime.date(), doc="")
USER_tier = param.Selector(
widget_datetime = param.Date(
default=DEFAULT_CURRENT_TIME.datetime.date(),
label="Date and time (UTC)",
doc="",
)
url_mjd = param.Number(default=None)
widget_tier = param.Selector(
default="",
objects=[""],
label="Tier",
Expand All @@ -135,11 +140,6 @@ class Scheduler(param.Parameterized):
summary_widget = param.Parameter(default=None, doc="")
reward_widget = param.Parameter(default=None, doc="")
show_loading_indicator = param.Boolean(default=False)
# data_time in mjd accepted as URL paramater
date_time = param.Number(default=None)
# the date_time value used allover the dashboard
# either set by URL parameter or date picker
_date_time = param.Number(default=None)

# Param parameters (used in depends decoraters and trigger calls).
_publish_summary_widget = param.Parameter(None)
Expand All @@ -156,6 +156,7 @@ class Scheduler(param.Parameterized):
map_title_pane = None

# Non-Param internal parameters.
_mjd = None
_tier = None
_survey = 0
_reward = -1
Expand All @@ -173,8 +174,6 @@ class Scheduler(param.Parameterized):
_display_reward = False
_display_dashboard_data = False
_do_not_trigger_update = True
_model_observatory = ModelObservatory(init_load_length=1)
_isDateUpdating = False

# ------------------------------------------------------------ User actions

Expand All @@ -189,6 +188,16 @@ def _update_scheduler_fname(self):
self.show_loading_indicator = False
return

# Current fix for _conditions.mjd having different datatypes.
if type(self._conditions._mjd) == np.ndarray:
self._conditions._mjd = self._conditions._mjd[0]

# Get mjd from pickle and set widget and URL to match.
self._do_not_trigger_update = True
self.url_mjd = self._conditions._mjd
self.widget_datetime = Time(self._conditions._mjd, format="mjd").to_datetime()
self._do_not_trigger_update = False

if not self.make_scheduler_summary_df():
self.clear_dashboard()
self.show_loading_indicator = False
Expand Down Expand Up @@ -219,35 +228,30 @@ def _update_scheduler_fname(self):

self.show_loading_indicator = False

@param.depends("date_time", watch=True)
def _update_date_from_mjd(self):
"""Update the dashboard when a user chooses a new date/time."""

self._isDateUpdating = True
self._date_time = self.date_time
self.date = Time(self.date_time, format="mjd").to_datetime()
self._isDateUpdating = False

@param.depends("date", watch=True)
def _update_date_from_picker(self):
"""Update the dashboard when a user chooses a new date/time."""
if not self._isDateUpdating:
self._isDateUpdating = True
self.date_time = Time(
Timestamp(
self.date,
tzinfo=ZoneInfo(DEFAULT_TIMEZONE),
)
).mjd
@param.depends("widget_datetime", watch=True)
def _update_mjd_from_picker(self):
"""Update the dashboard when a datetime is input in widget."""
if self._do_not_trigger_update:
return

@param.depends("_date_time", watch=True)
def _update_date(self):
"""Update the dashboard when date/time mjd value changes
either by url parameter or date picker
"""
self.show_loading_indicator = True
self.clear_dashboard()

self._do_not_trigger_update = True
self.url_mjd = Time(
Timestamp(
self.widget_datetime,
tzinfo=ZoneInfo(DEFAULT_TIMEZONE),
)
).mjd
self._do_not_trigger_update = False
self._mjd = self.url_mjd

if not self.update_conditions():
self.clear_dashboard()
self.show_loading_indicator = False
return

if not self.make_scheduler_summary_df():
self.clear_dashboard()
self.show_loading_indicator = False
Expand Down Expand Up @@ -281,12 +285,64 @@ def _update_date(self):

self.show_loading_indicator = False

@param.depends("USER_tier", watch=True)
@param.depends("url_mjd", watch=True)
def _update_mjd_from_url(self):
"""Update the dashboard when an mjd is input in URL."""
if self._do_not_trigger_update:
return

self.show_loading_indicator = True
self.clear_dashboard()

self._do_not_trigger_update = True
self.widget_datetime = Time(self.url_mjd, format="mjd").to_datetime()
self._do_not_trigger_update = False
self._mjd = self.url_mjd

if not self.update_conditions():
self.clear_dashboard()
self.show_loading_indicator = False
return

if not self.make_scheduler_summary_df():
self.clear_dashboard()
self.show_loading_indicator = False
return

if self.summary_widget is None:
self.create_summary_widget()
else:
self.update_summary_widget_data()
self.param.trigger("_publish_summary_widget")

self._do_not_trigger_update = True
self.summary_widget.selection = [0]
self._do_not_trigger_update = False

self.compute_survey_maps()
self.survey_map = self.param["survey_map"].objects[-1]
self._map_name = self.survey_map.split("@")[0].strip()

self.create_sky_map_base()
self.update_sky_map_with_survey_map()
self.param.trigger("_publish_map")

self.make_reward_df()
self.create_reward_widget()
self.param.trigger("_publish_reward_widget")

self._display_dashboard_data = True
self._display_reward = False
self.param.trigger("_update_headings")

self.show_loading_indicator = False

@param.depends("widget_tier", watch=True)
def _update_tier(self):
"""Update the dashboard when a user chooses a new tier."""
if not self._display_dashboard_data:
return
self._tier = self.USER_tier
self._tier = self.widget_tier
self._survey = 0
self._survey_name = self._scheduler_summary_df[
self._scheduler_summary_df["tier"] == self._tier
Expand Down Expand Up @@ -442,10 +498,10 @@ def clear_dashboard(self):
self.param.trigger("_publish_map")
self.param.trigger("_update_headings")

self.param["USER_tier"].objects = [""]
self.param["widget_tier"].objects = [""]
self.param["survey_map"].objects = [""]

self.USER_tier = ""
self.widget_tier = ""
self.survey_map = ""

self._tier = ""
Expand Down Expand Up @@ -487,36 +543,72 @@ def read_scheduler(self):

return False

def make_scheduler_summary_df(self):
"""Update conditions, and make the reward
and scheduler summary dataframes.
def update_conditions(self):
"""Update Conditions object.
Returns
-------
success : 'bool'
Record of success of conditions update and dataframe construction.
Record of success of Conditions update.
"""
if self._scheduler is None:
self._debugging_message = "Cannot update survey reward table as no pickle is loaded."
if self._conditions is None:
self._debugging_message = "Cannot update Conditions object as no Conditions object is loaded."

return False

try:
self._debugging_message = "Starting to make scheduler summary dataframe."
pn.state.notifications.info("Making scheduler summary dataframe...", duration=0)
self._debugging_message = "Starting to update Conditions object."
pn.state.notifications.info("Updating Conditions object...", duration=0)

# TODO: Conditions setter bug-fix.
# self._conditions.mjd = self._mjd

# self._conditions.mjd = self._date_time
if self._model_observatory.nside != self._scheduler.nside:
# Use instance of ModelObservatory until Conditions
# setting bug is fixed.
if (
not hasattr(self, "_model_observatory")
or self._model_observatory.nside != self._scheduler.nside
):
self._model_observatory = ModelObservatory(
nside=self._scheduler.nside,
init_load_length=1,
)
self._model_observatory.mjd = self._date_time

self._model_observatory.mjd = self._mjd
self._conditions = self._model_observatory.return_conditions()

self._scheduler.update_conditions(self._conditions)

self._debugging_message = "Finished updating Conditions object."
pn.state.notifications.clear()
pn.state.notifications.success("Conditions object updated successfully")

return True

except Exception:
tb = traceback.format_exc(limit=-1)
self._debugging_message = f"Conditions object unable to be updated: \n{tb}"
pn.state.notifications.clear()
pn.state.notifications.error("Conditions object unable to be updated!", duration=0)

return False

def make_scheduler_summary_df(self):
"""Make the reward and scheduler summary dataframes.
Returns
-------
success : 'bool'
Record of success of dataframe construction.
"""
if self._scheduler is None:
self._debugging_message = "Cannot update survey reward table as no pickle is loaded."

return False

try:
self._debugging_message = "Starting to make scheduler summary dataframe."
pn.state.notifications.info("Making scheduler summary dataframe...", duration=0)

self._reward_df = self._scheduler.make_reward_df(self._conditions)
scheduler_summary_df = schedview.compute.scheduler.make_scheduler_summary_df(
self._scheduler,
Expand All @@ -534,8 +626,8 @@ def make_scheduler_summary_df(self):
self._scheduler_summary_df = scheduler_summary_df

tiers = self._scheduler_summary_df.tier.unique().tolist()
self.param["USER_tier"].objects = tiers
self.USER_tier = tiers[0]
self.param["widget_tier"].objects = tiers
self.widget_tier = tiers[0]
self._tier = tiers[0]
self._survey = 0
self._survey_name = self._scheduler_summary_df[
Expand Down Expand Up @@ -1347,13 +1439,13 @@ def generate_key():
# ------------------------------------------------------------ Create dashboard


def scheduler_app(date=None, scheduler_pickle=None):
def scheduler_app(date_time=None, scheduler_pickle=None):
"""Create a dashboard with grids of Param parameters, Tabulator widgets,
and Bokeh plots.
Parameters
----------
date : 'datetime' or 'date', optional
widget_datetime : 'datetime' or 'date', optional
The date/datetime of interest. The default is None.
scheduler_pickle : 'str', optional
A filepath or URL for the scheduler pickle. The default is None.
Expand All @@ -1371,8 +1463,8 @@ def scheduler_app(date=None, scheduler_pickle=None):

scheduler = Scheduler()

if date is not None:
scheduler.date = date
if date_time is not None:
scheduler.widget_datetime = date_time

if scheduler_pickle is not None:
scheduler.scheduler_fname = scheduler_pickle
Expand Down Expand Up @@ -1404,16 +1496,16 @@ def update_loading(loading):
sizing_mode="stretch_width",
styles={"background": "#048b8c"},
)
# Parameter inputs (pickle, date, tier).
# Parameter inputs (pickle, widget_datetime, tier).
sched_app[8:33, 0:21] = pn.Param(
scheduler,
parameters=["scheduler_fname", "date", "USER_tier"],
parameters=["scheduler_fname", "widget_datetime", "widget_tier"],
widgets={
"scheduler_fname": {
"widget_type": pn.widgets.TextInput,
"placeholder": "filepath or URL of pickle",
},
"date": pn.widgets.DatetimePicker,
"widget_datetime": pn.widgets.DatetimePicker,
},
name="Select pickle file, date and tier.",
)
Expand Down Expand Up @@ -1484,7 +1576,7 @@ def update_loading(loading):
{
"scheduler_fname": "scheduler",
"nside": "nside",
"date_time": "mjd",
"url_mjd": "mjd",
},
)

Expand Down
4 changes: 2 additions & 2 deletions tests/test_scheduler_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class TestSchedulerDashboard(unittest.TestCase):
observatory = ModelObservatory(init_load_length=1)
scheduler = Scheduler()
scheduler.scheduler_fname = TEST_PICKLE
scheduler._date_time = Time(Timestamp(TEST_DATE, tzinfo=ZoneInfo(DEFAULT_TIMEZONE))).mjd
scheduler._mjd = Time(Timestamp(TEST_DATE, tzinfo=ZoneInfo(DEFAULT_TIMEZONE))).mjd

def setUp(self) -> None:
bokeh.io.reset_output()
Expand All @@ -81,7 +81,7 @@ def test_scheduler_app(self):
def test_read_scheduler(self):
self.scheduler = Scheduler()
self.scheduler.scheduler_fname = TEST_PICKLE
self.scheduler._date_time = Time(Timestamp(TEST_DATE, tzinfo=ZoneInfo(DEFAULT_TIMEZONE))).mjd
self.scheduler._mjd = Time(Timestamp(TEST_DATE, tzinfo=ZoneInfo(DEFAULT_TIMEZONE))).mjd
self.scheduler.read_scheduler()
self.assertIsInstance(self.scheduler._scheduler, CoreScheduler)
self.assertIsInstance(self.scheduler._conditions, Conditions)
Expand Down

0 comments on commit d4626da

Please sign in to comment.