diff options
Diffstat (limited to 'forum/models')
-rw-r--r-- | forum/models/__init__.py | 92 | ||||
-rw-r--r-- | forum/models/answer.py | 28 | ||||
-rw-r--r-- | forum/models/base.py | 158 | ||||
-rw-r--r-- | forum/models/content.py | 22 | ||||
-rw-r--r-- | forum/models/meta.py | 34 | ||||
-rw-r--r-- | forum/models/question.py | 78 | ||||
-rw-r--r-- | forum/models/tag.py | 4 | ||||
-rw-r--r-- | forum/models/user.py | 29 |
8 files changed, 255 insertions, 190 deletions
diff --git a/forum/models/__init__.py b/forum/models/__init__.py index 3b4fd8c0..2df44bcc 100644 --- a/forum/models/__init__.py +++ b/forum/models/__init__.py @@ -1,11 +1,7 @@ -import signals -from question import Question ,QuestionRevision, QuestionView, AnonymousQuestion, FavoriteQuestion -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 AuthKeyUserAssociation -from repute import Badge, Award, Repute +import logging +import re +import hashlib +import datetime from django.core.urlresolvers import reverse from django.core.mail import EmailMessage from forum.search.indexer import create_fulltext_indexes @@ -17,15 +13,19 @@ from django.template.defaultfilters import slugify from django.utils.safestring import mark_safe from django.db import models from django.conf import settings as django_settings -from forum import const -import logging -import re -import hashlib - -import datetime from django.contrib.contenttypes.models import ContentType - -#todo: must go after signals +from forum import const +from forum.models.question import Question, QuestionRevision +from forum.models.question import QuestionView, AnonymousQuestion +from forum.models.question import FavoriteQuestion +from forum.models.answer import Answer, AnonymousAnswer, AnswerRevision +from forum.models.tag import Tag, MarkedTag +from forum.models.meta import Vote, Comment, FlaggedItem +from forum.models.user import Activity, ValidationHash, EmailFeedSetting +from forum.models import signals +#from user import AuthKeyUserAssociation +from forum.models.repute import Badge, Award, Repute +from forum.conf import settings as forum_settings from forum import auth User.add_to_class('is_approved', models.BooleanField(default=False)) @@ -186,7 +186,7 @@ def _process_vote(user, post, timestamp=None, cancel=False, vote_type=None): if cancel: auth.onDownVotedCanceled(vote, post, user, timestamp) else: - auth.onDonwVoted(vote, post, user, timestamp) + auth.onDownVoted(vote, post, user, timestamp) def upvote(self, post, timestamp=None, cancel=False): _process_vote( @@ -210,14 +210,14 @@ def accept_answer(self, answer, timestamp=None, cancel=False): else: auth.onAnswerAccept(answer, self, timestamp=timestamp) -def flag_post(self, post, timestamp=None, cancel=False): +def flag_post(user, post, timestamp=None, cancel=False): if cancel:#todo: can't unflag? return if post.flagged_items.filter(user=user).count() > 0: return else: flag = FlaggedItem( - user = self, + user = user, content_object = post, flagged_at = timestamp, ) @@ -296,7 +296,7 @@ def send_instant_notifications_about_activity_in_post( #get details about update #todo: is there a way to solve this import issue? - from forum.conf import settings as forum_settings + #from forum.conf import settings as forum_settings base_url = forum_settings.APP_URL data = { 'receiving_user': u, @@ -313,7 +313,8 @@ def send_instant_notifications_about_activity_in_post( } #send update subject = _('email update message subject') - text = format_instant_notification_body(template, data) + text = format_instant_notification_body( + ) msg = EmailMessage(subject, text, django_settings.DEFAULT_FROM_EMAIL, [u.email]) print 'sending email to %s' % u.email print 'subject: %s' % subject @@ -337,29 +338,8 @@ def record_ask_event(instance, created, **kwargs): ) activity.save() -#todo: translate this -record_answer_event_re = re.compile("You have received (a|\d+) .*new response.*") def record_answer_event(instance, created, **kwargs): if created: - q_author = instance.question.author - found_match = False - for m in q_author.message_set.all(): - match = record_answer_event_re.search(m.message) - if match: - found_match = True - try: - cnt = int(match.group(1)) - except: - cnt = 1 - m.message = u"You have received %d <a href=\"%s?sort=responses\">new responses</a>."\ - % (cnt+1, q_author.get_profile_url()) - m.save() - break - if not found_match: - msg = u"You have received a <a href=\"%s?sort=responses\">new response</a>."\ - % q_author.get_profile_url() - q_author.message_set.create(message=msg) - activity = Activity( user = instance.author, active_at = instance.added_at, @@ -376,15 +356,26 @@ def record_answer_event(instance, created, **kwargs): #todo: change to more general post_update_activity -def record_post_update_activity(post, newly_mentioned_users, **kwargs): +def record_post_update_activity( + post, + newly_mentioned_users = list(), + timestamp = None, + created = False, + **kwargs + ): + """called upon signal forum.models.signals.post_updated + which is sent at the end of save() method in posts + """ #todo: take into account created == True case - activity_type = post.get_updated_activity_type() + activity_type = post.get_updated_activity_type(created) + + assert(timestamp != None) #fields will depend on post type and maybe activity type #post has to be saved already, b/c Activity is in generic relation to post activity = Activity( user = post.get_last_author(), - active_at = post.added_at, + active_at = timestamp, content_object = post, activity_type = activity_type ) @@ -420,8 +411,6 @@ def record_revision_question_event(instance, created, **kwargs): instance.question.get_author_list(include_comments = True) ) - receiving_users.update( - ) for a in instance.question.answers.all(): receiving_users.update(a.get_author_list()) @@ -562,6 +551,8 @@ def record_cancel_vote(instance, **kwargs): #todo: same problem - cannot access receiving user here activity.save() +#todo: weird that there is no record delete answer or comment +#is this even necessary to keep track of? def record_delete_question(instance, delete_by, **kwargs): """ when user deleted the question @@ -634,7 +625,7 @@ def record_user_full_updated(instance, **kwargs): def post_stored_anonymous_content(sender,user,session_key,signal,*args,**kwargs): aq_list = AnonymousQuestion.objects.filter(session_key = session_key) aa_list = AnonymousAnswer.objects.filter(session_key = session_key) - from forum.conf import settings as forum_settings + #from forum.conf import settings as forum_settings if forum_settings.EMAIL_VALIDATION == True:#add user to the record for aq in aq_list: aq.author = user @@ -652,7 +643,6 @@ def post_stored_anonymous_content(sender,user,session_key,signal,*args,**kwargs) #signal for User model save changes django_signals.pre_save.connect(calculate_gravatar_hash, sender=User) django_signals.post_save.connect(record_ask_event, sender=Question) -django_signals.post_save.connect(record_answer_event, sender=Answer) django_signals.post_save.connect(record_revision_question_event, sender=QuestionRevision) django_signals.post_save.connect(record_revision_answer_event, sender=AnswerRevision) django_signals.post_save.connect(record_award_event, sender=Award) @@ -675,6 +665,10 @@ signals.post_updated.connect( record_post_update_activity, sender=Comment ) +signals.post_updated.connect( + record_post_update_activity, + sender=Answer + ) #post_syncdb.connect(create_fulltext_indexes) #todo: wtf??? what is x=x about? diff --git a/forum/models/answer.py b/forum/models/answer.py index 3f52b4ab..4b35fd31 100644 --- a/forum/models/answer.py +++ b/forum/models/answer.py @@ -1,18 +1,15 @@ -from base import AnonymousContent, ContentRevision, DeletableContent -from content import Content -#todo: take care of copy-paste markdowner stuff maybe make html automatic field? -from forum import const -from markdown2 import Markdown -from django.utils.html import strip_tags -from forum.utils.html import sanitize_html +import datetime from django.db import models from django.utils.http import urlquote as django_urlquote from django.template.defaultfilters import slugify from django.core.urlresolvers import reverse -import datetime -markdowner = Markdown(html4tags=True) +from forum.models.base import AnonymousContent, DeletableContent +from forum.models.base import ContentRevision +from forum.models.base import save_post, parse_post_text +from forum.models import content +from forum.models.question import Question +from forum import const -from question import Question class AnswerManager(models.Manager): def create_new(self, question=None, author=None, added_at=None, wiki=False, text='', email_notify=False): @@ -22,7 +19,7 @@ class AnswerManager(models.Manager): added_at = added_at, wiki = wiki, text = text, - html = sanitize_html(markdowner.convert(text)), + #.html field is denormalized by the save() call ) if answer.wiki: answer.last_edited_by = answer.author @@ -84,16 +81,19 @@ class AnswerManager(models.Manager): # cursor.execute(self.GET_ANSWERS_FROM_USER_QUESTIONS, [user_id, user_id]) # return cursor.fetchall() -class Answer(Content, DeletableContent): +class Answer(content.Content, DeletableContent): question = models.ForeignKey('Question', related_name='answers') accepted = models.BooleanField(default=False) accepted_at = models.DateTimeField(null=True, blank=True) objects = AnswerManager() - class Meta(Content.Meta): + class Meta(content.Content.Meta): db_table = u'answer' + save = save_post + parse = parse_post_text + def apply_edit(self, edited_at=None, edited_by=None, text=None, comment=None, wiki=False): if text is None: @@ -105,7 +105,7 @@ class Answer(Content, DeletableContent): self.last_edited_at = edited_at self.last_edited_by = edited_by - self.html = sanitize_html(markdowner.convert(text)) + #self.html is denormalized in save() self.text = text #todo: bug wiki has no effect here self.save() diff --git a/forum/models/base.py b/forum/models/base.py index 110c0deb..1b3a4c2a 100644 --- a/forum/models/base.py +++ b/forum/models/base.py @@ -1,99 +1,143 @@ import datetime -import hashlib -from urllib import quote_plus, urlencode from django.db import models -from django.utils.http import urlquote as django_urlquote from django.utils.html import strip_tags -from django.core.urlresolvers import reverse from django.contrib.auth.models import User from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType -from django.utils.translation import ugettext as _ from django.contrib.sitemaps import ping_google -import django.dispatch -from django.conf import settings +#todo: maybe merge forum.utils.markup and forum.utils.html from forum.utils import markup +from forum.utils.html import sanitize_html from django.utils import html import logging +from markdown2 import Markdown + +markdowner = Markdown(html4tags=True) #todo: following methods belong to a future common post class -def render_post_text_and_get_newly_mentioned_users(post, - urlize_content = False): +def parse_post_text(post): + """typically post has a field to store raw source text + in comment it is called .comment, in Question and Answer it is + called .text + also there is another field called .html (consistent across models) + so the goal of this function is to render raw text into .html + and extract any metadata given stored in source (currently + this metadata is limited by twitter style @mentions + but there may be more in the future + + so really it should be renamed into ..._and_get_meta_data + """ text = post.get_text() - if urlize_content: + if post._urlize: text = html.urlize(text) - if '@' not in text: - post.html = text - return list() - - from forum.models.user import Activity - - mentioned_by = post.get_last_author() - - op = post.get_origin_post() - anticipated_authors = op.get_author_list( include_comments = True, recursive = True ) - - extra_name_seeds = markup.extract_mentioned_name_seeds(text) - - extra_authors = set() - for name_seed in extra_name_seeds: - extra_authors.update(User.objects.filter(username__startswith = name_seed)) + if post._use_markdown: + text = sanitize_html(markdowner.convert(text)) - #it is important to preserve order here so that authors of post get mentioned first - anticipated_authors += list(extra_authors) + #todo, add markdown parser call conditional on + #post.use_markdown flag + post_html = text + mentioned_authors = list() + removed_mentions = list() + if '@' in text: + from forum.models.user import Activity - mentioned_authors, post.html = markup.mentionize_text(text, anticipated_authors) + mentioned_by = post.get_last_author() - #maybe delete some previous mentions - if post.id != None: - #only look for previous mentions if post was already saved before - prev_mention_qs = Activity.objects.get_mentions( - mentioned_in = post + op = post.get_origin_post() + anticipated_authors = op.get_author_list( + include_comments = True, + recursive = True ) - new_set = set(mentioned_authors) - for mention in prev_mention_qs: - delta_set = set(mention.receiving_users.all()) - new_set - if not delta_set: - mention.delete() - new_set -= delta_set - mentioned_authors = list(new_set) + extra_name_seeds = markup.extract_mentioned_name_seeds(text) - return mentioned_authors + extra_authors = set() + for name_seed in extra_name_seeds: + extra_authors.update(User.objects.filter( + username__startswith = name_seed + ) + ) -def save_content(self, urlize_content = False, **kwargs): + #it is important to preserve order here so that authors of post + #get mentioned first + anticipated_authors += list(extra_authors) + + mentioned_authors, post_html = markup.mentionize_text( + text, + anticipated_authors + ) + + #find mentions that were removed and identify any previously + #entered mentions so that we can send alerts on only new ones + if post.pk is not None: + #only look for previous mentions if post was already saved before + prev_mention_qs = Activity.objects.get_mentions( + mentioned_in = post + ) + new_set = set(mentioned_authors) + for prev_mention in prev_mention_qs: + + user = prev_mention.get_mentioned_user() + if user in new_set: + #don't report mention twice + new_set.remove(user) + else: + removed_mentions.append(prev_mention) + mentioned_authors = list(new_set) + + data = { + 'html': post_html, + 'newly_mentioned_users': mentioned_authors, + 'removed_mentions': removed_mentions, + } + return data + +def save_post(post, **kwargs): """generic save method to use with posts """ - new_mentions = self._render_text_and_get_newly_mentioned_users( - urlize_content - ) + data = post.parse() - from forum.models.user import Activity + post.html = data['html'] + newly_mentioned_users = data['newly_mentioned_users'] + removed_mentions = data['removed_mentions'] - #this save must precede saving the mention activity - super(self.__class__, self).save(**kwargs) + #a hack allowing to save denormalized .summary field for questions + if hasattr(post, 'summary'): + post.summary = strip_tags(post.html)[:120] + + #delete removed mentions + for rm in removed_mentions: + rm.delete() - post_author = self.get_last_author() + created = post.pk is None - for u in new_mentions: + #this save must precede saving the mention activity + #because generic relation needs primary key of the related object + super(post.__class__, post).save(**kwargs) + last_author = post.get_last_author() + + #create new mentions + for u in newly_mentioned_users: + from forum.models.user import Activity Activity.objects.create_new_mention( mentioned_whom = u, - mentioned_in = self, - mentioned_by = post_author + mentioned_in = post, + mentioned_by = last_author ) - #todo: this is handled in signal because models for posts #are too spread out from forum.models import signals signals.post_updated.send( - post = self, - newly_mentioned_users = new_mentions, - sender = self.__class__ + post = post, + newly_mentioned_users = newly_mentioned_users, + timestamp = post.get_time_of_last_edit(), + created = created, + sender = post.__class__ ) try: diff --git a/forum/models/content.py b/forum/models/content.py index 95064692..5f144f1e 100644 --- a/forum/models/content.py +++ b/forum/models/content.py @@ -1,9 +1,10 @@ +import datetime +import logging from django.contrib.auth.models import User from django.contrib.contenttypes import generic +from django.contrib.sitemaps import ping_google from django.db import models -from meta import Comment, Vote, FlaggedItem -import datetime -import logging +from forum.models.meta import Comment, Vote, FlaggedItem class Content(models.Model): """ @@ -35,6 +36,9 @@ class Content(models.Model): votes = generic.GenericRelation(Vote) flagged_items = generic.GenericRelation(FlaggedItem) + _use_markdown = True + _urlize = False + class Meta: abstract = True app_label = 'forum' @@ -80,6 +84,12 @@ class Content(models.Model): def get_last_author(self): return self.last_edited_by + def get_time_of_last_edit(self): + if self.last_edited_at: + return self.last_edited_at + else: + return self.added_at + def get_author_list(self, include_comments = False, recursive = False, exclude_list = None): authors = set() authors.update([r.author for r in self.revisions.all()]) @@ -108,7 +118,11 @@ class Content(models.Model): return True else: - raise Exception('unexpected User.tag_filter_setting %' % self.tag_filter_setting) + raise ValueError( + 'unexpected User.tag_filter_setting %s' \ + % 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 d0530694..5f36d76e 100644 --- a/forum/models/meta.py +++ b/forum/models/meta.py @@ -1,9 +1,7 @@ +import datetime from django.db import models -from base import MetaContent, UserContent -from base import render_post_text_and_get_newly_mentioned_users -from base import save_content from forum import const -import datetime +from forum.models import base class VoteManager(models.Manager): def get_up_vote_count_from_user(self, user): @@ -26,7 +24,7 @@ class VoteManager(models.Manager): return 0 -class Vote(MetaContent, UserContent): +class Vote(base.MetaContent, base.UserContent): VOTE_UP = +1 VOTE_DOWN = -1 VOTE_CHOICES = ( @@ -39,7 +37,7 @@ class Vote(MetaContent, UserContent): objects = VoteManager() - class Meta(MetaContent.Meta): + class Meta(base.MetaContent.Meta): unique_together = ('content_type', 'object_id', 'user') db_table = u'vote' @@ -65,25 +63,28 @@ class FlaggedItemManager(models.Manager): else: return 0 -class FlaggedItem(MetaContent, UserContent): +class FlaggedItem(base.MetaContent, base.UserContent): """A flag on a Question or Answer indicating offensive content.""" flagged_at = models.DateTimeField(default=datetime.datetime.now) objects = FlaggedItemManager() - class Meta(MetaContent.Meta): + class Meta(base.MetaContent.Meta): unique_together = ('content_type', 'object_id', 'user') db_table = u'flagged_item' def __unicode__(self): return '[%s] flagged at %s' %(self.user, self.flagged_at) -class Comment(MetaContent, UserContent): +class Comment(base.MetaContent, base.UserContent): comment = models.CharField(max_length = const.COMMENT_HARD_MAX_LENGTH) added_at = models.DateTimeField(default = datetime.datetime.now) html = models.CharField(max_length = const.COMMENT_HARD_MAX_LENGTH, default='') - class Meta(MetaContent.Meta): + _urlize = True + _use_markdown = False + + class Meta(base.MetaContent.Meta): ordering = ('-added_at',) db_table = u'comment' @@ -97,15 +98,13 @@ class Comment(MetaContent, UserContent): def set_text(self, text): self.comment = text - _render_text_and_get_newly_mentioned_users = \ - render_post_text_and_get_newly_mentioned_users - - _save = save_content + def parse(self): + return base.parse_post_text(self) def save(self,**kwargs): - self._save(urlize_content = True) + base.save_post(self) - def get_updated_activity_type(self): + def get_updated_activity_type(self, created = False): if self.content_object.__class__.__name__ == 'Question': return const.TYPE_ACTIVITY_COMMENT_QUESTION elif self.content_object.__class__.__name__ == 'Answer': @@ -123,6 +122,9 @@ class Comment(MetaContent, UserContent): users -= set([self.user])#remove activity user return list(users) + def get_time_of_last_edit(self): + return self.added_at + def delete(self, **kwargs): #todo: not very good import in models of other models #todo: potentially a circular import diff --git a/forum/models/question.py b/forum/models/question.py index 6a36e0aa..0d63426d 100644 --- a/forum/models/question.py +++ b/forum/models/question.py @@ -1,23 +1,20 @@ -from base import DeletableContent, AnonymousContent, ContentRevision -from content import Content -from forum.models import signals -from tag import Tag -from forum import const -from forum.utils.html import sanitize_html -from markdown2 import Markdown -from django.utils.html import strip_tags +import logging import datetime from django.conf import settings from django.utils.datastructures import SortedDict -from forum.models.tag import MarkedTag from django.db import models from django.contrib.auth.models import User -from django.utils.http import urlquote as django_urlquote +from django.utils.http import urlquote as django_urlquote from django.template.defaultfilters import slugify from django.core.urlresolvers import reverse - -markdowner = Markdown(html4tags=True) - +from django.contrib.sitemaps import ping_google +from django.utils.translation import ugettext as _ +from forum.models.tag import Tag, MarkedTag +from forum.models import signals +from forum.models.base import AnonymousContent, DeletableContent, ContentRevision +from forum.models.base import save_post, parse_post_text +from forum.models import content +from forum import const from forum.utils.lists import LazyList #todo: too bad keys are duplicated see const sort methods @@ -35,8 +32,7 @@ QUESTION_ORDER_BY_MAP = { class QuestionManager(models.Manager): def create_new(self, title=None,author=None,added_at=None, wiki=False,tagnames=None, text=None): - html = sanitize_html(markdowner.convert(text)) - summary = strip_tags(html)[:120] + question = Question( title = title, author = author, @@ -45,11 +41,15 @@ class QuestionManager(models.Manager): last_activity_by = author, wiki = wiki, tagnames = tagnames, - html = html, + #html field is denormalized in .save() call text = text, - summary = summary + #summary field is denormalized in .save() call ) if question.wiki: + #todo: this is confusing - last_edited_at field + #is used as an indicator whether question has been edited + #in template forum/skins/default/templates/post_contributor_info.html + #but in principle, post creation should count as edit as well question.last_edited_by = question.author question.last_edited_at = added_at question.wikified_at = added_at @@ -233,7 +233,7 @@ class QuestionManager(models.Manager): return False - #todo: why not make this into a method of class Question? + #todo: why not make this into a method of Question class? # also it is actually strange - why do we need the answer_count # field if the count depends on who is requesting this? def update_answer_count(self, question): @@ -244,7 +244,7 @@ class QuestionManager(models.Manager): # for some reasons, this Answer class failed to be imported, # although we have imported all classes from models on top. - from answer import Answer + from forum.models.answer import Answer self.filter(id=question.id).update( answer_count=Answer.objects.get_answers_from_question(question).filter(deleted=False).count()) @@ -285,7 +285,7 @@ class QuestionManager(models.Manager): return LazyList(get_data) -class Question(Content, DeletableContent): +class Question(content.Content, DeletableContent): title = models.CharField(max_length=300) tags = models.ManyToManyField('Tag', related_name='questions') answer_accepted = models.BooleanField(default=False) @@ -312,9 +312,11 @@ class Question(Content, DeletableContent): objects = QuestionManager() - class Meta(Content.Meta): + class Meta(content.Content.Meta): db_table = u'question' + parse = parse_post_text + def delete(self): super(Question, self).delete() try: @@ -335,8 +337,8 @@ class Question(Content, DeletableContent): # Update the Question's tag associations signals.tags_updated = self.objects.update_tags( self, - form.cleaned_data['tags'], - request.user + tagnames, + retagged_by ) # Create a new revision @@ -351,7 +353,7 @@ class Question(Content, DeletableContent): text = latest_revision.text ) # send tags updated singal - signals.tags_updated.send(sender=question.__class__, question=self) + signals.tags_updated.send(sender=Question, question=self) def get_origin_post(self): return self @@ -374,10 +376,6 @@ class Question(Content, DeletableContent): if edited_at is None: edited_at = datetime.datetime.now() - #todo: have this copy-paste in few places - html = sanitize_html(markdowner.convert(text)) - question_summary = strip_tags(html)[:120] - # Update the Question itself self.title = title self.last_edited_at = edited_at @@ -385,8 +383,6 @@ class Question(Content, DeletableContent): self.last_edited_by = edited_by self.last_activity_by = edited_by self.tagnames = tags - self.summary = question_summary - self.html = html self.text = text #wiki is an eternal trap whence there is no exit @@ -436,13 +432,15 @@ class Question(Content, DeletableContent): This is required as we're using ``tagnames`` as the sole means of adding and editing tags. """ - initial_addition = (self.id is None) - - super(Question, self).save(**kwargs) + initial_addition = (self.pk is None) + + save_post(self, **kwargs) if initial_addition: - tags = Tag.objects.get_or_create_multiple(self.tagname_list(), - self.author) + tags = Tag.objects.get_or_create_multiple( + self.tagname_list(), + self.author + ) self.tags.add(*tags) Tag.objects.update_use_counts(tags) @@ -454,7 +452,10 @@ class Question(Content, DeletableContent): return u','.join([unicode(tag) for tag in self.tagname_list()]) def get_absolute_url(self): - return '%s%s' % (reverse('question', args=[self.id]), django_urlquote(slugify(self.title))) + return '%s%s' % ( + reverse('question', args=[self.id]), + django_urlquote(slugify(self.title)) + ) def has_favorite_by_user(self, user): if not user.is_authenticated(): @@ -463,7 +464,7 @@ class Question(Content, DeletableContent): return FavoriteQuestion.objects.filter(question=self, user=user).count() > 0 def get_answer_count_by_user(self, user_id): - from answer import Answer + from forum.models.answer import Answer query_set = Answer.objects.filter(author__id=user_id) return query_set.filter(question=self).count() @@ -523,6 +524,7 @@ class Question(Content, DeletableContent): answer_comments.append(comment) #create the report + from forum.conf import settings as forum_settings if edited or new_answers or modified_answers or answer_comments: out = [] if edited: @@ -618,5 +620,3 @@ class AnonymousQuestion(AnonymousContent): text=self.text, ) self.delete() - -from answer import Answer, AnswerManager diff --git a/forum/models/tag.py b/forum/models/tag.py index 0e585547..89f706f5 100644 --- a/forum/models/tag.py +++ b/forum/models/tag.py @@ -1,9 +1,9 @@ -from base import DeletableContent from django.db import models from django.db import connection, transaction from django.contrib.auth.models import User - from django.utils.translation import ugettext as _ +from forum.models.base import DeletableContent + class TagManager(models.Manager): UPDATE_USED_COUNTS_QUERY = ( diff --git a/forum/models/user.py b/forum/models/user.py index c05d911b..083aca79 100644 --- a/forum/models/user.py +++ b/forum/models/user.py @@ -1,18 +1,17 @@ -#todo: remove this with Django 1.2 +from hashlib import md5 +import string +from random import Random +import datetime +import logging from django.db import models +from django.db.backends.dummy.base import IntegrityError from django.contrib.contenttypes.models import ContentType -from forum.models import signals from django.contrib.contenttypes import generic from django.contrib.auth.models import User -from hashlib import md5 -import string -from random import Random +from django.utils.translation import ugettext as _ 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): @@ -80,8 +79,12 @@ class ActivityManager(models.Manager): mentioned_whom = None, mentioned_at = None, mentioned_in = None, - reported = None + reported = None, + mentioned_at__gt = None, ): + """extract mention-type activity objects + todo: implement better rich field lookups + """ kwargs = dict() @@ -90,6 +93,8 @@ class ActivityManager(models.Manager): if mentioned_at: #todo: handle cases with rich lookups here like __lt kwargs['active_at'] = mentioned_at + elif mentioned_at__gt: + kwargs['active_at__gt'] = mentioned_at__gt if mentioned_by: kwargs['user'] = mentioned_by @@ -139,6 +144,12 @@ class Activity(models.Model): app_label = 'forum' db_table = u'activity' + def get_mentioned_user(self): + assert(self.activity_type == const.TYPE_ACTIVITY_MENTION) + user_qs = self.receiving_users.all() + assert(len(user_qs) == 1) + return user_qs[0] + class EmailFeedSettingManager(models.Manager): def exists_match_to_post_and_subscriber( |