class StickyThread(BaseCanvasModel): comment = ForeignKey(Comment, null=False, related_name='sticky_threads') curator = ForeignKey(User, blank=True, null=True, default=None, related_name='sticky_threads') text = TextField() timestamp = UnixTimestampField(default=0) sort = IntegerField() class Meta: ordering = ['sort'] @classmethod def get_or_create(cls, comment): try: sticky = cls.objects.get(comment=comment.id) except cls.DoesNotExist: cmt = Comment.objects.get(pk=comment.id) sticky = cls(comment=cmt) try: sticky.sort = 1 + max( cls.objects.values_list('sort', flat=True)) except ValueError: sticky.sort = 1 sticky.save() return sticky
class Playback(BaseCanvasModel): comment = ForeignKey(QuestComment, related_name='playbacks', null=False) viewer = ForeignKey(User, null=False) timestamp = UnixTimestampField(default=0) class Meta(object): unique_together = ( 'comment', 'viewer', ) @classmethod def append(cls, **kwargs): """ Ignores dupes. """ if not 'timestamp' in kwargs: kwargs['timestamp'] = Now() instance = cls(**kwargs) try: instance.save() except IntegrityError: return instance.comment.details.force() @bgwork.defer def playback_action(): Actions.playback(instance.viewer, instance.comment) def to_client(self, **kwargs): return { 'timestamp': self.timestamp, 'viewer': self.viewer, }
class IapReceipt(BaseCanvasModel): """ See: http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/VerifyingStoreReceipts/VerifyingStoreReceipts.html """ purchaser = ForeignKey(User, db_index=True, related_name='iap_receipts') receipt_data = TextField() timestamp = UnixTimestampField() product_id = CharField(blank=True, max_length=256) version_external_identifier = CharField(blank=True, max_length=256) bvrs = CharField(blank=True, max_length=256) bid = CharField(blank=True, max_length=256) verified = BooleanField(default=False) def verify(self): try: cleaned_data = verify_receipt(self.receipt_data) except ValidationError: self.verified = False self.save() raise for prop in ['bid', 'bvrs', 'product_id']: setattr(self, prop, cleaned_data[prop]) # Missing in the sandbox. if 'version_external_identifier' in cleaned_data: self.version_external_identifier = cleaned_data[ 'version_external_identifier'] self.verified = True self.save()
class SpotlightedThread(BaseCanvasModel): comment = ForeignKey(Comment, null=False, related_name='spotlighted_threads') curator = ForeignKey(User, blank=True, null=True, default=None, related_name='spotlighted_threads') timestamp = UnixTimestampField(default=0) sort = IntegerField() class Meta: ordering = ['sort'] @classmethod def get_or_create(cls, comment): if comment.parent_comment_id: comment = comment.parent_comment try: spotlighted = cls.objects.get(comment=comment.id) except cls.DoesNotExist: cmt = Comment.objects.get(pk=comment.id) spotlighted = cls(comment=cmt) spotlighted.sort = 1 spotlighted.save() return spotlighted
class MonsterInvite(BaseCanvasModel): monster_part = ForeignKey(MonsterPart, null=False, related_name='invites') timestamp = UnixTimestampField(null=False) used_by = ForeignKey(User, null=True) @classmethod def get_by_monsterpart(cls, monster_part): try: invite = cls.objects.get(monster_part=monster_part) except cls.DoesNotExist: invite = cls(monster_part=monster_part, timestamp=Services.time.time()) invite.save() return invite @property def code(self): str_code = "{0}{1}".format(self.pk, int(self.timestamp)) return base36encode(int(str_code)) @property def is_used(self): return not self.used_by is None def claim(self, user): self.timestamp = Services.time.time() self.used_by = user self.save()
class Activity(BaseCanvasModel): actor = ForeignKey(User, null=True) timestamp = UnixTimestampField() activity_type = CharField(max_length=255, blank=False) data = TextField() key = PositiveIntegerField(blank=True, null=True) class Meta(object): unique_together = ('activity_type', 'key') @classmethod def from_redis_activity(cls, activity, key=None): act = cls() if activity.actor: act.actor = User.objects.get(pk=activity.actor['id']) act.timestamp = activity.timestamp act.activity_type = activity.TYPE act.key = key discard_keys = ['actor', 'ts', 'type'] base = activity.to_dict() act._data = {k: base[k] for k in base.keys() if k not in discard_keys} act.data = util.dumps(act._data) act.save() return act def _details(self): base = util.loads(self.data) base.update({ 'id': self.id, 'ts': self.timestamp, 'activity_type': self.activity_type, }) #TODO have a UserDetails for example.com too to get rid of this branch. if self.actor: if settings.PROJECT == 'canvas': base['actor'] = { 'id': self.actor.id, 'username': self.actor.username, } elif settings.PROJECT == 'drawquest': base['actor'] = UserDetails.from_id(self.actor.id).to_client() return base @classmethod def details_by_id(cls, id): return CachedCall( "activity:%s:details_v2" % id, lambda: cls.objects.get(id=id)._details(), 30 * 24 * 60 * 60, ) @property def details(self): return Activity.details_by_id(self.id)
class IPBlock(BaseCanvasModel): ip = IPAddressField(blank=False, unique=True, db_index=True) moderator = ForeignKey(User, null=True, limit_choices_to={'is_staff': True}) timestamp = UnixTimestampField() note = TextField() class Meta: verbose_name_plural = 'IP blocks' verbose_name = 'IP block'
class Activity(BaseCanvasModel): actor_id = BigIntegerField(blank=True, null=True) timestamp = UnixTimestampField() activity_type = IntegerField(blank=False) data = TextField() key = BigIntegerField(blank=True, null=True) # Common fields, better to have in SQL than JSON. comment_id = BigIntegerField(blank=True, null=True) quest_id = BigIntegerField(blank=True, null=True) class Meta(object): unique_together = ('activity_type', 'key') @classmethod def from_redis_activity(cls, activity, key=None): act = cls() activity_data = activity.to_dict() if activity.actor: act.actor_id = int(activity.actor['id']) act.quest_id = activity_data.get('quest_id') act.comment_id = activity_data.get('comment_id') act.timestamp = activity.timestamp act.activity_type = ACTIVITY_TYPE_IDS[activity.TYPE] act.key = key discard_keys = ['actor', 'ts', 'type', 'quest_id', 'comment_id'] act._data = { k: activity_data[k] for k in activity_data.keys() if k not in discard_keys } act.data = util.dumps(act._data) act.save() return act def _details(self): base = util.loads(self.data) base.update({ 'id': self.id, 'ts': self.timestamp, 'activity_type': dict((v, k) for k, v in ACTIVITY_TYPE_IDS.items())[self.activity_type], }) if self.quest_id: base['quest_id'] = self.quest_id if self.comment_id: base['comment_id'] = self.comment_id #TODO have a UserDetails for example.com too to get rid of this branch. if self.actor_id: base['actor'] = UserDetails.from_id(self.actor_id).to_client() return base @classmethod def details_by_id(cls, id, **kwargs): return CachedCall( 'activity:{}:details_v3'.format(id), lambda: cls.objects.get(id=id)._details(), 10 * 24 * 60 * 60, ) @property def details(self): return self.details_by_id(self.id)
class ScheduledQuest(BaseCanvasModel): quest = ForeignKey('Quest', null=False) curator = ForeignKey(User, blank=True, null=True, default=None, related_name='scheduled_quests') timestamp = UnixTimestampField(default=0) appeared_on = UnixTimestampField(null=True, db_index=True) sort = IntegerField() class Meta: ordering = ['-appeared_on'] @classmethod def get_or_create(cls, quest): if quest.parent_comment_id: quest = quest.parent_comment try: return cls.objects.get(quest=quest.id) except cls.DoesNotExist: return cls.objects.create(quest=Quest.objects.get(pk=quest.id), sort=1) @classmethod def archived(cls, select_quests=False): qs = cls.objects if select_quests: qs = qs.select_related('quest') current_quest_id = redis.get('dq:current_scheduled_quest') if current_quest_id: qs = qs.exclude(id=current_quest_id) return qs.exclude(appeared_on__isnull=True).order_by('-appeared_on') @classmethod def unarchived(cls): return cls.objects.filter(appeared_on__isnull=True).order_by('sort') def _publish_quest_of_the_day(self): signals.current_quest_changed.send(ScheduledQuest, instance=self) RealtimeChannel('qotd', 1).publish({'quest_id': self.quest_id}) push_notification('quest_of_the_day', _(u"Today's Quest: %(quest_title)s" % {'quest_title': self.quest.title}), extra_metadata={'quest_id': self.quest.id}, badge=1) def set_as_current_quest(self): redis.set('dq:current_scheduled_quest', self.id) self.appeared_on = Services.time.time() self.save() self.quest.details.force() self._publish_quest_of_the_day() @classmethod def rollover_next_quest(cls): """ Sets the next scheduled quest as the currently active one / quest of the day. """ try: cls.unarchived().order_by('sort')[0].set_as_current_quest() except IndexError: cls.archived().exclude(quest__title='Give him a smile!').order_by('appeared_on')[0].set_as_current_quest() @classmethod def current_scheduled_quest(cls): """ The `ScheduledQuest` instance representing the current quest of the day. """ scheduled_quest_id = redis.get('dq:current_scheduled_quest') if scheduled_quest_id: return cls.objects.get(id=scheduled_quest_id)