Ejemplo n.º 1
0
 def __init__(self, name):
     self.name = name.lower().strip().replace('#', '')
     self.base_key = 'tag:{}:posts'.format(self.name)
     self.new = RedisSortedSet(self.base_key)
     self.images_only = RedisLastBumpedBuffer(self.base_key + ':images', 1000)
     self.popular = RedisLastBumpedBuffer(self.base_key + ':popular', 1000)
     self.post_count = RedisKey(self.base_key + ':count')
Ejemplo n.º 2
0
 def _record(timestamp_key):
     if request:
         RedisSet(timestamp_key + ":unique_ips").sadd(
             util.ip_to_int(request.META.get('REMOTE_ADDR')))
     if user:
         RedisSet(timestamp_key + ":uniques").sadd(user.id)
     RedisKey(timestamp_key + ":count").incr(1)
Ejemplo n.º 3
0
 def __init__(self, name):
     self.name = name.lower().strip().replace('#', '')
     self.base_key = 'tag:{}:posts'.format(self.name)
     self.new = RedisSortedSet(self.base_key)
     self.images_only = RedisLastBumpedBuffer(self.base_key + ':images', 1000)
     self.popular = RedisLastBumpedBuffer(self.base_key + ':popular', 1000)
     self.post_count = RedisKey(self.base_key + ':count')
Ejemplo n.º 4
0
    def __init__(self, type_id, name="", value=None, preference=None, limited=False, hidden=False, unusable=False,
                 cost=None, title="", shop_filename=None, shop_text="", purchasable=None, achievement=None,
                 admin_only=False, maximum=None, hide_from_inventory=False, seasonal=False):
        """
        admin:
            Whether this sticker is only available for admins.
        shop_filename:
            Defaults to "name.png" if unspecified here.
        purchasable:
            Leave this as `None` and it will be determined from the cost and achievement status.
            Set it to `False` to override this and force it to not be sold in the shop.
        """
        if cost is not None:
            cost = int(cost)

        if value is None:
            if cost is None or cost < 1:
                value = 1
            else:
                value = math.sqrt(cost+1)
        else:
            value = float(value)

        self._purchasable = purchasable
        if purchasable and not cost:
            raise ValueError('A sticker without a cost cannot be purchasable.')

        if shop_filename is None:
            shop_filename = u'{0}.png'.format(name)

        self.type_id = type_id
        self.name = name
        self.value = value
        self.preference = preference if preference is not None else type_id
        self._is_limited = bool(limited or cost or maximum)
        # not placeable anymore, but should still show up on existing posts.
        self._is_unusable = unusable
        # not placeable anymore nor should it show up on already stickered posts.
        self.is_hidden = hidden
        self.cost = cost
        self.title = title
        self.shop_filename = shop_filename
        self.shop_text = shop_text
        self.achievement = achievement
        self.admin_only = admin_only
        self.maximum = maximum
        self.user_remaining = None

        self.inventory_hash = None
        if self.maximum:
            self.inventory_hash = RedisKey("sticker:%s:remaining" % self.name)

        self.seasonal = seasonal
        self.hide_from_inventory = hide_from_inventory
Ejemplo n.º 5
0
 def branch_count(self, experiment_name, branch_name):
     return int(
         RedisKey(self.basekey + ":" + "experiment:%s:%s:count" %
                  (experiment_name, branch_name)).get() or 0)
Ejemplo n.º 6
0
 def hourly_count(self, datetime):
     return int(
         RedisKey(self.basekey + ":" + datetime.strftime("%Y.%m.%d.%H") +
                  ":count").get() or 0)
Ejemplo n.º 7
0
 def daily_count(self, day):
     return int(RedisKey(self.daykey(day, 'count')).get() or 0)
Ejemplo n.º 8
0
 def timestamp_key(self):
     return RedisKey(self.basekey + ":last_timestamp")
