示例#1
0
def all_enabled(user=None):
    """Return a list of enabled features and experiments for the user.
    
    Provides the user's assigned variant and the experiment ID for experiments.

    This does not trigger bucketing events, so it should not be used for
    feature flagging purposes on the server. It is meant to let clients
    condition features on experiment variants. Those clients should manually
    send the appropriate bucketing events.

    :param user - (optional) an Account. Defaults to None, for which we
                  determine logged-out features.
    :return dict - a dictionary mapping enabled feature keys to True or to the
                   experiment/variant information
    """
    features = FeatureState.get_all(_world)

    # Get enabled features and experiments
    active = {}
    for feature in features:
        experiment = feature.config.get('experiment')
        if experiment:
            # Get experiment names, ids, and assigned variants, leaving out
            # experiments for which this user is excluded
            variant = feature.variant(user)
            if variant:
                active[feature.name] = {
                    'experiment_id': experiment.get('experiment_id'),
                    'variant': variant
                }
        elif feature.is_enabled(user):
                active[feature.name] = True

    return active
示例#2
0
def _get_featurestate(name):
    """Get a FeatureState object for this feature, creating it if necessary.

    :param name string - a given feature name
    :return FeatureState
    """
    if name not in _featurestate_cache:
        _featurestate_cache[name] = FeatureState(name, _world)

    return _featurestate_cache[name]
示例#3
0
文件: feature.py 项目: z0r0/saidit
def _get_featurestate(name):
    """Get a FeatureState object for this feature, creating it if necessary.

    :param name string - a given feature name
    :return FeatureState
    """

    featurestate = _featurestate_cache.get(name, None)
    if featurestate is None:
        featurestate = FeatureState(name, _world)
        _featurestate_cache[name] = featurestate

    return featurestate
示例#4
0
    def test_choose_variant(self):
        """Test FeatureState's _choose_variant function."""
        no_variants = {}
        three_variants = {
            'remove_vote_counters': 5,
            'control_1': 10,
            'control_2': 5,
        }
        three_variants_more = {
            'remove_vote_counters': 15.6,
            'control_1': 10,
            'control_2': 20,
        }

        counters = collections.defaultdict(collections.Counter)
        for bucket in xrange(FeatureState.NUM_BUCKETS):
            variant = FeatureState._choose_variant(bucket, no_variants)
            if variant:
                counters['no_variants'][variant] += 1
            # Ensure variant-choosing is deterministic.
            self.assertEqual(
                variant,
                FeatureState._choose_variant(bucket, no_variants))

            variant = FeatureState._choose_variant(bucket, three_variants)
            if variant:
                counters['three_variants'][variant] += 1
            # Ensure variant-choosing is deterministic.
            self.assertEqual(
                variant,
                FeatureState._choose_variant(bucket, three_variants))

            previous_variant = variant
            variant = FeatureState._choose_variant(bucket, three_variants_more)
            if variant:
                counters['three_variants_more'][variant] += 1
            # Ensure variant-choosing is deterministic.
            self.assertEqual(
                variant,
                FeatureState._choose_variant(bucket, three_variants_more))
            # If previously we had a variant, we should still have the same one
            # now.
            if previous_variant:
                self.assertEqual(variant, previous_variant)

        # Only controls chosen in the no-variant case.
        for variant, percentage in FeatureState.DEFAULT_CONTROL_GROUPS.items():
            count = counters['no_variants'][variant]
            # The variant percentage is expressed as a part of 100, so we need
            # to calculate the fraction-of-1 percentage and scale it
            # accordingly.
            scaled_percentage = float(count) / (FeatureState.NUM_BUCKETS / 100)
            self.assertEqual(scaled_percentage, percentage)
        for variant, percentage in three_variants.items():
            count = counters['three_variants'][variant]
            scaled_percentage = float(count) / (FeatureState.NUM_BUCKETS / 100)
            self.assertEqual(scaled_percentage, percentage)
        for variant, percentage in three_variants_more.items():
            count = counters['three_variants_more'][variant]
            scaled_percentage = float(count) / (FeatureState.NUM_BUCKETS / 100)
            self.assertEqual(scaled_percentage, percentage)

        # Test boundary conditions around the maximum percentage allowed for
        # variants.
        fifty_fifty = {
            'control_1': 50,
            'control_2': 50,
        }
        almost_fifty_fifty = {
            'control_1': 49,
            'control_2': 51,
        }
        for bucket in xrange(FeatureState.NUM_BUCKETS):
            variant = FeatureState._choose_variant(bucket, fifty_fifty)
            counters['fifty_fifty'][variant] += 1
            variant = FeatureState._choose_variant(bucket, almost_fifty_fifty)
            counters['almost_fifty_fifty'][variant] += 1
        count = counters['fifty_fifty']['control_1']
        scaled_percentage = float(count) / (FeatureState.NUM_BUCKETS / 100)
        self.assertEqual(scaled_percentage, 50)

        count = counters['fifty_fifty']['control_2']
        scaled_percentage = float(count) / (FeatureState.NUM_BUCKETS / 100)
        self.assertEqual(scaled_percentage, 50)

        count = counters['almost_fifty_fifty']['control_1']
        scaled_percentage = float(count) / (FeatureState.NUM_BUCKETS / 100)
        self.assertEqual(scaled_percentage, 49)

        count = counters['almost_fifty_fifty']['control_2']
        scaled_percentage = float(count) / (FeatureState.NUM_BUCKETS / 100)
        self.assertEqual(scaled_percentage, 50)