Esempio n. 1
0
 def open_icat(self):
     """ Try to open an ICAT session, if we don't have one already. """
     try:
         if self.icat is None:
             self.icat = ICATCommunication(**self.kwargs)
     except Exception as e:
         logger.error("Failed to connect to ICAT: %s - %s" %
                      (type(e).__name, e))
         raise ICATConnectionException()
Esempio n. 2
0
    def get_current_and_upcoming_variables(self, instrument_name):
        """
        Fetches the instrument variables for:
        - The next run number
        - Upcoming run numbers
        - Upcoming known experiments
        as a tuple of (current_variables, upcoming_variables_by_run, upcoming_variables_by_experiment)
        """
        instrument = InstrumentUtils().get_instrument(instrument_name)
        completed_status = StatusUtils().get_completed()

        # First, we find the latest run number to determine what's upcoming.
        try:
            latest_completed_run_number = ReductionRun.objects.filter(
                instrument=instrument, run_version=0,
                status=completed_status).order_by(
                    '-run_number').first().run_number
        except AttributeError:
            latest_completed_run_number = 1

        # Then we find all the upcoming runs and force updating of all subsequent variables.
        upcoming_run_variables = InstrumentVariable.objects.filter(
            instrument=instrument,
            start_run__isnull=False,
            start_run__gt=latest_completed_run_number +
            1).order_by('start_run')
        upcoming_run_numbers = set(
            [var.start_run for var in upcoming_run_variables])
        [
            self.show_variables_for_run(instrument_name, run_number)
            for run_number in upcoming_run_numbers
        ]

        # Get the most recent run variables.
        current_variables = self.show_variables_for_run(
            instrument_name, latest_completed_run_number)
        if not current_variables:
            # If no variables are saved, we'll use the default ones, and set them while we're at it.
            current_variables = self.get_default_variables(instrument_name)
            self.set_variables_for_runs(instrument_name, current_variables)

        # And then select the variables for all subsequent run numbers; collect the immediate upcoming variables and all subsequent sets.
        upcoming_variables_by_run = self.show_variables_for_run(
            instrument_name, latest_completed_run_number + 1)
        upcoming_variables_by_run += list(
            InstrumentVariable.objects.filter(
                instrument=instrument,
                start_run__in=upcoming_run_numbers).order_by('start_run'))

        # Get the upcoming experiments, and then select all variables for these experiments.
        upcoming_experiments = []
        with ICATCommunication() as icat:
            upcoming_experiments = list(
                icat.get_upcoming_experiments_for_instrument(instrument_name))
        upcoming_variables_by_experiment = InstrumentVariable.objects.filter(
            instrument=instrument,
            experiment_reference__in=upcoming_experiments).order_by(
                'experiment_reference')

        return current_variables, upcoming_variables_by_run, upcoming_variables_by_experiment
Esempio n. 3
0
 def open_icat(self):
     """ Try to open an ICAT session, if we don't have one already. """
     try:
         if self.icat is None:
             self.icat = ICATCommunication(**self.kwargs)
     except Exception as e:
         logger.error("Failed to connect to ICAT: %s - %s" % (type(e).__name, e))
         raise ICATConnectionException()
