class PopularityWinrateDistribution: def __init__(self, redis, name, max_items=9, bucket_size=5, ttl=600, use_lua=None): self.name = name self.max_items = max_items self.bucket_size = bucket_size self.observations = RedisPopularityDistribution( redis, name="%s_OBSERVATIONS" % self.name, namespace="POPULARITY", ttl=ttl, max_items=self.max_items, bucket_size=self.bucket_size, use_lua=use_lua ) self.wins = RedisPopularityDistribution( redis, name="%s_WINS" % self.name, namespace="POPULARITY", ttl=ttl, max_items=self.max_items, bucket_size=self.bucket_size, use_lua=use_lua ) def increment(self, key, win=False, as_of=None): self.observations.increment(key, as_of=as_of) if win: self.wins.increment(key, as_of=as_of) def distribution(self, start_ts, end_ts): games = self.observations.distribution( start_ts=start_ts, end_ts=end_ts, ) wins = self.wins.distribution( start_ts=start_ts, end_ts=end_ts, ) result = {} for key, val in games.items(): result[key] = { "games": val, "wins": wins.get(key, 0) } return result
def test_redis_popularity_distribution(_mock_lock): r = fakeredis.FakeStrictRedis() distribution = RedisPopularityDistribution(r, "DECKS", namespace="test") actuals = defaultdict(int) for deck in DECKS: actuals[deck] += 1 distribution.increment(deck) assert distribution.size() == 18 assert distribution.observations() == len(DECKS) actual_most_popular = list(sorted(actuals.items(), key=lambda t: t[1], reverse=True)) dist_most_popular = list( sorted(distribution.distribution().items(), key=lambda t: t[1], reverse=True) ) actual_result = actual_most_popular[0][0] expected_result = int(dist_most_popular[0][0]) assert actual_result == expected_result assert distribution.popularity(expected_result) == 32.0
def test_bucket_sizes_and_ttls(_mock_lock): r = fakeredis.FakeStrictRedis() distribution = RedisPopularityDistribution( r, "DECKS", ttl=5, namespace="test", bucket_size=1 ) # Create t_0 as 5 seconds in the past current_ts = datetime.utcnow() td = timedelta(seconds=5, microseconds=current_ts.microsecond) t_0 = current_ts - td def t_N(N): return t_0 + timedelta(seconds=N) distribution.increment("A", as_of=t_N(1)) distribution.increment("B", as_of=t_N(2)) distribution.increment("A", as_of=t_N(3)) distribution.increment("A", as_of=t_N(4)) # First assert the full distribution exists expected_distribution = {"A": 3.0, "B": 1.0} actual_distribution = distribution.distribution() assert expected_distribution == actual_distribution # Then assert accessing a partial distribution (t_2, t_3) within the full time range expected_distribution = {"A": 1.0, "B": 1.0} actual_distribution = distribution.distribution(start_ts=t_N(2), end_ts=t_N(3)) assert expected_distribution == actual_distribution # Finally, assert that the first observation of "A" has aged out due to the TTL time.sleep(1) expected_distribution = {"A": 2.0, "B": 1} actual_distribution = distribution.distribution() assert expected_distribution == actual_distribution