Source code for uataq._laboratory

"""
A factory class for creating site objects with instruments from a configuration file.
"""

import importlib.resources
import json
from copy import deepcopy
from typing import Literal

from uataq import sites
from uataq.instruments import InstrumentEnsemble


[docs] class Laboratory: """ Factory class for creating site objects from config file. This class provides methods for creating site objects from a configuration file. The configuration file should contain information about the sites and their instruments. Attributes ---------- config : dict The configuration data, keyed by site ID. sites : list[str] The site IDs in the configuration. instruments : list[str] The names of the instruments configured at any site. Methods ------- get_site(SID): Returns a site object for the specified site ID. """
[docs] def __init__(self, config: str | dict): """ Initialize the Laboratory class. Parameters ---------- config : str | dict The path to the configuration file or a dictionary containing the configuration data. Raises ------ ValueError If the configuration data is invalid. """ if isinstance(config, str): # config is a file path with open(config) as config_file: self.config = json.load(config_file) elif isinstance(config, dict): # config is a dictionary self.config = config else: raise ValueError( "Invalid configuration data. Must be a file path or dictionary." ) self.sites = list(self.config.keys()) self.instruments = list( set( [ instrument for SID in self.sites for instrument in self.config[SID].get("instruments", {}) ] ) )
def __repr__(self) -> str: config = json.dumps(self.config, indent=4) return f"Laboratory(config={config})" def __str__(self) -> Literal["UATAQ Laboratory"]: return "UATAQ Laboratory"
[docs] def get_site(self, SID: str) -> sites.Site: """ Return site object from config file Parameters ---------- SID : str The site ID. Returns ------- sites.Site A site object. Raises ------ ValueError If the site ID is not found in the configuration file. ValueError If no instruments are found for the specified site. """ SID = SID.upper() config = deepcopy(self.config).get(SID) if config is None: raise ValueError(f"Site '{SID}' not found in the configuration file.") # Get site class based on config is_mobile = config.get("is_mobile", False) SiteClass = sites.MobileSite if is_mobile else sites.Site # If logger are specified at the site level, then pass them to the instrument ensemble. # If both are specified, the instrument ensemble will use the loggers specified at the instrument level. loggers = config.pop("loggers", None) # Build instrument ensemble based on config instrument_configs = config.pop("instruments", None) if instrument_configs is None: raise ValueError(f"No instruments found for site '{SID}'.") instruments = InstrumentEnsemble(SID, instrument_configs, loggers) return SiteClass(SID, config, instruments)
config = json.loads( importlib.resources.files("uataq").joinpath("config.json").read_text() ) laboratory = Laboratory(config)
[docs] def get_site(SID: str) -> sites.Site: """ Return site object from config file Parameters ---------- SID : str The site ID. Returns ------- sites.Site A site object: a :class:`~uataq.sites.MobileSite` for a train or a bus, else a :class:`~uataq.sites.Site`. Raises ------ ValueError If the site ID is not in the configuration file (see :doc:`/guides/errors`). Examples -------- The case of the ID does not matter: >>> import uataq >>> uataq.get_site("wbb").SID 'WBB' >>> type(uataq.get_site("TRX01")).__name__ 'MobileSite' """ return laboratory.get_site(SID)