Esempio n. 4
0
    def get_current_and_upcoming_variables(self,
                                           instrument_name,
                                           last_run_object=None):
        """
        :param instrument_name: The name of the instrument
        :param last_run_object: Optionally provide an object of the last run on the instrument
        Fetches the instrument variables for:
        - The next run number
        - Upcoming run numbers
        - Upcoming known experiments
        as a tuple of
        (current_variables, upcoming_variables_by_run, upcoming_variables_by_experiment)
        """
        instrument = InstrumentUtils().get_instrument(instrument_name)
        completed_status = StatusUtils().get_completed()

        # First, we find the latest run number to determine what's upcoming.
        try:
            if last_run_object and last_run_object.status.value_verbose(
            ) == 'Completed':
                latest_completed_run_number = last_run_object.run_number
            else:
                latest_completed_run_number = ReductionRun.objects.filter(instrument=instrument,
                                                                          run_version=0,
                                                                          status=completed_status)\
                    .order_by('-run_number').first().run_number
        except AttributeError:
            latest_completed_run_number = 1

        # Then we find all the upcoming runs and force updating of all subsequent variables.
        # pylint:disable=no-member
        upcoming_run_variables = InstrumentVariable.objects.\
            filter(instrument=instrument,
                   start_run__isnull=False,
                   start_run__gt=latest_completed_run_number + 1).order_by('start_run')

        upcoming_run_numbers = set(
            [var.start_run for var in upcoming_run_variables])
        # pylint:disable=expression-not-assigned
        [
            self.show_variables_for_run(instrument_name, run_number)
            for run_number in upcoming_run_numbers
        ]

        # Get the most recent run variables.
        current_variables = self.show_variables_for_run(
            instrument_name, latest_completed_run_number)
        if not current_variables:
            # If no variables are saved, we'll use the default ones, and set them while we're at it.
            current_variables = self.get_default_variables(instrument_name)
            self.set_variables_for_runs(instrument_name, current_variables)

        # And then select the variables for all subsequent run numbers;
        # collect the immediate upcoming variables and all subsequent sets.
        upcoming_variables_by_run = self.show_variables_for_run(
            instrument_name, latest_completed_run_number + 1)
        # pylint:disable=no-member
        upcoming_variables_by_run += list(
            InstrumentVariable.objects.filter(
                instrument=instrument,
                start_run__in=upcoming_run_numbers).order_by('start_run'))

        # Get the upcoming experiments, and then select all variables for these experiments.
        upcoming_experiments = []
        with ICATCommunication() as icat:
            upcoming_experiments = list(
                icat.get_upcoming_experiments_for_instrument(instrument_name))

        # pylint:disable=line-too-long,no-member
        upcoming_variables_by_experiment = InstrumentVariable.objects.\
            filter(instrument=instrument,
                   experiment_reference__in=upcoming_experiments).order_by('experiment_reference')

        return current_variables, upcoming_variables_by_run, upcoming_variables_by_experiment
Esempio n. 5
0
class ICATCache(object):
    """
    A wrapper for ICATCommunication that caches information, and in the case of ICAT failure will try to use this cache.
    It stores Cache models in the database, and will get all fields from ICATCommunication if a model is requested but has expired or doesn't exist.
    Most of the methods it wraps from ICATCommunication are templated rather than declared explicitly; see below.    
    """
    def __init__(self, **kwargs):
        self.kwargs = kwargs
        self.icat = None
        self.cache_lifetime = CACHE_LIFETIME
    
    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        if self.icat is not None:
            self.icat.__exit__(type, value, traceback)
            
    def open_icat(self):
        """ Try to open an ICAT session, if we don't have one already. """
        try:
            if self.icat is None:
                self.icat = ICATCommunication(**self.kwargs)
        except Exception as e:
            logger.error("Failed to connect to ICAT: %s - %s" % (type(e).__name, e))
            raise ICATConnectionException()
            
    def is_valid(self, cache_obj):
        """ Check whether a cache object is fresh and is not None. """
        return cache_obj and cache_obj.created + datetime.timedelta(seconds=self.cache_lifetime) > timezone.now()
        
    def to_list(self, l):
        return ",".join(map(str, l))
            
    def cull_invalid(self, list):
        """ Removes all objects in the list that have expired. """
        rlist = []
        for obj in list:
            obj.delete() if not self.is_valid(obj) else rlist.append(obj)
        return rlist
        
    def update_cache(self, obj_type, obj_id):
        """
        Adds an object of type obj_type and id obj_id to the cache - querying ICAT - and returns the object.
        E.g., obj_type = InstrumentCache, obj_id = "WISH".
        """
        self.open_icat() # Open an ICAT session if we don't have one open.
        
        if obj_type != ExperimentCache:
            # Check func_list for the attributes that each model should have, and the corresponding ICATCommunication function to query for it; call it for each, building a dict, and then splice it into the constructor kwargs.
            new_obj = obj_type(**{attr:(getattr(self.icat, func)(obj_id) if typ is None else self.to_list(getattr(self.icat, func)(obj_id))) for (func, model, attr, typ) in func_list if model == obj_type})
        else:
            # In this case, ICATCommunication returns all the ExperimentCache fields in one query, so we splice that into the constructor.
            new_obj = obj_type(**{attr:str(val) for attr,val in self.icat.get_experiment_details(obj_id).iteritems() if attr is not "reference_number"})
        new_obj.id_name = obj_id
        new_obj.save()
        return new_obj
        
    def check_cache(self, obj_type, obj_id):
        """
        Checks the cache for an object of type obj_type and id obj_id -  querying for a new one if there isn't a fresh copy - and returns it.
        If ICAT is unavailable, use a local copy if it exists. If we can't use anything, return None.
        """
        in_cache = obj_type.objects.filter(id_name = obj_id).order_by("-created")
        ret_obj = None
        if in_cache:
            ret_obj = in_cache[0]
        if not self.is_valid(ret_obj):
            try:
                ret_obj = self.update_cache(obj_type, obj_id)
                self.cull_invalid(in_cache)
            except ICATConnectionException:
                pass
                
        return ret_obj
        
    def get_valid_experiments_for_instruments(self, user_number, instruments):
        experiment_dict = {}
        user_experiments = set(self.get_associated_experiments(user_number))
        for instrument_name in instruments:
            instrument_experiments = self.get_valid_experiments_for_instrument(instrument_name)
            experiment_dict[instrument_name] = list(user_experiments.intersection(instrument_experiments))
        return experiment_dict
        
    def get_experiment_details(self, experiment_number):
        experiment = self.check_cache(ExperimentCache, experiment_number)
        return experiment.__dict__
