<commit_before>#!/usr/bin/env python

"""
@package  ion.services.sa.instrument.agent_status_builder
@author   <NAME>
"""
from ooi.logging import log
from pyon.agent.agent import ResourceAgentClient
from pyon.core.bootstrap import IonObject
from pyon.core.exception import NotFound, Unauthorized, BadRequest

from interface.objects import ComputedValueAvailability, ComputedIntValue, ComputedDictValue, ComputedListValue
from interface.objects import AggregateStatusType, DeviceStatusType
from pyon.ion.resource import RT, PRED


class AgentStatusBuilder(object):

    def __init__(self, process=None):
        """
        the process should be the "self" of a service instance
        """
        assert process
        self.process = process


    def set_status_computed_attributes(self, computed_attrs, values_dict=None, availability=None, reason=None):
        mappings = {AggregateStatusType.AGGREGATE_COMMS    : "communications_status_roll_up",
                    AggregateStatusType.AGGREGATE_POWER    : "power_status_roll_up",
                    AggregateStatusType.AGGREGATE_DATA     : "data_status_roll_up",
                    AggregateStatusType.AGGREGATE_LOCATION : "location_status_roll_up"}

        if values_dict is None:
            values_dict = {}

        for k, a in mappings.iteritems():
            if k in values_dict:
                status = ComputedIntValue(status=availability, value=values_dict[k], reason=reason)
            else:
                status = ComputedIntValue(status=ComputedValueAvailability.NOTAVAILABLE, reason="%s not in %s" % (k, values_dict))
            setattr(computed_attrs, a, status)


    def set_status_computed_attributes_notavailable(self, computed_attrs, reason):
        self.set_status_computed_attributes(computed_attrs, None, ComputedValueAvailability.NOTAVAILABLE, reason)
        if hasattr(computed_attrs, "child_device_status"):
            computed_attrs.child_device_status = ComputedDictValue(status=ComputedValueAvailability.NOTAVAILABLE,
                                                                   reason=reason)


    def get_device_agent(self, device_id):
        if not device_id or device_id is None:
            return None, "No device ID was provided"

        try:
            h_agent = ResourceAgentClient(device_id, process=self.process)
            log.debug("got the agent client here: %s for the device id: %s and process: %s",
                      h_agent, device_id, self.process)
        except NotFound:
            return None, "Could not connect to agent instance -- may not be running"

        except Unauthorized:
            return None, "The requester does not have the proper role to access the status of this agent"

        except Exception as e:
            raise e

        return h_agent, ""


    def add_child_statuses_to_extension(self, extension_obj, child_agg_status):
        """
        Extension_obj is an extension
        device_id is the primary device
        child_device_ids is any child device ids -- will be a list, not none
        device_of_site is a dictionary of site_id -> device_id, indicating that this is a site extension of some kind
        """

        def csl(device_id_list):
            return self.compute_status_list(child_agg_status, device_id_list)

        log.debug("Building instrument and platform status dicts")
        extension_obj.computed.instrument_status = csl([dev._id for dev in extension_obj.instrument_devices])
        extension_obj.computed.platform_status   = csl([dev._id for dev in extension_obj.platform_devices])


    # get a lookup table that includes child_agg_status + the parent device status as dev_id -> {AggStatusType: DeviceStatusType}
    def get_cumulative_status_dict(self, device_id):
        h_agent, reason = self.get_device_agent(device_id)
        log.debug("Got h_agent = %s, reason = %s", h_agent, reason)
        if None is h_agent:
            return None, reason

        # read child agg status
        try:
            child_agg_status = h_agent.get_agent(['child_agg_status'])['child_agg_status']
            log.debug('get_cumulative_status_dict child_agg_status : %s', child_agg_status)

            #retrieve the platform status from the platform agent
            this_status = h_agent.get_agent(['aggstatus'])['aggstatus']
            log.debug("this_status for %s is %s", device_id, this_status)

            if not child_agg_status:
                child_agg_status = {}

            child_agg_status[device_id] = this_status

            return child_agg_status, None

        except Unauthorized, un:
            return None, un.message
        except Exception as e:
            return None, "Error getting status: %s" % e



    #return this aggregate status, reason for fail, dict of device_id -> agg status
    def get_device_rollup_statuses_and_child_agg_status(self, device_id, child_device_ids=None, warn_missing=True):

        master_status_dict, reason = self.get_cumulative_status_dict(device_id)

        log.debug("Got master_status_dict = %s, reason = %s", master_status_dict, reason)
        if None is master_status_dict:
            return None, reason, None

        # no rolling up necessary for instruments (no child_device_ids list)
        if None is child_device_ids:
            return master_status_dict[device_id], None, None

        # if child device ids is not none, the developer had better be giving us a list
        assert isinstance(child_device_ids, list)


        rollup_statuses = {}
        # 1. loop through the items in this device status,
        # 2. append all the statuses of that type from child devices,
        # 3. crush
        for stype, svalue in AggregateStatusType._str_map.iteritems():
            if not device_id in master_status_dict:
                log.warn("parent device %s not found in master_status_dict", device_id)
                this_status = {}
            else:
                this_status = master_status_dict[device_id]

            one_type_status_list = [this_status.get(stype, DeviceStatusType.STATUS_UNKNOWN)]
            for child_device_id in child_device_ids:
                if not child_device_id in master_status_dict:
                    if warn_missing:
                        log.warn("Child device '%s' of parent device '%s' not found in master_status_dict",
                                 child_device_id, device_id)
                else:
                    # get the dict of AggregateStatusType -> DeviceStatusType
                    child_statuses = master_status_dict[child_device_id]
                    one_type_status_list.append(child_statuses.get(stype, DeviceStatusType.STATUS_UNKNOWN))

            rollup_statuses[stype] = self._crush_status_list(one_type_status_list)

        log.debug("combined_status is %s", rollup_statuses)

        return rollup_statuses, None, master_status_dict



    # child_device_ids is None for instruments, a list for platforms
    def add_device_rollup_statuses_to_computed_attributes(self, device_id, extension_computed, child_device_ids=None):

        rollup_statuses, reason, child_agg_status = self.get_device_rollup_statuses_and_child_agg_status(device_id,
                                                                                                     child_device_ids)


        if None is rollup_statuses:
            self.set_status_computed_attributes_notavailable(extension_computed, reason)

            if hasattr(extension_computed, "child_device_status"):
                extension_computed.child_device_status = ComputedDictValue(status=ComputedValueAvailability.NOTAVAILABLE,
                                                                           reason=reason)
            return None

        # no rolling up necessary for instruments (no child_device_ids list)
        if None is child_device_ids:
            self.set_status_computed_attributes(extension_computed, rollup_statuses,
                                                ComputedValueAvailability.PROVIDED)
            return None

        # get child agg status if we can set child_device_status
        # todo: split into instrument and platform<commit_msg>fix governance test<commit_after>#!/usr/bin/env python

