summaryrefslogtreecommitdiffstats
path: root/forum/models
diff options
context:
space:
mode:
Diffstat (limited to 'forum/models')
-rw-r--r--forum/models/__init__.py41
-rw-r--r--forum/models/answer.py6
-rw-r--r--forum/models/base.py29
-rw-r--r--forum/models/meta.py17
-rw-r--r--forum/models/question.py9
-rw-r--r--forum/models/user.py213
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):