Source code for panoptes.pocs.scheduler

"""Scheduler package helpers.

Provides factory functions to construct a Scheduler instance and its list of
constraints from PANOPTES configuration. The module avoids importing concrete
classes directly; instead it dynamically loads the configured scheduler and
constraints using panoptes.utils.library.load_module.
"""

from pathlib import Path

from panoptes.utils import error
from panoptes.utils.config.client import get_config
from panoptes.utils.library import load_module

from panoptes.pocs.scheduler.constraint import BaseConstraint
from panoptes.pocs.utils.location import create_location_from_config, download_iers_a_file
from panoptes.pocs.utils.logger import get_logger

logger = get_logger()


[docs] def create_scheduler_from_config(config=None, observer=None, iers_url=None, *args, **kwargs): """Construct a Scheduler instance based on configuration. Reads the 'scheduler' section from the config, ensures IERS tables are configured, resolves the observing site (Observer), and loads the configured Scheduler class and fields file. Any configured constraints are built via create_constraints_from_config and passed to the Scheduler constructor. Args: config (dict | None): Scheduler configuration; if None, fetched via get_config. observer (astroplan.Observer | None): Existing Observer; if None, created from config. iers_url (str | None): Optional override for the IERS A table URL. *args: Additional positional args forwarded to the Scheduler constructor. **kwargs: Additional keyword args forwarded to the Scheduler constructor. Returns: panoptes.pocs.scheduler.dispatch.Scheduler: The constructed Scheduler instance. Raises: panoptes.utils.error.NotFound: If the fields file cannot be located or module not found. """ scheduler_config = config or get_config("scheduler", default=None) logger.info(f"scheduler_config: {scheduler_config!r}") if scheduler_config is None or len(scheduler_config) == 0: logger.info("No scheduler in config") return None download_iers_a_file(iers_url=iers_url) if not observer: logger.debug("No Observer provided, creating location from config.") site_details = create_location_from_config() observer = site_details.observer # Read the targets from the file fields_file = Path(scheduler_config.get("fields_file", "simple.yaml")) base_dir = Path(str(get_config("directories.base", default="."))) fields_dir = Path(str(get_config("directories.fields", default="./conf_files/fields"))) fields_path = base_dir / fields_dir / fields_file logger.info(f"Creating fields from path: {fields_path}") if fields_path.exists(): scheduler_type = scheduler_config.get("type", "panoptes.pocs.scheduler.dispatch") try: # Load the required module module = load_module(f"{scheduler_type}") constraints = create_constraints_from_config(config=scheduler_config) # Create the Scheduler instance pocs_scheduler = module.Scheduler( observer, fields_file=str(fields_path), constraints=constraints, *args, **kwargs ) logger.debug("Scheduler created") except error.NotFound as e: raise error.NotFound(msg=e) else: raise error.NotFound(msg=f"Fields file does not exist: {fields_path=!r}") return pocs_scheduler
[docs] def create_constraints_from_config(config=None) -> list[BaseConstraint]: """Build a list of constraint instances from scheduler configuration. Reads the 'scheduler.constraints' list from the provided config (or global config if None), resolves each constraint's dotted Python path (falling back to panoptes.pocs.scheduler.constraint.<Name> when a short name is given), and instantiates the constraint with any provided options. Args: config (dict | None): Scheduler configuration dict; if None, use get_config('scheduler'). Returns: list[BaseConstraint]: Instantiated constraint objects ready to pass to the Scheduler. """ config = config or get_config("scheduler", default=dict()) constraints = list() for constraint_config in config.get("constraints", list()): name = constraint_config["name"] if len(name.split(".")) < 2: name = f"panoptes.pocs.scheduler.constraint.{name}" try: constraint_module = load_module(name) except error.NotFound: logger.warning(f"Invalid constraint config given: {constraint_config=}") else: options = constraint_config.get("options", dict()) constraints.append(constraint_module(**options)) return constraints