"""
@package  ion.services.sa.instrument.agent_status_builder
@author   <NAME>
"""
from ooi.logging import log
from pyon.agent.agent import ResourceAgentClient
from pyon.core.bootstrap import IonObject
from pyon.core.exception import NotFound, Unauthorized, BadRequest

from interface.objects import ComputedValueAvailability, ComputedIntValue, ComputedDictValue, ComputedListValue
from interface.objects import AggregateStatusType, DeviceStatusType
from pyon.ion.resource import RT, PRED


class AgentStatusBuilder(object):

    def __init__(self, process=None):
        """
        the process should be the "self" of a service instance
        """
        assert process
        self.process = process


    def set_status_computed_attributes(self, computed_attrs, values_dict=None, availability=None, reason=None):
        mappings = {AggregateStatusType.AGGREGATE_COMMS    : "communications_status_roll_up",
                    AggregateStatusType.AGGREGATE_POWER    : "power_status_roll_up",
                    AggregateStatusType.AGGREGATE_DATA     : "data_status_roll_up",
                    AggregateStatusType.AGGREGATE_LOCATION : "location_status_roll_up"}

        if values_dict is None:
            values_dict = {}

        for k, a in mappings.iteritems():
            if k in values_dict:
                status = ComputedIntValue(status=availability, value=values_dict[k], reason=reason)
            else:
                if None is reason: reason = "%s not in %s" % (k, values_dict)
                status = ComputedIntValue(status=ComputedValueAvailability.NOTAVAILABLE, reason=reason)
            setattr(computed_attrs, a, status)


    def set_status_computed_attributes_notavailable(self, computed_attrs, reason):
        self.set_status_computed_attributes(computed_attrs, None, ComputedValueAvailability.NOTAVAILABLE, reason)
        if hasattr(computed_attrs, "child_device_status"):
            computed_attrs.child_device_status = ComputedDictValue(status=ComputedValueAvailability.NOTAVAILABLE,
                                                                   reason=reason)


    def get_device_agent(self, device_id):
        if not device_id or device_id is None:
            return None, "No device ID was provided"

        try:
            h_agent = ResourceAgentClient(device_id, process=self.process)
            log.debug("got the agent client here: %s for the device id: %s and process: %s",
                      h_agent, device_id, self.process)
        except NotFound:
            return None, "Could not connect to agent instance -- may not be running"

        except Unauthorized:
            return None, "The requester does not have the proper role to access the status of this agent"

        except Exception as e:
            raise e

        return h_agent, ""


    def add_child_statuses_to_extension(self, extension_obj, child_agg_status):
        """
        Extension_obj is an extension
        device_id is the primary device
        child_device_ids is any child device ids -- will be a list, not none
        device_of_site is a dictionary of site_id -> device_id, indicating that this is a site extension of some kind
        """

        def csl(device_id_list):
            return self.compute_status_list(child_agg_status, device_id_list)

        log.debug("Building instrument and platform status dicts")
        extension_obj.computed.instrument_status = csl([dev._id for dev in extension_obj.instrument_devices])
        extension_obj.computed.platform_status   = csl([dev._id for dev in extension_obj.platform_devices])


    # get a lookup table that includes child_agg_status + the parent device status as dev_id -> {AggStatusType: DeviceStatusType}
    def get_cumulative_status_dict(self, device_id):
        h_agent, reason = self.get_device_agent(device_id)
        log.debug("Got h_agent = %s, reason = %s", h_agent, reason)
        if None is h_agent:
            return None, reason

        # read child agg status
        try:
            #retrieve the platform status from the platform agent
            this_status = h_agent.get_agent(['aggstatus'])['aggstatus']
            log.debug("this_status for %s is %s", device_id, this_status)

        except Unauthorized, un:
            return None, un.message
        except Exception as e:
            return None, "Error getting status: %s" % e

        one_status = {device_id: this_status}

        try:
            child_agg_status = h_agent.get_agent(['child_agg_status'])['child_agg_status']
            log.debug('get_cumulative_status_dict child_agg_status : %s', child_agg_status)

            if not child_agg_status:
                child_agg_status = {}

            return dict(child_agg_status.items() + one_status.items()), None

        except Unauthorized, un:
            return one_status, un.message
        except Exception as e:
            return one_status, "Error getting status: %s" % e



    #return this aggregate status, reason for fail, dict of device_id -> agg status
    def get_device_rollup_statuses_and_child_agg_status(self, device_id, child_device_ids=None, warn_missing=True):

        master_status_dict, reason = self.get_cumulative_status_dict(device_id)

        log.debug("Got master_status_dict = %s, reason = %s", master_status_dict, reason)
        if None is master_status_dict:
            return None, reason, None

        # no rolling up necessary for instruments (no child_device_ids list)
        if None is child_device_ids:
            return master_status_dict[device_id], None, None

        # if child device ids is not none, the developer had better be giving us a list
        assert isinstance(child_device_ids, list)


        rollup_statuses = {}
        # 1. loop through the items in this device status,
        # 2. append all the statuses of that type from child devices,
        # 3. crush
        for stype, svalue in AggregateStatusType._str_map.iteritems():
            if not device_id in master_status_dict:
                log.warn("parent device %s not found in master_status_dict", device_id)
                this_status = {}
            else:
                this_status = master_status_dict[device_id]

            one_type_status_list = [this_status.get(stype, DeviceStatusType.STATUS_UNKNOWN)]
            for child_device_id in child_device_ids:
                if not child_device_id in master_status_dict:
                    if warn_missing:
                        log.warn("Child device '%s' of parent device '%s' not found in master_status_dict",
                                 child_device_id, device_id)
                else:
                    # get the dict of AggregateStatusType -> DeviceStatusType
                    child_statuses = master_status_dict[child_device_id]
                    one_type_status_list.append(child_statuses.get(stype, DeviceStatusType.STATUS_UNKNOWN))

            rollup_statuses[stype] = self._crush_status_list(one_type_status_list)

        log.debug("combined_status is %s", rollup_statuses)

        return rollup_statuses, None, master_status_dict



    # child_device_ids is None for instruments, a list for platforms
    def add_device_rollup_statuses_to_computed_attributes(self, device_id, extension_computed, child_device_ids=None):

        rollup_statuses, reason, child_agg_status = self.get_device_rollup_statuses_and_child_agg_status(device_id,
                                                                                                     child_device_ids)
        log.debug("Got rollup_statuses = %s, reason = %s", rollup_statuses, reason)

        if None is rollup_statuses:
            log.debug("setting status notavailable")
            self.set_status_computed_attributes_notavailable(extension_computed, reason)

            if hasattr(extension_computed, "child_device_status"):
                extension_computed.child_device_status = ComputedDictValue(status=ComputedValueAvailability.NOTAVAILABLE,
                                                                           reason=reason)
            return None

        # no rolling up necessary for instruments (no child_device_ids list)
        if None is child_device_ids:
            self.set_status_computed_attributes(extension_computed, rollup_statuses,
                                                ComputedValueAvailability.PROVIDED)
            return None

        # get child agg status if we can set child_device_status
        # todo: split into instrument and platform
{'fraction_non_alphanumeric': 0.06442961020383836, 'fraction_numerical': 0.000357611157468113, 'mean_word_length': 2.500730231587732, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2002068', 'n_tokens_mistral': 4571, 'n_tokens_neox': 4401, 'n_words': 1201}