Source code for sofia_redux.scan.info.base

# Licensed under a 3-clause BSD style license - see LICENSE.rst

from abc import ABC
from astropy import units
from copy import deepcopy
import numpy as np

from sofia_redux.scan.configuration.configuration import Configuration
from sofia_redux.scan.utilities.bracketed_values import BracketedValues

__all__ = ['InfoBase']


[docs] class InfoBase(ABC): UNKNOWN_STRING_VALUE = 'UNKNOWN' UNKNOWN_INT_VALUE = -9999 UNKNOWN_FLOAT_VALUE = -9999.0 def __init__(self): """ Initialize a basic informational object. This provides the template from which all specific information objects are founded. All contain a configuration from which they extract FITS header options and process accordingly. Following a reduction, they should also be able to parse their information back to a FITS header for inclusion in the final output product and may or may not perform additional operation on scans. """ self.configuration = None self.scan_applied = False self.scan = None self.configuration_applied = False @property def referenced_attributes(self): """ Return a set of attribute names that should be referenced during copy. Returns ------- set (str) """ return {'configuration', 'scan'} @property def log_id(self): """ Return the string log ID for the info. Returns ------- str """ return 'base' @property def log_prefix(self): """ Return the log prefix of the information object. This is used to specify table entries for extraction. Returns ------- str """ return f'{self.log_id.lower().strip()}.'
[docs] def copy(self): """ Return a full copy of the information. Returns ------- InfoBase """ referenced_only = self.referenced_attributes new = self.__class__() for attribute, value in self.__dict__.items(): if attribute in referenced_only: setattr(new, attribute, value) elif hasattr(value, 'copy'): setattr(new, attribute, value.copy()) else: setattr(new, attribute, deepcopy(value)) return new
@property def options(self): """ Return the FITS header options. Returns ------- FitsOptions """ if self.configuration is None or not self.configuration.fits.enabled: return None return self.configuration.fits
[docs] def set_configuration(self, configuration): """ Set the configuration for the information. Parameters ---------- configuration : Configuration Returns ------- None """ if not isinstance(configuration, Configuration): raise ValueError(f"configuration must be a " f"{Configuration} instance.") self.configuration = configuration self.apply_configuration() if self.scan_applied: self.validate()
[docs] def set_scan(self, scan): """ Set the scan applicable to the information. Parameters ---------- scan : Scan Returns ------- None """ self.apply_scan(scan) if self.configuration_applied: self.validate()
def __str__(self): """ Return a string representation of the info base. Returns ------- str """ s = '' for key, value in self.__dict__.items(): s += f'{key}: {value}\n' return s
[docs] def apply_configuration(self): """ Apply the configuration to the information. Returns ------- None """ self.configuration_applied = True
[docs] def apply_scan(self, scan): """ Apply scan information to the information. Parameters ---------- scan : Scan Returns ------- None """ self.scan_applied = True self.scan = scan
[docs] def validate(self): # pragma: no cover """ Validate the data obtained from FITS header and configuration Returns ------- None """ pass
[docs] def validate_scan(self, scan): # pragma: no cover """ Validate scan information with *THIS* information. Parameters ---------- scan : Scan Returns ------- None """ pass
[docs] def parse_image_header(self, header): # pragma: no cover """ Apply settings from a FITS image header. Parameters ---------- header : astropy.fits.Header Returns ------- None """ pass
[docs] def edit_image_header(self, header, scans=None): # pragma: no cover """ Edit an image header with available information. Parameters ---------- header : astropy.io.fits.header.Header The FITS header to edit. scans : list (Scan), optional A list of scans to use during editing. Returns ------- None """ pass
[docs] def edit_scan_header(self, header, scans=None): # pragma: no cover """ Edit a scan header with available information. Parameters ---------- header : astropy.io.fits.header.Header The FITS header to edit. scans : list (Scan), optional A list of scans to use during editing. Returns ------- None """ pass
[docs] def merge(self, last): """ Set end of any range values to the last ranged end value. Parameters ---------- last : InfoBase Returns ------- None """ for attribute, value in self.__dict__.items(): if isinstance(value, BracketedValues): last_value = getattr(last, attribute, None) if isinstance(last_value, BracketedValues): value.end = last_value.end
[docs] @classmethod def valid_header_value(cls, value): """ Check whether a header value is valid. Parameters ---------- value : None or bool or int or str or float or Quantity Returns ------- valid : bool """ if value is None: return False if isinstance(value, bool): return True elif isinstance(value, int): return value != cls.UNKNOWN_INT_VALUE elif isinstance(value, str): return value != cls.UNKNOWN_STRING_VALUE elif isinstance(value, float): return (value != cls.UNKNOWN_FLOAT_VALUE) and np.isfinite(value) elif isinstance(value, units.Quantity): x = value.value return (x != cls.UNKNOWN_FLOAT_VALUE) and np.isfinite(x) else: return False
[docs] def get_table_entry(self, name): """ Given a name, return the parameter stored in the information object. Note that names do not exactly match to attribute names. Parameters ---------- name : str Returns ------- value """ for key, value in self.__dict__.items(): if key == name: return value else: return None