Esempio n. 6
0
class ICATCache(object):
    """
    A wrapper for ICATCommunication that caches information, and in the case of ICAT failure will try to use this cache.
    It stores Cache models in the database, and will get all fields from ICATCommunication if a model is requested but has expired or doesn't exist.
    Most of the methods it wraps from ICATCommunication are templated rather than declared explicitly; see below.    
    """
    def __init__(self, **kwargs):
        self.kwargs = kwargs
        self.icat = None
        self.cache_lifetime = CACHE_LIFETIME

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        if self.icat is not None:
            self.icat.__exit__(type, value, traceback)

    def open_icat(self):
        """ Try to open an ICAT session, if we don't have one already. """
        try:
            if self.icat is None:
                self.icat = ICATCommunication(**self.kwargs)
        except Exception as e:
            logger.error("Failed to connect to ICAT: %s - %s" %
                         (type(e).__name, e))
            raise ICATConnectionException()

    def is_valid(self, cache_obj):
        """ Check whether a cache object is fresh and is not None. """
        return cache_obj and cache_obj.created + datetime.timedelta(
            seconds=self.cache_lifetime) > timezone.now()

    def to_list(self, l):
        return ",".join(map(str, l))

    def cull_invalid(self, list):
        """ Removes all objects in the list that have expired. """
        rlist = []
        for obj in list:
            obj.delete() if not self.is_valid(obj) else rlist.append(obj)
        return rlist

    def update_cache(self, obj_type, obj_id):
        """
        Adds an object of type obj_type and id obj_id to the cache - querying ICAT - and returns the object.
        E.g., obj_type = InstrumentCache, obj_id = "WISH".
        """
        self.open_icat()  # Open an ICAT session if we don't have one open.

        if obj_type != ExperimentCache:
            # Check func_list for the attributes that each model should have, and the corresponding ICATCommunication function to query for it; call it for each, building a dict, and then splice it into the constructor kwargs.
            new_obj = obj_type(
                **{
                    attr: (getattr(self.icat, func)(obj_id) if typ is None else
                           self.to_list(getattr(self.icat, func)(obj_id)))
                    for (func, model, attr, typ) in func_list
                    if model == obj_type
                })
        else:
            # In this case, ICATCommunication returns all the ExperimentCache fields in one query, so we splice that into the constructor.
            new_obj = obj_type(
                **{
                    attr: str(val)
                    for attr, val in self.icat.get_experiment_details(
                        obj_id).iteritems() if attr is not "reference_number"
                })
        new_obj.id_name = obj_id
        new_obj.save()
        return new_obj

    def check_cache(self, obj_type, obj_id):
        """
        Checks the cache for an object of type obj_type and id obj_id -  querying for a new one if there isn't a fresh copy - and returns it.
        If ICAT is unavailable, use a local copy if it exists. If we can't use anything, return None.
        """
        in_cache = obj_type.objects.filter(id_name=obj_id).order_by("-created")
        ret_obj = None
        if in_cache:
            ret_obj = in_cache[0]
        if not self.is_valid(ret_obj):
            try:
                ret_obj = self.update_cache(obj_type, obj_id)
                self.cull_invalid(in_cache)
            except ICATConnectionException:
                pass

        return ret_obj

    def get_valid_experiments_for_instruments(self, user_number, instruments):
        experiment_dict = {}
        user_experiments = set(self.get_associated_experiments(user_number))
        for instrument_name in instruments:
            instrument_experiments = self.get_valid_experiments_for_instrument(
                instrument_name)
            experiment_dict[instrument_name] = list(
                user_experiments.intersection(instrument_experiments))
        return experiment_dict

    def get_experiment_details(self, experiment_number):
        experiment = self.check_cache(ExperimentCache, experiment_number)
        return experiment.__dict__