Ejemplo n.º 9
0
class Tag(object):
    top = property(lambda self: DateKey(lambda key: RedisLastBumpedBuffer(key, 30*30), self.base_key, ':top'))

    updates_channel = property(lambda self: RealtimeChannel('tu:%s' %  self.name, 5, ttl=24*60*60))

    def __repr__(self):
        return self.name

    def __init__(self, name):
        self.name = name.lower().strip().replace('#', '')
        self.base_key = 'tag:{}:posts'.format(self.name)
        self.new = RedisSortedSet(self.base_key)
        self.images_only = RedisLastBumpedBuffer(self.base_key + ':images', 1000)
        self.popular = RedisLastBumpedBuffer(self.base_key + ':popular', 1000)
        self.post_count = RedisKey(self.base_key + ':count')

    def to_client(self, **kwargs):
        return self.name

    def tag_comment(self, comment, timestamp=None):
        if timestamp is None:
            timestamp = Services.time.time()

        self.new.zadd(int(comment.id), timestamp)
        all_tags.sadd(self.name)

        if comment.reply_content is not None:
            self.images_only.bump(int(comment.id), score=timestamp)
            count = self.post_count.incr()
            self.updates_channel.publish({'post': comment.id, 'tag': self.name, 'count': count})

    def untag_comment(self, comment):
        self.new.zrem(comment.id)
        self.images_only.remove(comment.id)
        self.popular.remove(comment.id)

    def get_absolute_url(self):
        return '/x/' + self.name.replace('#','')

    def user_is_following(self, user):
        if not user.is_authenticated():
            return False

        return self.name in user.redis.followed_tags

    def merge_top_scores(self, day=None):
        """
        Merges daily top scores into monthly and monthly into yearly top scores for this group for the given day
        and the 365 days before it.

        If `day` is `None`, defaults to today.
        """
        if not day:
            day = Services.time.today()
        # Merge today + last 365 days
        days = [day - datetime.timedelta(n) for n in range(366)]

        months = defaultdict(list)
        for day in days:
            months[(day.year, day.month)].append(day)

        years = defaultdict(list)

        for (year, month) in months.keys():
            years[year].append(month)

        for (year, month), days in months.iteritems():
            dest = self.top.month(datetime.date(year, month, 1))
            source_keys = [self.top.day(day).key for day in days]
            redis.zunionstore(dest.key, source_keys, aggregate='max')
            dest.truncate(2)

        for year, year_months in years.iteritems():
            dest = self.top.year(datetime.date(year, 1, 1))
            source_keys = [self.top.month(datetime.date(year, month, 1)).key for month in year_months]
            redis.zunionstore(dest.key, source_keys, aggregate='max')
            dest.truncate(5)
