diagnostic.py 5.09 KB
Newer Older
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
# coding=utf-8
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    """
    Base class for the diagnostics. Provides a common interface for them and also
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    has a mechanism that allows diagnostic retrieval by name.
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed

    :param data_manager: data manager that will be used to store and retrieve the necessary data
    :type data_manager: DataManager
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    """
    alias = None
    """
    Alias to call the diagnostic. Must be overridden at the derived clases
    """
    _diag_list = dict()
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    def __init__(self, data_manager):
        self.data_manager = data_manager
        self.required_vars = []
        self.generated_vars = []
        self.can_run_multiple_instances = True

Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    def __repr__(self):
        return str(self)

Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        """
        Register a new diagnostic using the given alias. It must be call using the derived class.
        :param cls: diagnostic class to register
        :type cls: Diagnostic
        """
        if not issubclass(cls, Diagnostic):
            raise ValueError('Class {0} must be derived from Diagnostic'.format(cls))
        if cls.alias is None:
            raise ValueError('Diagnostic class {0} must have defined an alias'.format(cls))
        Diagnostic._diag_list[cls.alias] = cls
    # noinspection PyProtectedMember
    @staticmethod
    def get_diagnostic(name):
        """
        Return the class for a diagnostic given its name

        :param name: diagnostic alias
        :type name: str
        :return: the selected Diagnostic class, None if name can not be found
        :rtype: Diagnostic
        """
        if name in Diagnostic._diag_list.keys():
            return Diagnostic._diag_list[name]
    def send_file(self, filetosend, domain, var, startdate, member, chunk=None, grid=None, region=None,
                  box=None, rename_var=None, frequency=None, year=None, date_str=None, move_old=False,
                  vartype=VarType.MEAN):
        """

        :param filetosend:
        :param domain:
        :type domain: Domain
        :param var:
        :param startdate:
        :param member:
        :param chunk:
        :param grid:
        :param region:
        :param box:
        :param rename_var:
        :param frequency:
        :param year:
        :param date_str:
        :param move_old:
        :return:
        """
        self.data_manager.send_file(filetosend, domain, var, startdate, member, chunk, grid, region,
                                    box, rename_var, frequency, year, date_str, move_old, diagnostic=self,
                                    vartype=vartype)
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        """
        Calculates the diagnostic and stores the output

        Must be implemented by derived classes
        """
        raise NotImplementedError("Class must override compute method")
    def generate_jobs(cls, diags, options):
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        """
        Generate the instances of the diagnostics that will be run by the manager

        Must be implemented by derived classes.

        :param diags: diagnostics manager
        :type diags: Diags
        :param options: list of strings containing the options passed to the diagnostic
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        :type options: list[str]
Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
        :return:
        """
        raise NotImplementedError("Class must override generate_jobs class method")

    @staticmethod
    def process_options(options, options_available):
        processed = dict()
        options = options[1:]
        for x in range(len(options_available)):
            option_definition = options_available[x]
            if len(options) <= x:
                option_value = ''
            else:
                option_value = options[x]
            processed[option_definition.name] = option_definition.parse(option_value)
        return processed

Javier Vegas-Regidor's avatar
Javier Vegas-Regidor committed
    def __str__(self):
        """
        Must be implemented by derived classes
        :return:
        """
        return 'Developer must override base class __str__ method'


class DiagnosticOption(object):

    def __init__(self, name, default_value=None):
        self.name = name
        self.default_value = default_value

    def parse(self, option_value):
        option_value = self.check_default(option_value)
        return option_value

    def check_default(self, option_value):
        if option_value == '':
            if self.default_value is None:
                raise DiagnosticOptionError('Option {0} is not optional')
            else:
                return self.default_value
        return option_value


class DiagnosticFloatOption(DiagnosticOption):
    def parse(self, option_value):
        return float(self.check_default(option_value))


class DiagnosticIntOption(DiagnosticOption):
    def parse(self, option_value):
        return int(self.check_default(option_value))


class DiagnosticBoolOption(DiagnosticOption):
    def parse(self, option_value):
        option_value = self.check_default(option_value)
        if isinstance(option_value, bool):
            return option_value
        else:
            return option_value.lower() in ('true', 't', 'yes')


class DiagnosticOptionError(Exception):
    pass