diff options
Diffstat (limited to 'forum/models')
-rw-r--r-- | forum/models/__init__.py | 41 | ||||
-rw-r--r-- | forum/models/answer.py | 6 | ||||
-rw-r--r-- | forum/models/base.py | 29 | ||||
-rw-r--r-- | forum/models/meta.py | 17 | ||||
-rw-r--r-- | forum/models/question.py | 9 | ||||
-rw-r--r-- | forum/models/user.py | 213 |
6 files changed, 249 insertions, 66 deletions
diff --git a/forum/models/__init__.py b/forum/models/__init__.py index d4b9dcb4..1c20e74b 100644 --- a/forum/models/__init__.py +++ b/forum/models/__init__.py @@ -3,11 +3,12 @@ from answer import Answer, AnonymousAnswer, AnswerRevision from tag import Tag, MarkedTag from meta import Vote, Comment, FlaggedItem from user import Activity, ValidationHash, EmailFeedSetting -from user import Mention, AuthKeyUserAssociation +from user import AuthKeyUserAssociation from repute import Badge, Award, Repute from django.core.urlresolvers import reverse from forum.search.indexer import create_fulltext_indexes from django.db.models.signals import post_syncdb +from forum import const import logging import re @@ -250,7 +251,7 @@ def record_ask_event(instance, created, **kwargs): user=instance.author, active_at=instance.added_at, content_object=instance, - activity_type=TYPE_ACTIVITY_ASK_QUESTION + activity_type=const.TYPE_ACTIVITY_ASK_QUESTION ) activity.save() @@ -281,7 +282,7 @@ def record_answer_event(instance, created, **kwargs): user = instance.author, active_at = instance.added_at, content_object = instance, - activity_type = TYPE_ACTIVITY_ANSWER + activity_type = const.TYPE_ACTIVITY_ANSWER ) activity.save() receiving_users = instance.question.get_author_list( @@ -295,9 +296,9 @@ def record_answer_event(instance, created, **kwargs): def record_comment_event(instance, created, **kwargs): if created: if isinstance(instance.content_object, Question): - type = TYPE_ACTIVITY_COMMENT_QUESTION + activity_type = const.TYPE_ACTIVITY_COMMENT_QUESTION elif isinstance(instance.content_object, Answer): - type = TYPE_ACTIVITY_COMMENT_ANSWER + activity_type = const.TYPE_ACTIVITY_COMMENT_ANSWER else: logging.critical('recording comment for %s is not implemented' % type(instance.content_object)) @@ -305,7 +306,7 @@ def record_comment_event(instance, created, **kwargs): user = instance.user, active_at = instance.added_at, content_object = instance, - activity_type = type + activity_type = activity_type ) activity.save() @@ -322,7 +323,7 @@ def record_revision_question_event(instance, created, **kwargs): user=instance.author, active_at=instance.revised_at, content_object=instance, - activity_type=TYPE_ACTIVITY_UPDATE_QUESTION + activity_type=const.TYPE_ACTIVITY_UPDATE_QUESTION ) activity.save() receiving_users = set() @@ -342,7 +343,7 @@ def record_revision_answer_event(instance, created, **kwargs): user=instance.author, active_at=instance.revised_at, content_object=instance, - activity_type=TYPE_ACTIVITY_UPDATE_ANSWER + activity_type=const.TYPE_ACTIVITY_UPDATE_ANSWER ) activity.save() receiving_users = set() @@ -367,7 +368,7 @@ def record_award_event(instance, created, **kwargs): user=instance.user,#todo: change this to community user who gives the award active_at=instance.awarded_at, content_object=instance, - activity_type=TYPE_ACTIVITY_PRIZE + activity_type=const.TYPE_ACTIVITY_PRIZE ) activity.save() activity.receiving_users.add(instance.user) @@ -405,7 +406,7 @@ def record_answer_accepted(instance, created, **kwargs): user=instance.question.author, active_at=datetime.datetime.now(), content_object=instance, - activity_type=TYPE_ACTIVITY_MARK_ANSWER + activity_type=const.TYPE_ACTIVITY_MARK_ANSWER ) receiving_users = instance.get_author_list( exclude_list = [instance.question.author] @@ -428,9 +429,9 @@ def record_vote(instance, created, **kwargs): """ if created: if instance.vote == 1: - vote_type = TYPE_ACTIVITY_VOTE_UP + vote_type = const.TYPE_ACTIVITY_VOTE_UP else: - vote_type = TYPE_ACTIVITY_VOTE_DOWN + vote_type = const.TYPE_ACTIVITY_VOTE_DOWN activity = Activity( user=instance.user, @@ -449,7 +450,7 @@ def record_cancel_vote(instance, **kwargs): user=instance.user, active_at=datetime.datetime.now(), content_object=instance, - activity_type=TYPE_ACTIVITY_CANCEL_VOTE + activity_type=const.TYPE_ACTIVITY_CANCEL_VOTE ) #todo: same problem - cannot access receiving user here activity.save() @@ -459,9 +460,9 @@ def record_delete_question(instance, delete_by, **kwargs): when user deleted the question """ if instance.__class__ == "Question": - activity_type = TYPE_ACTIVITY_DELETE_QUESTION + activity_type = const.TYPE_ACTIVITY_DELETE_QUESTION else: - activity_type = TYPE_ACTIVITY_DELETE_ANSWER + activity_type = const.TYPE_ACTIVITY_DELETE_ANSWER activity = Activity( user=delete_by, @@ -477,7 +478,7 @@ def record_mark_offensive(instance, mark_by, **kwargs): user=mark_by, active_at=datetime.datetime.now(), content_object=instance, - activity_type=TYPE_ACTIVITY_MARK_OFFENSIVE + activity_type=const.TYPE_ACTIVITY_MARK_OFFENSIVE ) activity.save() receiving_users = instance.get_author_list( @@ -493,7 +494,7 @@ def record_update_tags(question, **kwargs): user=question.author, active_at=datetime.datetime.now(), content_object=question, - activity_type=TYPE_ACTIVITY_UPDATE_TAGS + activity_type=const.TYPE_ACTIVITY_UPDATE_TAGS ) activity.save() @@ -506,7 +507,7 @@ def record_favorite_question(instance, created, **kwargs): user=instance.user, active_at=datetime.datetime.now(), content_object=instance, - activity_type=TYPE_ACTIVITY_FAVORITE + activity_type=const.TYPE_ACTIVITY_FAVORITE ) activity.save() receiving_users = instance.question.get_author_list( @@ -519,7 +520,7 @@ def record_user_full_updated(instance, **kwargs): user=instance, active_at=datetime.datetime.now(), content_object=instance, - activity_type=TYPE_ACTIVITY_USER_FULL_UPDATED + activity_type=const.TYPE_ACTIVITY_USER_FULL_UPDATED ) activity.save() @@ -588,7 +589,6 @@ Activity = Activity EmailFeedSetting = EmailFeedSetting ValidationHash = ValidationHash AuthKeyUserAssociation = AuthKeyUserAssociation -Mention = Mention __all__ = [ 'Question', @@ -615,7 +615,6 @@ __all__ = [ 'EmailFeedSetting', 'ValidationHash', 'AuthKeyUserAssociation', - 'Mention', 'User', ] diff --git a/forum/models/answer.py b/forum/models/answer.py index 7f5c8ae8..1c33cad8 100644 --- a/forum/models/answer.py +++ b/forum/models/answer.py @@ -51,6 +51,12 @@ class AnswerManager(models.Manager): pass return answer + def get_author_list(self, **kwargs): + authors = set() + for answer in self: + authors.update(answer.get_author_list(**kwargs)) + return list(authors) + #GET_ANSWERS_FROM_USER_QUESTIONS = u'SELECT answer.* FROM answer INNER JOIN question ON answer.question_id = question.id WHERE question.author_id =%s AND answer.author_id <> %s' def get_answers_from_question(self, question, user=None): """ diff --git a/forum/models/base.py b/forum/models/base.py index 076d1cfa..068effbd 100644 --- a/forum/models/base.py +++ b/forum/models/base.py @@ -111,8 +111,8 @@ class Content(models.Model): last_edited_at = models.DateTimeField(null=True, blank=True) last_edited_by = models.ForeignKey(User, null=True, blank=True, related_name='last_edited_%(class)ss') - html = models.TextField(null=True) - text = models.TextField(null=True) #denormalized copy of latest revision + html = models.TextField(null=True)#html rendition of the latest revision + text = models.TextField(null=True)#denormalized copy of latest revision comments = generic.GenericRelation(Comment) votes = generic.GenericRelation(Vote) flagged_items = generic.GenericRelation(FlaggedItem) @@ -150,7 +150,10 @@ class Content(models.Model): self.save() def get_latest_revision(self): - return self.revisions.all()[0] + return self.revisions.all().order_by('-revised_at')[0] + + def get_latest_revision_number(self): + return self.get_latest_revision().revision def get_last_author(self): return self.last_edited_by @@ -162,12 +165,30 @@ class Content(models.Model): authors.update([c.user for c in self.comments.all()]) if recursive: if hasattr(self, 'answers'): - for a in self.answers.all(): + for a in self.answers.exclude(deleted = True): authors.update(a.get_author_list( include_comments = include_comments ) ) if exclude_list: authors -= set(exclude_list) return list(authors) + def passes_tag_filter_for_user(self, user): + tags = self.get_origin_post().tags.all() + + if self.tag_filter_setting == 'interesting': + #at least some of the tags must be marked interesting + return self.tag_selections.exists(tag__in = tags, reason = 'good') + + elif self.tag_filter_setting == 'ignored': + #at least one tag must be ignored + if self.tag_selections.exists(tag__in = tags, reason = 'bad'): + return False + else: + return True + + else: + raise Exception('unexpected User.tag_filter_setting %' % self.tag_filter_setting) + + def post_get_last_update_info(self):#todo: rename this subroutine when = self.added_at who = self.author diff --git a/forum/models/meta.py b/forum/models/meta.py index d792beca..8382fec7 100644 --- a/forum/models/meta.py +++ b/forum/models/meta.py @@ -99,13 +99,20 @@ class Comment(MetaContent, UserContent): logging.debug('problem pinging google did you register you sitemap with google?') def delete(self, **kwargs): - from forum.models.user import Mention - ctype = ContentType.objects.get_for_model(self) - Mention.objects.filter( - content_type = ctype, - object_id = self.id + #todo: not very good import in models of other models + #todo: potentially a circular import + from forum.models.user import Activity + Activity.objects.get_mentions( + mentioned_in = self ).delete() super(Comment,self).delete(**kwargs) + def get_absolute_url(self): + origin_post = self.get_origin_post() + return '%s#comment-%d' % (origin_post.get_absolute_url(), self.id) + + def get_latest_revision_number(self): + return 1 + def __unicode__(self): return self.comment diff --git a/forum/models/question.py b/forum/models/question.py index 58852495..c2fb6203 100644 --- a/forum/models/question.py +++ b/forum/models/question.py @@ -183,6 +183,15 @@ class QuestionManager(models.Manager): | Q(answers__in=answer_list) ).distinct().order_by('?') + def get_author_list(self, **kwargs): + #todo: - this is duplication - answer manager also has this method + #will be gone when models are consolidated + #note that method get_question_and_answer_contributors is similar in function + authors = set() + for question in self: + authors.update(question.get_author_list(**kwargs)) + return list(authors) + def update_tags(self, question, tagnames, user): """ Updates Tag associations for a question to match the given diff --git a/forum/models/user.py b/forum/models/user.py index 4e2b7f1a..2f4db6aa 100644 --- a/forum/models/user.py +++ b/forum/models/user.py @@ -2,70 +2,209 @@ from base import * from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic from django.contrib.auth.models import User +from forum.models.question import Question, QuestionRevision +from forum.models.answer import Answer, AnswerRevision +from forum.models.meta import Comment from hashlib import md5 import string from random import Random from forum import const +from forum.utils import functions import datetime +import logging from django.utils.translation import ugettext as _ +class ActivityManager(models.Manager): + def get_all_origin_posts(self): + #todo: redo this with query sets + origin_posts = set() + for m in self.all(): + post = m.content_object + if post and hasattr(post, 'get_origin_post'): + origin_posts.add(post.get_origin_post()) + else: + logging.debug( + 'method get_origin_post() not implemented for %s' \ + % unicode(post) + ) + return list(origin_posts) + + def create_new_mention( + self, + mentioned_by = None, + mentioned_whom = None, + mentioned_at = None, + mentioned_in = None, + reported = None + ): + + #todo: automate this using python inspect module + kwargs = dict() + + kwargs['activity_type'] = const.TYPE_ACTIVITY_MENTION + + if mentioned_at: + #todo: handle cases with rich lookups here like __lt + kwargs['active_at'] = mentioned_at + + if mentioned_by: + kwargs['user'] = mentioned_by + + if mentioned_in: + if functions.is_iterable(mentioned_in): + raise NotImplementedError('mentioned_in only works for single items') + else: + post_content_type = ContentType.objects.get_for_model(mentioned_in) + kwargs['content_type'] = post_content_type + kwargs['object_id'] = mentioned_in.id + + if reported == True: + kwargs['is_auditted'] = True + else: + kwargs['is_auditted'] = False + + + mention_activity = Activity(**kwargs) + mention_activity.save() + + if mentioned_whom: + if functions.is_iterable(mentioned_whom): + raise NotImplementedError('cannot yet mention multiple people at once') + else: + mention_activity.receiving_users.add(mentioned_whom) + + return mention_activity + + + def get_mentions( + self, + mentioned_by = None, + mentioned_whom = None, + mentioned_at = None, + mentioned_in = None, + reported = None + ): + + kwargs = dict() + + kwargs['activity_type'] = const.TYPE_ACTIVITY_MENTION + + if mentioned_at: + #todo: handle cases with rich lookups here like __lt + kwargs['active_at'] = mentioned_at + + if mentioned_by: + kwargs['user'] = mentioned_by + + if mentioned_whom: + if functions.is_iterable(mentioned_whom): + kwargs['receiving_users__in'] = mentioned_whom + else: + kwargs['receiving_users__in'] = (mentioned_whom,) + + if mentioned_in: + if functions.is_iterable(mentioned_in): + it = iter(mentioned_in) + raise NotImplementedError('mentioned_in only works for single items') + else: + post_content_type = ContentType.objects.get_for_model(mentioned_in) + kwargs['content_type'] = post_content_type + kwargs['object_id'] = mentioned_in.id + + if reported == True: + kwargs['is_auditted'] = True + else: + kwargs['is_auditted'] = False + + return self.filter(**kwargs) + + class Activity(models.Model): """ We keep some history data for user activities """ user = models.ForeignKey(User) receiving_users = models.ManyToManyField(User, related_name='received_activity') - activity_type = models.SmallIntegerField(choices=TYPE_ACTIVITY) + activity_type = models.SmallIntegerField(choices = const.TYPE_ACTIVITY) active_at = models.DateTimeField(default=datetime.datetime.now) content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id') is_auditted = models.BooleanField(default=False) + objects = ActivityManager() + def __unicode__(self): return u'[%s] was active at %s' % (self.user.username, self.active_at) + def get_response_type_content_object(self): + """ + This method will go when post models are + unified (todo:) + """ + cobj = self.content_object + if isinstance(cobj, Comment): + return cobj + elif isinstance(cobj, AnswerRevision): + return cobj.answer + elif isinstance(cobj, QuestionRevision): + return cobj.question + else: + raise NotImplementedError() + class Meta: app_label = 'forum' db_table = u'activity' -class MentionManager(models.Manager): - def get_question_list(self): - out = [] - for m in self.all(): - post = m.content_object - if isinstance(post, Question): - out.append(post) - elif isinstance(post, Answer): - out.append(post.question) - elif isinstance(post, Comment): - p = post.content_object - if isinstance(p, Question): - out.append(p) - elif isinstance(p, Answer): - out.append(p.question) - return out - -class Mention(models.Model): - """ - Table holding @mention type entries in the posts - todo: maybe merge this with Activity table - """ - mentioned_by = models.ForeignKey(User, related_name = 'mentions_sent') - mentioned_whom = models.ForeignKey(User, related_name = 'mentions_received') - mentioned_at = models.DateTimeField(default=datetime.datetime.now) - #have to use generic foreign key here to point to the context of the mention - content_type = models.ForeignKey(ContentType) - object_id = models.PositiveIntegerField() - content_object = generic.GenericForeignKey('content_type', 'object_id') - - objects = MentionManager() - - class Meta: - app_label = 'forum' - db_table = u'mention' +class EmailFeedSettingManager(models.Manager): + def exists_match_to_post_and_subscriber(self, post = None, subscriber = None, **kwargs): + """returns list of feeds matching the post + and subscriber + """ + feeds = self.filter(subscriber = subscriber, **kwargs) + + for feed in feeds: + + if feed.feed_type == 'm_and_c': + if isinstance(post, Comment): + return True + else: + post_content_type = ContentType.objects.get_for_model(post) + subscriber_mentions = Mention.objects.filter( + content_type = post_content_type, + object_id = post.id, + mentioned_whom = subscriber, + is_auditted = False + ) + if subscriber_mentions: + return True + else: + if feed.feed_type == 'q_all': + #'everything' category is tag filtered + if post.passes_tag_filter_for_user(subscriber): + return True + else: + + origin_post = post.get_origin_post() + + if feed.feed_type == 'q_ask': + if origin_post.author == subscriber: + return True + + elif feed.feed_type == 'q_ans': + #make sure that subscriber answered origin post + answers = origin_post.answers.exclude(deleted=True) + if subscriber in answers.get_author_list(): + return True + + elif feed.feed_type == 'q_sel': + #make sure that subscriber has selected this post + #individually + if subscriber in origin_post.followed_by.all(): + return True + return False class EmailFeedSetting(models.Model): DELTA_TABLE = { @@ -99,6 +238,8 @@ class EmailFeedSetting(models.Model): added_at = models.DateTimeField(auto_now_add=True) reported_at = models.DateTimeField(null=True) + objects = EmailFeedSettingManager() + #functions for rich comparison #PRECEDENCE = ('i','d','w','n')#the greater ones are first #def __eq__(self, other): |