Ejemplo n.º 10
0
class Sticker(object):
    """
    A datastructure to hold a Canvas sticker definition.

    Note that the `details` property (and `to_client()`) never gets updated after instantiation.
    """
    def __eq__(self, another_sticker):
        try:
            return self.type_id == another_sticker.type_id
        except AttributeError:
            return False

    def __init__(self, type_id, name="", value=None, preference=None, limited=False, hidden=False, unusable=False,
                 cost=None, title="", shop_filename=None, shop_text="", purchasable=None, achievement=None,
                 admin_only=False, maximum=None, hide_from_inventory=False, seasonal=False):
        """
        admin:
            Whether this sticker is only available for admins.
        shop_filename:
            Defaults to "name.png" if unspecified here.
        purchasable:
            Leave this as `None` and it will be determined from the cost and achievement status.
            Set it to `False` to override this and force it to not be sold in the shop.
        """
        if cost is not None:
            cost = int(cost)

        if value is None:
            if cost is None or cost < 1:
                value = 1
            else:
                value = math.sqrt(cost+1)
        else:
            value = float(value)

        self._purchasable = purchasable
        if purchasable and not cost:
            raise ValueError('A sticker without a cost cannot be purchasable.')

        if shop_filename is None:
            shop_filename = u'{0}.png'.format(name)

        self.type_id = type_id
        self.name = name
        self.value = value
        self.preference = preference if preference is not None else type_id
        self._is_limited = bool(limited or cost or maximum)
        # not placeable anymore, but should still show up on existing posts.
        self._is_unusable = unusable
        # not placeable anymore nor should it show up on already stickered posts.
        self.is_hidden = hidden
        self.cost = cost
        self.title = title
        self.shop_filename = shop_filename
        self.shop_text = shop_text
        self.achievement = achievement
        self.admin_only = admin_only
        self.maximum = maximum
        self.user_remaining = None

        self.inventory_hash = None
        if self.maximum:
            self.inventory_hash = RedisKey("sticker:%s:remaining" % self.name)

        self.seasonal = seasonal
        self.hide_from_inventory = hide_from_inventory

    @property
    def active_seasonal(self):
        return self in get_active_seasonal_stickers()

    @property
    def is_unusable(self):
        if self.seasonal:
            return not self.active_seasonal
        else:
            return self._is_unusable

    @property
    def is_limited(self):
        if self.seasonal:
            return self.active_seasonal
        else:
            return self._is_limited

    def is_epic(self):
        """ Recipients of Epic stickers get an exciting realtime notification. """
        return self.cost >= knobs.EPIC_STICKER_COST_THRESHOLD

    def is_star(self):
        from django.conf import settings
        if not hasattr(settings, 'STAR_STICKER_TYPE_ID'):
            return False
        return self.type_id == settings.STAR_STICKER_TYPE_ID

    def is_usable(self, user):
        """
        Whether this sticker can be used by the user. Takes several factors into account,
        not just `Sticker.is_unusable`.
        """
        return bool(self.cost
                    and not self.is_unusable
                    and not self.is_hidden
                    and (self.achievement is None or user.kv.achievements.by_id(self.achievement).get()))

    def is_limited_inventory(self):
        """ Whether this sticker has a limited number of units available. """
        return self.maximum != None

    def is_purchasable(self, user):
        if self._purchasable is None:
            if self.is_limited_inventory() and self.is_out_of_stock():
                # Then it can be purchased if there are enough.
                # Note that we do not check for whether the user has already bought the
                # sticker here. We also do not check if the user can afford it.
                # This logic is done in api.store_buy
                return False

            return bool(self.cost and (self.achievement is None
                                       or user.kv.achievements.by_id(self.achievement).get()))
        return self._purchasable

    @property
    def remaining(self):
        """ Returns the number of stickers available. """
        if self.inventory_hash:
            # Was the value ever bootstrapped
            if self.inventory_hash.get() == None:
                self.inventory_hash.set(self.maximum)
            return int(self.inventory_hash.get())
        return None

    def is_out_of_stock(self):
        return self.remaining == 0

    def decrement_inventory(self):
        try:
            return int(self.inventory_hash.decr())
        except:
            pass

    def to_client(self):
        keys = [
            'type_id',
            'name',
            'value',
            'preference',
            'is_limited',
            'is_unusable',
            'is_hidden',
            'cost',
            'title',
            'shop_filename',
            'shop_text',
            'achievement',
            'admin_only',
            'maximum',
            'user_remaining',
        ]
        return dict([(key, getattr(self, key)) for key in keys])

    def sort_key(self, count):
        score = count * (self.cost+1 if self.cost else 1)
        return (score, int(self.is_limited), self.preference)

    def __repr__(self):
        return unicode(self.to_client())
Ejemplo n.º 11
0
    def __init__(self,
                 type_id,
                 name="",
                 value=None,
                 preference=None,
                 limited=False,
                 hidden=False,
                 unusable=False,
                 cost=None,
                 title="",
                 shop_filename=None,
                 shop_text="",
                 purchasable=None,
                 achievement=None,
                 admin_only=False,
                 maximum=None,
                 hide_from_inventory=False,
                 seasonal=False):
        """
        admin:
            Whether this sticker is only available for admins.
        shop_filename:
            Defaults to "name.png" if unspecified here.
        purchasable:
            Leave this as `None` and it will be determined from the cost and achievement status.
            Set it to `False` to override this and force it to not be sold in the shop.
        """
        if cost is not None:
            cost = int(cost)

        if value is None:
            if cost is None or cost < 1:
                value = 1
            else:
                value = math.sqrt(cost + 1)
        else:
            value = float(value)

        self._purchasable = purchasable
        if purchasable and not cost:
            raise ValueError('A sticker without a cost cannot be purchasable.')

        if shop_filename is None:
            shop_filename = u'{0}.png'.format(name)

        self.type_id = type_id
        self.name = name
        self.value = value
        self.preference = preference if preference is not None else type_id
        self._is_limited = bool(limited or cost or maximum)
        # not placeable anymore, but should still show up on existing posts.
        self._is_unusable = unusable
        # not placeable anymore nor should it show up on already stickered posts.
        self.is_hidden = hidden
        self.cost = cost
        self.title = title
        self.shop_filename = shop_filename
        self.shop_text = shop_text
        self.achievement = achievement
        self.admin_only = admin_only
        self.maximum = maximum
        self.user_remaining = None

        self.inventory_hash = None
        if self.maximum:
            self.inventory_hash = RedisKey("sticker:%s:remaining" % self.name)

        self.seasonal = seasonal
        self.hide_from_inventory = hide_from_inventory
