Source code for sdss_brain.core

# !/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Filename: core.py
# Project: sdss_brain
# Author: Brian Cherinka
# Created: Sunday, 15th March 2020 4:53:35 pm
# License: BSD 3-clause "New" or "Revised" License
# Copyright (c) 2020 Brian Cherinka
# Last Modified: Wednesday, 18th March 2020 4:08:26 pm
# Modified By: Brian Cherinka


from __future__ import print_function, division, absolute_import
import abc
from sdss_brain.config import config
from sdss_brain.exceptions import BrainError
from sdss_brain.helpers import db_type
from sdss_brain.mixins.mma import MMAccess, MMAMixIn
from astropy.io import fits


[docs]class Base(abc.ABC): ''' abstract base class for tools ''' def __new__(cls, *args, **kwargs): # set the correct MMA class if MMAccess in cls.mro(): cls._mma = MMAccess else: cls._mma = MMAMixIn return super().__new__(cls) @abc.abstractmethod def _load_object_from_file(self, data: object = None) -> None: pass @abc.abstractmethod def _load_object_from_db(self, data=None) -> None: pass @abc.abstractmethod def _load_object_from_api(self, data=None) -> None: pass
[docs]class HindBrain(Base): ''' Base class for utilizing the MMA mixin This is a convenience class with the `~sdss_brain.mixins.mma.MMAccess` already implemented. This class initializes the ``MMAccess`` and provides logic to load data based on the data_origin. It also provides a simple ``repr``. In addition to any abstractmethod from the MMA, this class contains three abstractmethods you must override when subclassing. - **_load_object_from_file**: defines data load/handling from a local file - **_load_object_from_db**: defines data load/handling from a local database - **_load_object_from_api**: defines data load/handling from a remote API Parameters ---------- data_input : str The file or name of target data to load filename : str The absolute filepath to data to load objectid : str The object identifier of the data to load mode : str The operating mode: auto, local, or remote release : str The data release of the object, e.g. "DR16" data : object Optional data to instantiate the object with download : bool If True, downloads the object locally with sdss_access ignore_db : bool If True, ignores any database connection for local access use_db : db_type, see `~sdss_brain.helpers.database.DatabaseHandler` a database ORM or connection to override the default with Attributes ---------- _db : `~sdss_brain.helpers.database.DatabaseHandler` A db handler for any loaded sdssdb ORM or db connection for the object mapped_version : str The name of survey/category in the mapped_versions dictionary ''' _db = None mapped_version = None data_origin = None def __new__(cls, *args, **kwargs): # set any work versions cls.set_work_version(config.work_versions) return super().__new__(cls) def __init__(self, data_input: str = None, filename: str = None, objectid: str = None, mode: str = None, data: object = None, release: str = None, download: bool = None, ignore_db: bool = None, use_db: db_type = None, version: str = None) -> None: # set a version for sdsswork data checked_release = release or config.release if version: self.set_work_version(version) if checked_release.lower() != 'work': raise BrainError('version is only used for "work" data. ' 'Please set the input or config release to "WORK"') else: if not self._version and checked_release.lower() == 'work': raise BrainError('You are using a "work" release but have no work versions set! ' 'Try setting a global "work_version" dict or specify a "version" input!') # initialize the MMA self._mma.__init__(self, data_input=data_input, filename=filename, objectid=objectid, mode=mode, release=release, download=download, ignore_db=ignore_db, use_db=use_db or self._db) self.data = data if self.data_origin == 'file': self._load_object_from_file(data=data) elif self.data_origin == 'db': self._load_object_from_db(data=data) elif self.data_origin == 'api': self._load_object_from_api() def __repr__(self): objname = f"objectid='{self.objectid}'" if self.objectid else f"filename='{self.filename}'" return (f"<{self.__class__.__name__} {objname}, mode='{self.mode}', " f"data_origin='{self.data_origin}'>") def __del__(self): ''' Destructor for closing open objects ''' self._close() def _close(self): ''' close open object for each data_origin ''' # close open FITS files if self.data_origin == 'file' and isinstance(self.data, fits.HDUList): self.data.close() elif self.data_origin == 'db': pass elif self.data_origin == 'api': pass def __enter__(self): ''' constructor for context manager ''' return self def __exit__(self, type, value, traceback): ''' destructor for context manager ''' self._close() return True
[docs] @classmethod def set_work_version(cls, value: dict) -> None: """ Set the work version for the given class Sets the versions used by sdswork on the given class. This takes precendence over any versions set in the global config. Input is a valid dictionary containing version names and numbers as key value pairs, e.g. `{'drpver':'v2_4_3', 'run2d':'v1_1_1', 'apred':'r12'}`. The input dictionary is merged with any values specified on the config class. Parameters ---------- values : dict, optional Dictionary of version names:numbers needed in paths, by default {} Raises ------ ValueError when input value is not a dictionary """ if type(value) != dict: raise ValueError(f'input verion must be a dictionary') # update any existing work versions with the new values wv = config.work_versions.copy() wv.update(value) cls._version = wv
[docs] @classmethod def set_database_object(cls, value: db_type) -> None: """ Sets up the MMA to create a new db handler Sets up the MMA to create a new `~sdss_brain.helpers.database.DatabaseHandler` on the given class. Valid input can be any sdssdb ORM Model, DatabaseConnection, or schema module. Parameters ---------- value : db_type The type of ``sdssdb`` database input """ # set this straight to the value since the MMA converts it into a DatabaseHandler cls._db = value
[docs]class Brain(HindBrain, MMAccess): """ The hind Brain with support for ``sdss_access`` See `~HindBrain`, `~sdss_brain.mixins.mma.MMAccess`, and `~sdss_brain.mixins.access.AccessMixIn` for detailed information. """
[docs]class BrainNoAccess(HindBrain, MMAMixIn): """ A version of `~Brain` without support for ``sdss_access`` See `~HindBrain` and `~sdss_brain.mixins.mma.MMAMixIn` for detailed information. """