docs: def get_variation(self, experiment, user_id, attributes, ignore_user_profile=False):
      """ Top-level function to help determine variation user should be put in.
  
      First, check if experiment is running.
      Second, check if user is forced in a variation.
      Third, check if there is a stored decision for the user and return the corresponding variation.
      Fourth, figure out if user is in the experiment by evaluating audience conditions if any.
      Fifth, bucket the user and return the variation.
  
      Args:
        experiment: Experiment for which user variation needs to be determined.
        user_id: ID for user.
        attributes: Dict representing user attributes.
        ignore_user_profile: True to ignore the user profile lookup. Defaults to False.
  
      Returns:
        Variation user should see. None if user is not in experiment or experiment is not running.
      """
  
      # Check if experiment is running
      if not experiment_helper.is_experiment_running(experiment):
        self.logger.info('Experiment "%s" is not running.' % experiment.key)
        return None
  
      # Check if the user is forced into a variation
      variation = self.config.get_forced_variation(experiment.key, user_id)
      if variation:
        return variation
  
      # Check to see if user is white-listed for a certain variation
      variation = self.get_forced_variation(experiment, user_id)
      if variation:
        return variation
  
      # Check to see if user has a decision available for the given experiment
      user_profile = UserProfile(user_id)
      if not ignore_user_profile and self.user_profile_service:
        try:
          retrieved_profile = self.user_profile_service.lookup(user_id)
        except:
          self.logger.exception('Unable to retrieve user profile for user "%s" as lookup failed.' % user_id)
          retrieved_profile = None
  
        if validator.is_user_profile_valid(retrieved_profile):
          user_profile = UserProfile(**retrieved_profile)
          variation = self.get_stored_variation(experiment, user_profile)
          if variation:
            return variation
        else:
          self.logger.warning('User profile has invalid format.')
  
      # Bucket user and store the new decision
      if not audience_helper.is_user_in_experiment(self.config, experiment, attributes, self.logger):
        self.logger.info('User "%s" does not meet conditions to be in experiment "%s".' % (
          user_id,
          experiment.key
        ))
        return None
  
      # Determine bucketing ID to be used
      bucketing_id = self._get_bucketing_id(user_id, attributes)
      variation = self.bucketer.bucket(experiment, user_id, bucketing_id)
  
      if variation:
        # Store this new decision and return the variation for the user
        if not ignore_user_profile and self.user_profile_service:
          try:
            user_profile.save_variation_for_experiment(experiment.id, variation.id)
            self.user_profile_service.save(user_profile.__dict__)
          except:
            self.logger.exception('Unable to save user profile for user "%s".' % user_id)
        return variation
  
      return None