Ejemplo n.º 12
0
class Sticker(object):
    """
    A datastructure to hold a Canvas sticker definition.

    Note that the `details` property (and `to_client()`) never gets updated after instantiation.
    """
    def __eq__(self, another_sticker):
        try:
            return self.type_id == another_sticker.type_id
        except AttributeError:
            return False

    def __init__(self,
                 type_id,
                 name="",
                 value=None,
                 preference=None,
                 limited=False,
                 hidden=False,
                 unusable=False,
                 cost=None,
                 title="",
                 shop_filename=None,
                 shop_text="",
                 purchasable=None,
                 achievement=None,
                 admin_only=False,
                 maximum=None,
                 hide_from_inventory=False,
                 seasonal=False):
        """
        admin:
            Whether this sticker is only available for admins.
        shop_filename:
            Defaults to "name.png" if unspecified here.
        purchasable:
            Leave this as `None` and it will be determined from the cost and achievement status.
            Set it to `False` to override this and force it to not be sold in the shop.
        """
        if cost is not None:
            cost = int(cost)

        if value is None:
            if cost is None or cost < 1:
                value = 1
            else:
                value = math.sqrt(cost + 1)
        else:
            value = float(value)

        self._purchasable = purchasable
        if purchasable and not cost:
            raise ValueError('A sticker without a cost cannot be purchasable.')

        if shop_filename is None:
            shop_filename = u'{0}.png'.format(name)

        self.type_id = type_id
        self.name = name
        self.value = value
        self.preference = preference if preference is not None else type_id
        self._is_limited = bool(limited or cost or maximum)
        # not placeable anymore, but should still show up on existing posts.
        self._is_unusable = unusable
        # not placeable anymore nor should it show up on already stickered posts.
        self.is_hidden = hidden
        self.cost = cost
        self.title = title
        self.shop_filename = shop_filename
        self.shop_text = shop_text
        self.achievement = achievement
        self.admin_only = admin_only
        self.maximum = maximum
        self.user_remaining = None

        self.inventory_hash = None
        if self.maximum:
            self.inventory_hash = RedisKey("sticker:%s:remaining" % self.name)

        self.seasonal = seasonal
        self.hide_from_inventory = hide_from_inventory

    @property
    def active_seasonal(self):
        return self in get_active_seasonal_stickers()

    @property
    def is_unusable(self):
        if self.seasonal:
            return not self.active_seasonal
        else:
            return self._is_unusable

    @property
    def is_limited(self):
        if self.seasonal:
            return self.active_seasonal
        else:
            return self._is_limited

    def is_epic(self):
        """ Recipients of Epic stickers get an exciting realtime notification. """
        return self.cost >= knobs.EPIC_STICKER_COST_THRESHOLD

    def is_star(self):
        from django.conf import settings
        if not hasattr(settings, 'STAR_STICKER_TYPE_ID'):
            return False
        return self.type_id == settings.STAR_STICKER_TYPE_ID

    def is_usable(self, user):
        """
        Whether this sticker can be used by the user. Takes several factors into account,
        not just `Sticker.is_unusable`.
        """
        return bool(self.cost and not self.is_unusable and not self.is_hidden
                    and
                    (self.achievement is None
                     or user.kv.achievements.by_id(self.achievement).get()))

    def is_limited_inventory(self):
        """ Whether this sticker has a limited number of units available. """
        return self.maximum != None

    def is_purchasable(self, user):
        if self._purchasable is None:
            if self.is_limited_inventory() and self.is_out_of_stock():
                # Then it can be purchased if there are enough.
                # Note that we do not check for whether the user has already bought the
                # sticker here. We also do not check if the user can afford it.
                # This logic is done in api.store_buy
                return False

            return bool(
                self.cost
                and (self.achievement is None
                     or user.kv.achievements.by_id(self.achievement).get()))
        return self._purchasable

    @property
    def remaining(self):
        """ Returns the number of stickers available. """
        if self.inventory_hash:
            # Was the value ever bootstrapped
            if self.inventory_hash.get() == None:
                self.inventory_hash.set(self.maximum)
            return int(self.inventory_hash.get())
        return None

    def is_out_of_stock(self):
        return self.remaining == 0

    def decrement_inventory(self):
        try:
            return int(self.inventory_hash.decr())
        except:
            pass

    def to_client(self, **kwargs):
        keys = [
            'type_id',
            'name',
            'value',
            'preference',
            'is_limited',
            'is_unusable',
            'is_hidden',
            'cost',
            'title',
            'shop_filename',
            'shop_text',
            'achievement',
            'admin_only',
            'maximum',
            'user_remaining',
        ]
        return dict([(key, getattr(self, key)) for key in keys])

    def sort_key(self, count):
        score = count * (self.cost + 1 if self.cost else 1)
        return (score, int(self.is_limited), self.preference)

    def __repr__(self):
        return unicode(self.to_client())
Ejemplo n.º 13
0
class Tag(object):
    top = property(lambda self: DateKey(lambda key: RedisLastBumpedBuffer(key, 30*30), self.base_key, ':top'))

    updates_channel = property(lambda self: RealtimeChannel('tu:%s' %  self.name, 5, ttl=24*60*60))

    def __repr__(self):
        return self.name

    def __init__(self, name):
        self.name = name.lower().strip().replace('#', '')
        self.base_key = 'tag:{}:posts'.format(self.name)
        self.new = RedisSortedSet(self.base_key)
        self.images_only = RedisLastBumpedBuffer(self.base_key + ':images', 1000)
        self.popular = RedisLastBumpedBuffer(self.base_key + ':popular', 1000)
        self.post_count = RedisKey(self.base_key + ':count')

    def to_client(self):
        return self.name

    def tag_comment(self, comment, timestamp=None):
        if timestamp is None:
            timestamp = Services.time.time()

        self.new.zadd(int(comment.id), timestamp)
        all_tags.sadd(self.name)

        if comment.reply_content is not None:
            self.images_only.bump(int(comment.id), score=timestamp)
            count = self.post_count.incr()
            self.updates_channel.publish({'post': comment.id, 'tag': self.name, 'count': count})

    def untag_comment(self, comment):
        self.new.zrem(comment.id)
        self.images_only.remove(comment.id)
        self.popular.remove(comment.id)

    def get_absolute_url(self):
        return '/x/' + self.name.replace('#','')

    def user_is_following(self, user):
        if not user.is_authenticated():
            return False

        return self.name in user.redis.followed_tags

    def merge_top_scores(self, day=None):
        """
        Merges daily top scores into monthly and monthly into yearly top scores for this group for the given day
        and the 365 days before it.

        If `day` is `None`, defaults to today.
        """
        if not day:
            day = Services.time.today()
        # Merge today + last 365 days
        days = [day - datetime.timedelta(n) for n in range(366)]

        months = defaultdict(list)
        for day in days:
            months[(day.year, day.month)].append(day)

        years = defaultdict(list)

        for (year, month) in months.keys():
            years[year].append(month)

        for (year, month), days in months.iteritems():
            dest = self.top.month(datetime.date(year, month, 1))
            source_keys = [self.top.day(day).key for day in days]
            redis.zunionstore(dest.key, source_keys, aggregate='max')
            dest.truncate(2)

        for year, year_months in years.iteritems():
            dest = self.top.year(datetime.date(year, 1, 1))
            source_keys = [self.top.month(datetime.date(year, month, 1)).key for month in year_months]
            redis.zunionstore(dest.key, source_keys, aggregate='max')
            dest.truncate(5)