diff options
-rw-r--r-- | askbot/models/__init__.py | 14 | ||||
-rw-r--r-- | askbot/models/base.py | 31 | ||||
-rw-r--r-- | askbot/models/meta.py | 21 | ||||
-rw-r--r-- | askbot/models/question.py | 2 | ||||
-rw-r--r-- | askbot/models/tag.py | 7 | ||||
-rw-r--r-- | askbot/skins/default/templates/user_profile/user_recent.html | 34 | ||||
-rw-r--r-- | askbot/skins/default/templates/user_profile/user_stats.html | 2 | ||||
-rw-r--r-- | askbot/tests/misc_tests.py | 47 | ||||
-rw-r--r-- | askbot/views/commands.py | 116 | ||||
-rw-r--r-- | askbot/views/users.py | 543 |
10 files changed, 265 insertions, 552 deletions
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index 7d251815..7fff58fd 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -312,7 +312,7 @@ def _assert_user_can( raise django_exceptions.PermissionDenied(error_message) def user_assert_can_unaccept_best_answer(self, answer = None): - assert(isinstance(answer, Answer)) + assert getattr(answer, 'post_type', '') == 'answer' blocked_error_message = _( 'Sorry, you cannot accept or unaccept best answers ' 'because your account is blocked' @@ -368,7 +368,7 @@ def user_assert_can_unaccept_best_answer(self, answer = None): raise django_exceptions.PermissionDenied(error_message) def user_assert_can_accept_best_answer(self, answer = None): - assert(isinstance(answer, Answer)) + assert getattr(answer, 'post_type', '') == 'answer' self.assert_can_unaccept_best_answer(answer) def user_assert_can_vote_for_post( @@ -601,12 +601,12 @@ def user_assert_can_edit_post(self, post = None): def user_assert_can_edit_question(self, question = None): - assert isinstance(question, Post) and question.post_type=='question' + assert getattr(question, 'post_type', '') == 'question' self.assert_can_edit_post(question) def user_assert_can_edit_answer(self, answer = None): - assert isinstance(answer, Post) and answer.post_type=='answer' + assert getattr(answer, 'post_type', '') == 'answer' self.assert_can_edit_post(answer) @@ -953,10 +953,12 @@ def user_post_comment( comment = body_text, added_at = timestamp, ) + if hasattr(comment, 'self_comment'): # Handle Post-s + comment = comment.self_comment award_badges_signal.send(None, event = 'post_comment', actor = self, - context_object = comment.self_comment, + context_object = comment, timestamp = timestamp ) return comment @@ -1407,7 +1409,7 @@ def user_post_answer( self.assert_can_post_answer() - if not isinstance(question, Post) or not question.post_type == 'question': + if getattr(question, 'post_type', '') != 'question': raise TypeError('question argument must be provided') if body_text is None: raise ValueError('Body text is required to post answer') diff --git a/askbot/models/base.py b/askbot/models/base.py index 5ac2ac7a..121b0182 100644 --- a/askbot/models/base.py +++ b/askbot/models/base.py @@ -5,8 +5,6 @@ import logging from django.db import models from django.utils.html import strip_tags from django.contrib.auth.models import User -from django.contrib.contenttypes import generic -from django.contrib.contenttypes.models import ContentType from django.contrib.sitemaps import ping_google #todo: maybe merge askbot.utils.markup and forum.utils.html @@ -188,35 +186,6 @@ class BaseQuerySetManager(models.Manager): except AttributeError: return getattr(self.get_query_set(), attr, *args) -class UserContent(models.Model): - user = models.ForeignKey(User, related_name='%(class)ss') - - class Meta: - abstract = True - app_label = 'askbot' - - -class MetaContent(models.Model): - """ - Base class for Vote and Comment - """ - content_type = models.ForeignKey(ContentType) - object_id = models.PositiveIntegerField() - content_object = generic.GenericForeignKey('content_type', 'object_id') - - class Meta: - abstract = True - app_label = 'askbot' - -class DeletableContent(models.Model): - deleted = models.BooleanField(default=False) - deleted_at = models.DateTimeField(null=True, blank=True) - deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_%(class)ss') - - class Meta: - abstract = True - app_label = 'askbot' - class AnonymousContent(models.Model): """ diff --git a/askbot/models/meta.py b/askbot/models/meta.py index 0a4fb349..6fc1e70d 100644 --- a/askbot/models/meta.py +++ b/askbot/models/meta.py @@ -1,4 +1,5 @@ import datetime +from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.db import models from django.utils import html as html_utils @@ -29,21 +30,26 @@ class VoteManager(models.Manager): return 0 -class Vote(base.MetaContent, base.UserContent): +class Vote(base.UserContent): VOTE_UP = +1 VOTE_DOWN = -1 VOTE_CHOICES = ( (VOTE_UP, u'Up'), (VOTE_DOWN, u'Down'), ) + user = models.ForeignKey('auth.User', related_name='votes') + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey('content_type', 'object_id') vote = models.SmallIntegerField(choices=VOTE_CHOICES) voted_at = models.DateTimeField(default=datetime.datetime.now) objects = VoteManager() - class Meta(base.MetaContent.Meta): + class Meta: unique_together = ('content_type', 'object_id', 'user') + app_label = 'askbot' db_table = u'vote' def __unicode__(self): @@ -86,7 +92,7 @@ class Vote(base.MetaContent, base.UserContent): #todo: move this class to content -class Comment(base.MetaContent, base.UserContent): +class Comment(base.UserContent): post_type = 'comment' comment = models.CharField(max_length = const.COMMENT_HARD_MAX_LENGTH) added_at = models.DateTimeField(default = datetime.datetime.now) @@ -94,13 +100,20 @@ class Comment(base.MetaContent, base.UserContent): score = models.IntegerField(default = 0) offensive_flag_count = models.IntegerField(default = 0) + user = models.ForeignKey('auth.User', related_name='comments') + + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey('content_type', 'object_id') + _urlize = True _use_markdown = True _escape_html = True is_anonymous = False #comments are never anonymous - may change - class Meta(base.MetaContent.Meta): + class Meta: ordering = ('-added_at',) + app_label = 'askbot' db_table = u'comment' #these two are methods diff --git a/askbot/models/question.py b/askbot/models/question.py index d0a30fcc..e9b732f6 100644 --- a/askbot/models/question.py +++ b/askbot/models/question.py @@ -919,7 +919,7 @@ class QuestionView(models.Model): class FavoriteQuestion(models.Model): """A favorite Question of a User.""" thread = models.ForeignKey(Thread) - user = models.ForeignKey(User, related_name='unused_user_favorite_questions') + user = models.ForeignKey(User, related_name='user_favorite_questions') added_at = models.DateTimeField(default=datetime.datetime.now) class Meta: diff --git a/askbot/models/tag.py b/askbot/models/tag.py index 7bd49d12..349b409b 100644 --- a/askbot/models/tag.py +++ b/askbot/models/tag.py @@ -124,9 +124,14 @@ class Tag(DeletableContent): # Denormalised data used_count = models.PositiveIntegerField(default=0) + deleted = models.BooleanField(default=False) + deleted_at = models.DateTimeField(null=True, blank=True) + deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_tags') + objects = TagManager() - class Meta(DeletableContent.Meta): + class Meta: + app_label = 'askbot' db_table = u'tag' ordering = ('-used_count', 'name') diff --git a/askbot/skins/default/templates/user_profile/user_recent.html b/askbot/skins/default/templates/user_profile/user_recent.html index 84d10357..09689419 100644 --- a/askbot/skins/default/templates/user_profile/user_recent.html +++ b/askbot/skins/default/templates/user_profile/user_recent.html @@ -13,25 +13,23 @@ </div> <div style="float:left;overflow:hidden;"> {% if act.is_badge %} - <a href="{{act.badge.get_absolute_url()}}" title="{{ act.badge.get_type_display() }} : {% trans description=act.badge.description %}{{description}}{% endtrans %}" class="medal"><span class="{{ act.badge.css_class }}">●</span> {% trans name=act.badge.name %}{{name}}{% endtrans %}</a> - {% if act.related_object_type == 'question' %}{# question #} - {% for question in questions %}{# could also create a new dict #} - {% if question.question_id == act.obj %} - (<a title="{{question.summary|collapse}}" - href="{% url question question.question_id %}{{question.thread.title|slugify}}">{% trans %}source{% endtrans %}</a>) - {% endif %} - {% endfor %} - {% elif act.related_object_type == 'answer' %}{# answer #} - {% for answer in answers %}{# could also create a new dict #} - {% if answer.answer_id == act.obj %} - (<a title="{{answer.text|collapse}}" - href="{% url question answer.question_id %}{{answer.question_title|slugify}}#{{answer.answer_id}}">{% trans %}source{% endtrans %}</a>) - {% endif %} - {% endfor %} - {% endif %} + <a href="{{act.badge.get_absolute_url()}}" + title="{{ act.badge.get_type_display() }} : {% trans description=act.badge.description %}{{description}}{% endtrans %}" + class="medal"> + <span class="{{ act.badge.css_class }}">●</span> {% trans name=act.badge.name %}{{name}}{% endtrans %} + </a> + {% if act.content_object.post_type == 'question' %} + {% set question=act.content_object %} + (<a title="{{question.summary|collapse}}" + href="{% url question question.id %}{{question.thread.title|slugify}}">{% trans %}source{% endtrans %}</a>) + {% elif act.content_object.post_type == 'answer' %} + {% set answer=act.content_object %} + (<a title="{{answer.text|collapse}}" + href="{% url question answer.question_id %}{{answer.question.thread.title|slugify}}#{{answer.id}}">{% trans %}source{% endtrans %}</a>) + {% endif %} {% else %} - <span class="post-type-{{ act.type_id }}"><a href="{{ act.title_link }}">{{ act.title|escape }}</a></span> - {% if act.summary %}<span class="revision-summary">{{ act.summary|escape }}</span>{% endif %} + <span class="post-type-{{ act.type_id }}"><a href="{{ act.title_link }}">{{ act.title|escape }}</a></span> + {% if act.summary %}<span class="revision-summary">{{ act.summary|escape }}</span>{% endif %} {% endif %} <div style="height:5px"></div> </div> diff --git a/askbot/skins/default/templates/user_profile/user_stats.html b/askbot/skins/default/templates/user_profile/user_stats.html index 0691dbad..95619560 100644 --- a/askbot/skins/default/templates/user_profile/user_stats.html +++ b/askbot/skins/default/templates/user_profile/user_stats.html @@ -114,7 +114,7 @@ </span> <ul id="badge-context-{{ badge.id }}" class="badge-context-list" style="display:none"> {% for award in badge_user_awards %} - {% if award.content_object %} + {% if award.content_object_is_post %} <li> <a title="{{ award.content_object.get_snippet()|collapse }}" diff --git a/askbot/tests/misc_tests.py b/askbot/tests/misc_tests.py index 306d62ce..3150c377 100644 --- a/askbot/tests/misc_tests.py +++ b/askbot/tests/misc_tests.py @@ -1,12 +1,4 @@ -import datetime -from django.contrib.contenttypes.models import ContentType -from django.test.client import Client from askbot.tests.utils import AskbotTestCase -from askbot.conf import settings -from askbot import models -from askbot.models.badges import award_badges_signal - -from askbot.views.users import get_related_object_type_name from askbot.models.post import PostRevision class MiscTests(AskbotTestCase): @@ -16,45 +8,6 @@ class MiscTests(AskbotTestCase): self.u2 = self.create_user(username='user2') self.u3 = self.create_user(username='user3') - def test_get_related_object_type_name_for_question(self): - question = self.post_question(user=self.u1) - #import ipdb; ipdb.set_trace() - ct = ContentType.objects.get_for_model(question) - self.assertEqual('question', get_related_object_type_name(ct.id, question.id)) - - def test_get_related_object_type_name_for_question_revision(self): - question = self.post_question(user=self.u1) - revision = question.revisions.all()[0] - ct = ContentType.objects.get_for_model(revision) - self.assertEqual('question', get_related_object_type_name(ct.id, revision.id)) - - def test_get_related_object_type_name_for_answer(self): - question = self.post_question(user=self.u1) - answer = self.post_answer(user=self.u1, question=question) - ct = ContentType.objects.get_for_model(answer) - self.assertEqual('answer', get_related_object_type_name(ct.id, answer.id)) - - def test_get_related_object_type_name_for_answer_revision(self): - question = self.post_question(user=self.u1) - answer = self.post_answer(user=self.u1, question=question) - revision = answer.revisions.all()[0] - ct = ContentType.objects.get_for_model(revision) - self.assertEqual('answer', get_related_object_type_name(ct.id, revision.id)) - - def test_get_related_object_type_name_for_anything_else_1(self): - ct = ContentType.objects.get_for_model(self.u2) - self.assertTrue( - get_related_object_type_name(ct.id, self.u2.id) is None - ) - - def test_get_related_object_type_name_for_anything_else_2(self): - question = self.post_question(user=self.u1) - comment = self.post_comment(user=self.u1, parent_post=question) - ct = ContentType.objects.get_for_model(comment) - self.assertTrue( - get_related_object_type_name(ct.id, comment.id) is None - ) - def test_proper_PostRevision_manager_is_used(self): "Makes sure that both normal and related managers for PostRevision don't implement .create() method" question = self.post_question(user=self.u1) diff --git a/askbot/views/commands.py b/askbot/views/commands.py index 2ef56731..e0e83209 100644 --- a/askbot/views/commands.py +++ b/askbot/views/commands.py @@ -25,6 +25,65 @@ from askbot.skins.loaders import render_into_skin from askbot import const import logging + +def manage_inbox(request): + """delete, mark as new or seen user's + response memo objects, excluding flags + request data is memo_list - list of integer id's of the ActivityAuditStatus items + and action_type - string - one of delete|mark_new|mark_seen + """ + + response_data = dict() + try: + if request.is_ajax(): + if request.method == 'POST': + post_data = simplejson.loads(request.raw_post_data) + if request.user.is_authenticated(): + activity_types = const.RESPONSE_ACTIVITY_TYPES_FOR_DISPLAY + activity_types += (const.TYPE_ACTIVITY_MENTION, ) + user = request.user + memo_set = models.ActivityAuditStatus.objects.filter( + id__in = post_data['memo_list'], + activity__activity_type__in = activity_types, + user = user + ) + + action_type = post_data['action_type'] + if action_type == 'delete': + memo_set.delete() + elif action_type == 'mark_new': + memo_set.update(status = models.ActivityAuditStatus.STATUS_NEW) + elif action_type == 'mark_seen': + memo_set.update(status = models.ActivityAuditStatus.STATUS_SEEN) + else: + raise exceptions.PermissionDenied( + _('Oops, apologies - there was some error') + ) + + user.update_response_counts() + + response_data['success'] = True + data = simplejson.dumps(response_data) + return HttpResponse(data, mimetype="application/json") + else: + raise exceptions.PermissionDenied( + _('Sorry, but anonymous users cannot access the inbox') + ) + else: + raise exceptions.PermissionDenied('must use POST request') + else: + #todo: show error page but no-one is likely to get here + return HttpResponseRedirect(reverse('index')) + except Exception, e: + message = unicode(e) + if message == '': + message = _('Oops, apologies - there was some error') + response_data['message'] = message + response_data['success'] = False + data = simplejson.dumps(response_data) + return HttpResponse(data, mimetype="application/json") + + def process_vote(user = None, vote_direction = None, post = None): """function (non-view) that actually processes user votes - i.e. up- or down- votes @@ -78,63 +137,6 @@ def process_vote(user = None, vote_direction = None, post = None): return response_data -def manage_inbox(request): - """delete, mark as new or seen user's - response memo objects, excluding flags - request data is memo_list - list of integer id's of the ActivityAuditStatus items - and action_type - string - one of delete|mark_new|mark_seen - """ - - response_data = dict() - try: - if request.is_ajax(): - if request.method == 'POST': - post_data = simplejson.loads(request.raw_post_data) - if request.user.is_authenticated(): - activity_types = const.RESPONSE_ACTIVITY_TYPES_FOR_DISPLAY - activity_types += (const.TYPE_ACTIVITY_MENTION, ) - user = request.user - memo_set = models.ActivityAuditStatus.objects.filter( - id__in = post_data['memo_list'], - activity__activity_type__in = activity_types, - user = user - ) - - action_type = post_data['action_type'] - if action_type == 'delete': - memo_set.delete() - elif action_type == 'mark_new': - memo_set.update(status = models.ActivityAuditStatus.STATUS_NEW) - elif action_type == 'mark_seen': - memo_set.update(status = models.ActivityAuditStatus.STATUS_SEEN) - else: - raise exceptions.PermissionDenied( - _('Oops, apologies - there was some error') - ) - - user.update_response_counts() - - response_data['success'] = True - data = simplejson.dumps(response_data) - return HttpResponse(data, mimetype="application/json") - else: - raise exceptions.PermissionDenied( - _('Sorry, but anonymous users cannot access the inbox') - ) - else: - raise exceptions.PermissionDenied('must use POST request') - else: - #todo: show error page but no-one is likely to get here - return HttpResponseRedirect(reverse('index')) - except Exception, e: - message = unicode(e) - if message == '': - message = _('Oops, apologies - there was some error') - response_data['message'] = message - response_data['success'] = False - data = simplejson.dumps(response_data) - return HttpResponse(data, mimetype="application/json") - def vote(request, id): """ diff --git a/askbot/views/users.py b/askbot/views/users.py index a0a16028..64a0d829 100644 --- a/askbot/views/users.py +++ b/askbot/views/users.py @@ -7,9 +7,11 @@ and other views showing profile-related information. Also this module includes the view listing all forum users. """ import calendar +import collections import functools import datetime import logging +import operator from django.db.models import Count, Q from django.conf import settings as django_settings @@ -37,18 +39,6 @@ from askbot.skins.loaders import render_into_skin from askbot.templatetags import extra_tags -#todo: queries in the user activity summary view must be redone -def get_related_object_type_name(content_type_id, object_id): - if content_type_id == ContentType.objects.get_for_model(models.Question).id: - return 'question' - elif content_type_id == ContentType.objects.get_for_model(models.Answer).id: - return 'answer' - elif content_type_id == ContentType.objects.get_for_model(models.PostRevision).id: - post_revision = models.PostRevision.objects.get(id=object_id) - return post_revision.revision_type_str() - - return None - def owner_or_moderator_required(f): @functools.wraps(f) def wrapped_func(request, profile_owner, context): @@ -317,15 +307,14 @@ def user_stats(request, user, context): # # INFO: There's bug in Django that makes the following query kind of broken (GROUP BY clause is problematic): # http://stackoverflow.com/questions/7973461/django-aggregation-does-excessive-group-by-clauses - # Fortunately it looks to return correct results for the test data + # Fortunately it looks like it returns correct results for the test data user_tags = models.Tag.objects.filter(threads__posts__author=user).\ annotate(user_tag_usage_count=Count('threads')).\ order_by('-user_tag_usage_count')[:const.USER_VIEW_DATA_SIZE] user_tags = list(user_tags) # evaluate - # - # Badges/Awards (TODO: refactor into Managers/QuerySets when a pattern emerges; Simplify when we get rid of Queastion&Answer models) + # Badges/Awards (TODO: refactor into Managers/QuerySets when a pattern emerges; Simplify when we get rid of Question&Answer models) # question_type = ContentType.objects.get_for_model(models.Question) answer_type = ContentType.objects.get_for_model(models.Answer) @@ -351,27 +340,24 @@ def user_stats(request, user, context): elif post.self_answer_id: awarded_answers_map[post.self_answer_id] = post - badges_dict = {} + badges_dict = collections.defaultdict(list) for award in user_awards: # Fetch content object if award.content_type_id == question_type.id: award.content_object = awarded_questions_map[award.object_id] + award.content_object_is_post = True elif award.content_type_id == answer_type.id: award.content_object = awarded_answers_map[award.object_id] + award.content_object_is_post = True else: - award.content_object = None + award.content_object_is_post = False # "Assign" to its Badge - if award not in badges_dict: - badges_dict[award.badge] = [award] - else: - badges_dict[award.badge].append(award) - - badges = list(badges_dict.items()) - badges.sort(key=lambda badge_tuple: len(badge_tuple[1]), reverse=True) + badges_dict[award.badge].append(award) - total_badges = len(badges) + badges = badges_dict.items() + badges.sort(key=operator.itemgetter(1), reverse=True) data = { 'active_tab':'users', @@ -398,7 +384,7 @@ def user_stats(request, user, context): 'user_tags' : user_tags, 'badges': badges, - 'total_badges' : total_badges, + 'total_badges' : len(badges), } context.update(data) @@ -411,7 +397,7 @@ def user_recent(request, user, context): if type_id in item: return item[1] - class Event: + class Event(object): is_badge = False def __init__(self, time, type, title, summary, answer_id, question_id): self.time = time @@ -427,271 +413,121 @@ def user_recent(request, user, context): if int(answer_id) > 0: self.title_link += '#%s' % answer_id - class AwardEvent: + class AwardEvent(object): is_badge = True - def __init__(self, time, obj, cont, type, id, related_object_type = None): + def __init__(self, time, type, content_object, badge): self.time = time - self.obj = obj - self.cont = cont self.type = get_type_name(type) - self.type_id = type - self.badge = get_object_or_404(models.BadgeData, id=id) - self.related_object_type = related_object_type + self.content_object = content_object + self.badge = badge activities = [] - # ask questions - questions = models.Activity.objects.extra( - select={ - 'title' : 'askbot_thread.title', - 'question_id' : 'question.id', - 'summary' : 'question.summary', - 'active_at' : 'activity.active_at', - 'activity_type' : 'activity.activity_type' - }, - tables=['activity', 'question', 'askbot_thread'], - where=['activity.content_type_id = %s AND activity.object_id = question.id AND askbot_thread.id = question.thread_id AND ' + - 'activity.user_id = %s AND activity.activity_type = %s AND NOT question.deleted'], - params=[ContentType.objects.get_for_model(models.Question).id, user.id, const.TYPE_ACTIVITY_ASK_QUESTION], - order_by=['-activity.active_at'] - ).values( - 'title', - 'question_id', - 'summary', - 'active_at', - 'activity_type' - ) - - for q in questions: - q_event = Event( - q['active_at'], - q['activity_type'], - q['title'], - '', - '0', - q['question_id'] - ) - activities.append(q_event) - - # answers - answers = models.Activity.objects.extra( - select={ - 'title' : 'askbot_thread.title', - 'question_id' : 'question.id', - 'summary' : 'question.summary', - 'answer_id' : 'answer.id', - 'active_at' : 'activity.active_at', - 'activity_type' : 'activity.activity_type' - }, - tables=['activity', 'answer', 'question', 'askbot_thread'], - where=['activity.content_type_id = %s AND activity.object_id = answer.id AND ' + - 'answer.question_id=question.id AND askbot_thread.id=question.thread_id AND NOT answer.deleted AND activity.user_id=%s AND '+ - 'activity.activity_type=%s AND NOT question.deleted'], - params=[ContentType.objects.get_for_model(models.Answer).id, user.id, const.TYPE_ACTIVITY_ANSWER], - order_by=['-activity.active_at'] - ).values( - 'title', - 'question_id', - 'summary', - 'answer_id', - 'active_at', - 'activity_type' - ) - if len(answers) > 0: - answer_activities = [(Event(q['active_at'], q['activity_type'], q['title'], '', q['answer_id'], \ - q['question_id'])) for q in answers] - activities.extend(answer_activities) - - # question comments - comments = models.Activity.objects.extra( - select={ - 'title' : 'askbot_thread.title', - 'question_id' : 'comment.object_id', - 'added_at' : 'comment.added_at', - 'activity_type' : 'activity.activity_type' - }, - tables=['activity', 'question', 'comment', 'askbot_thread'], - - where=['activity.content_type_id = %s AND activity.object_id = comment.id AND '+ - 'activity.user_id = comment.user_id AND comment.object_id=question.id AND askbot_thread.id=question.thread_id AND '+ - 'comment.content_type_id=%s AND activity.user_id = %s AND activity.activity_type=%s AND ' + - 'NOT question.deleted'], - params=[ContentType.objects.get_for_model(models.Comment).id, ContentType.objects.get_for_model(models.Question).id, user.id, const.TYPE_ACTIVITY_COMMENT_QUESTION], - order_by=['-comment.added_at'] - ).values( - 'title', - 'question_id', - 'added_at', - 'activity_type' - ) - - if len(comments) > 0: - comments = [(Event(q['added_at'], q['activity_type'], q['title'], '', '0', \ - q['question_id'])) for q in comments] - activities.extend(comments) - - # answer comments - comments = models.Activity.objects.extra( - select={ - 'title' : 'askbot_thread.title', - 'question_id' : 'question.id', - 'answer_id' : 'answer.id', - 'added_at' : 'comment.added_at', - 'activity_type' : 'activity.activity_type' - }, - tables=['activity', 'question', 'answer', 'comment', 'askbot_thread'], - - where=['activity.content_type_id = %s AND activity.object_id = comment.id AND '+ - 'activity.user_id = comment.user_id AND comment.object_id=answer.id AND '+ - 'comment.content_type_id=%s AND question.id = answer.question_id AND askbot_thread.id=question.thread_id AND '+ - 'activity.user_id = %s AND activity.activity_type=%s AND '+ - 'NOT answer.deleted AND NOT question.deleted'], - params=[ContentType.objects.get_for_model(models.Comment).id, ContentType.objects.get_for_model(models.Answer).id, user.id, const.TYPE_ACTIVITY_COMMENT_ANSWER], - order_by=['-comment.added_at'] - ).values( - 'title', - 'question_id', - 'answer_id', - 'added_at', - 'activity_type' - ) - - if len(comments) > 0: - comments = [(Event(q['added_at'], q['activity_type'], q['title'], '', q['answer_id'], \ - q['question_id'])) for q in comments] - activities.extend(comments) - - # question revisions - revisions = models.Activity.objects.extra( - select={ - 'title' : 'askbot_postrevision.title', - 'question_id' : 'askbot_postrevision.question_id', - 'added_at' : 'activity.active_at', - 'activity_type' : 'activity.activity_type', - 'summary' : 'askbot_postrevision.summary' - }, - tables=['activity', 'askbot_postrevision', 'question'], - where=[''' - activity.content_type_id=%s AND activity.object_id=askbot_postrevision.id AND - askbot_postrevision.question_id=question.id AND askbot_postrevision.revision_type=%s AND NOT question.deleted AND - activity.user_id=askbot_postrevision.author_id AND activity.user_id=%s AND - activity.activity_type=%s - '''], - params=[ContentType.objects.get_for_model(models.PostRevision).id, models.PostRevision.QUESTION_REVISION, user.id, const.TYPE_ACTIVITY_UPDATE_QUESTION], - order_by=['-activity.active_at'] - ).values( - 'title', - 'question_id', - 'added_at', - 'activity_type', - 'summary' - ) - - if len(revisions) > 0: - revisions = [(Event(q['added_at'], q['activity_type'], q['title'], q['summary'], '0', \ - q['question_id'])) for q in revisions] - activities.extend(revisions) - - # answer revisions - revisions = models.Activity.objects.extra( - select={ - 'title' : 'askbot_thread.title', - 'question_id' : 'question.id', - 'answer_id' : 'answer.id', - 'added_at' : 'activity.active_at', - 'activity_type' : 'activity.activity_type', - 'summary' : 'askbot_postrevision.summary' - }, - tables=['activity', 'askbot_postrevision', 'question', 'answer', 'askbot_thread'], - where=[''' - activity.content_type_id=%s AND activity.object_id=askbot_postrevision.id AND - askbot_postrevision.answer_id=answer.id AND askbot_postrevision.revision_type=%s AND - answer.question_id=question.id AND askbot_thread.id=question.thread_id AND NOT question.deleted AND NOT answer.deleted AND - activity.user_id=askbot_postrevision.author_id AND activity.user_id=%s AND - activity.activity_type=%s - '''], - params=[ContentType.objects.get_for_model(models.PostRevision).id, models.PostRevision.ANSWER_REVISION, user.id, const.TYPE_ACTIVITY_UPDATE_ANSWER], - order_by=['-activity.active_at'] - ).values( - 'title', - 'question_id', - 'added_at', - 'answer_id', - 'activity_type', - 'summary' - ) - - if len(revisions) > 0: - revisions = [(Event(q['added_at'], q['activity_type'], q['title'], q['summary'], \ - q['answer_id'], q['question_id'])) for q in revisions] - activities.extend(revisions) - - # accepted answers - accept_answers = models.Activity.objects.extra( - select={ - 'title' : 'askbot_thread.title', - 'question_id' : 'question.id', - 'added_at' : 'activity.active_at', - 'activity_type' : 'activity.activity_type', - }, - tables=['activity', 'answer', 'question', 'askbot_thread'], - where=['activity.content_type_id = %s AND activity.object_id = answer.id AND '+ - 'activity.user_id = question.author_id AND activity.user_id = %s AND '+ - 'NOT answer.deleted AND NOT question.deleted AND '+ - 'answer.question_id=question.id AND askbot_thread.id=question.thread_id AND activity.activity_type=%s'], - params=[ContentType.objects.get_for_model(models.Answer).id, user.id, const.TYPE_ACTIVITY_MARK_ANSWER], - order_by=['-activity.active_at'] - ).values( - 'title', - 'question_id', - 'added_at', - 'activity_type', - ) - if len(accept_answers) > 0: - accept_answers = [(Event(q['added_at'], q['activity_type'], q['title'], '', '0', \ - q['question_id'])) for q in accept_answers] - activities.extend(accept_answers) - #award history - awards = models.Activity.objects.extra( - select={ - 'badge_id' : 'askbot_badgedata.id', - 'awarded_at': 'award.awarded_at', - 'object_id': 'award.object_id', - 'content_type_id': 'award.content_type_id', - 'activity_type' : 'activity.activity_type' - }, - tables=['activity', 'award', 'askbot_badgedata'], - where=['activity.user_id = award.user_id AND activity.user_id = %s AND '+ - 'award.badge_id=askbot_badgedata.id AND activity.object_id=award.id AND activity.activity_type=%s'], - params=[user.id, const.TYPE_ACTIVITY_PRIZE], - order_by=['-activity.active_at'] - ).values( - 'badge_id', - 'awarded_at', - 'object_id', - 'content_type_id', - 'activity_type' - ) - for award in awards: - related_object_type = get_related_object_type_name( - content_type_id=award['content_type_id'], - object_id=award['object_id'] - ) - activities.append( - AwardEvent( - award['awarded_at'], - award['object_id'], - award['content_type_id'], - award['activity_type'], - award['badge_id'], - related_object_type = related_object_type - ) - ) - - activities.sort(lambda x,y: cmp(y.time, x.time)) + + # TODO: Convert to Post + + for activity in models.Activity.objects.filter(user=user): + + # TODO: multi-if means that we have here a construct for which a design pattern should be used + + # ask questions + if activity.activity_type == const.TYPE_ACTIVITY_ASK_QUESTION: + q = activity.content_object + if q.deleted: + activities.append(Event( + time=activity.active_at, + type=activity.activity_type, + title=q.thread.title, + summary=q.summary, # TODO: was set to '' before, but that was probably wrong + answer_id=0, + question_id=q.id + )) + + elif activity.activity_type == const.TYPE_ACTIVITY_ANSWER: + ans = activity.content_object + if not ans.deleted and not ans.question.deleted: + activities.append(Event( + time=activity.active_at, + type=activity.activity_type, + title=ans.question.thread.title, + summary=ans.question.summary, + answer_id=ans.id, + question_id=ans.question.id + )) + + elif activity.activity_type == const.TYPE_ACTIVITY_COMMENT_QUESTION: + cm = activity.content_object + q = cm.content_object + if not q.deleted: + activities.append(Event( + time=cm.added_at, + type=activity.activity_type, + title=q.thread.title, + summary='', + answer_id=0, + question_id=q.id + )) + + elif activity.activity_type == const.TYPE_ACTIVITY_COMMENT_ANSWER: + cm = activity.content_object + ans = cm.content_object + if not ans.deleted and not ans.question.deleted: + activities.append(Event( + time=cm.added_at, + type=activity.activity_type, + title=ans.question.thread.title, + summary='', + answer_id=ans.id, + question_id=ans.question.id + )) + + elif activity.activity_type == const.TYPE_ACTIVITY_UPDATE_QUESTION: + q = activity.content_object + if not q.deleted: + activities.append(Event( + time=activity.active_at, + type=activity.activity_type, + title=q.thread.title, + summary=q.summary, + answer_id=0, + question_id=q.id + )) + + elif activity.activity_type == const.TYPE_ACTIVITY_UPDATE_ANSWER: + ans = activity.content_object + if not ans.deleted and not ans.question.deleted: + activities.append(Event( + time=activity.active_at, + type=activity.activity_type, + title=ans.question.thread.title, + summary=ans.summary, + answer_id=ans.id, + question_id=ans.question.id + )) + + elif activity.activity_type == const.TYPE_ACTIVITY_MARK_ANSWER: + ans = activity.content_object + if not ans.deleted and not ans.question.deleted: + activities.append(Event( + time=activity.active_at, + type=activity.activity_type, + title=ans.question.thread.title, + summary='', + answer_id=0, + question_id=ans.question.id + )) + + elif activity.activity_type == const.TYPE_ACTIVITY_PRIZE: + award = activity.content_object + activities.append(AwardEvent( + time=award.awarded_at, + type=activity.activity_type, + content_object=award.content_object, + badge=award.badge, + )) + + activities.sort(key=operator.attrgetter('time'), reverse=True) data = { - 'answers': answers, - 'questions': questions, 'active_tab': 'users', 'page_class': 'user-profile-page', 'tab_name' : 'recent', @@ -728,12 +564,10 @@ def user_responses(request, user, context): user = request.user, activity__activity_type__in = activity_types ).select_related( - 'activity__active_at', - 'activity__object_id', + 'activity', 'activity__content_type', - 'activity__question__thread__title', - 'activity__user__username', - 'activity__user__id', + 'activity__question__thread', + 'activity__user', 'activity__user__gravatar', ).order_by( '-activity__active_at' @@ -801,57 +635,23 @@ def user_network(request, user, context): return render_into_skin('user_profile/user_network.html', context, request) @owner_or_moderator_required -def user_votes(request, user, context): - +def user_votes(request, user, context): # TODO: Convert to Post, but first migrate Vote to using Post + all_votes = list(models.Vote.objects.filter(user=user)) votes = [] - question_votes = models.Vote.objects.extra( - select={ - 'title' : 'askbot_thread.title', - 'question_id' : 'question.id', - 'answer_id' : 0, - 'voted_at' : 'vote.voted_at', - 'vote' : 'vote', - }, - select_params=[user.id], - tables=['vote', 'question', 'auth_user', 'askbot_thread'], - where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = question.id AND askbot_thread.id=question.thread_id '+ - 'AND vote.user_id=auth_user.id'], - params=[ContentType.objects.get_for_model(models.Question).id, user.id], - order_by=['-vote.id'] - ).values( - 'title', - 'question_id', - 'answer_id', - 'voted_at', - 'vote', - ) - if(len(question_votes) > 0): - votes.extend(question_votes) - - answer_votes = models.Vote.objects.extra( - select={ - 'title' : 'askbot_thread.title', - 'question_id' : 'question.id', - 'answer_id' : 'answer.id', - 'voted_at' : 'vote.voted_at', - 'vote' : 'vote', - }, - select_params=[user.id], - tables=['vote', 'answer', 'question', 'auth_user', 'askbot_thread'], - where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = answer.id '+ - 'AND answer.question_id = question.id AND askbot_thread.id=question.thread_id AND vote.user_id=auth_user.id'], - params=[ContentType.objects.get_for_model(models.Answer).id, user.id], - order_by=['-vote.id'] - ).values( - 'title', - 'question_id', - 'answer_id', - 'voted_at', - 'vote', - ) - if(len(answer_votes) > 0): - votes.extend(answer_votes) - votes.sort(lambda x,y: cmp(y['voted_at'], x['voted_at'])) + for vote in all_votes: + obj = vote.content_object + if isinstance(obj, models.Question): + vote.title = obj.thread.title + vote.question_id = obj.id + vote.answer_id = 0 + votes.append(vote) + elif isinstance(obj, models.Answer): + vote.title = obj.question.thread.title + vote.question_id = obj.question.id + vote.answer_id = obj.id + votes.append(vote) + + votes.sort(key=operator.attrgetter('id'), reverse=True) data = { 'active_tab':'users', @@ -864,28 +664,14 @@ def user_votes(request, user, context): context.update(data) return render_into_skin('user_profile/user_votes.html', context, request) + def user_reputation(request, user, context): - reputes = models.Repute.objects.filter(user=user).order_by('-reputed_at') - #select_related() adds stuff needed for the query - reputes = reputes.select_related( - 'question__thread__title', - 'question__id', - 'user__username' - ) - #prepare data for the graph - rep_list = [] - #last values go in first - rep_list.append('[%s,%s]' % ( - calendar.timegm( - datetime.datetime.now().timetuple() - ) * 1000, - user.reputation - ) - ) - #ret remaining values in + reputes = models.Repute.objects.filter(user=user).select_related('question', 'question__thread', 'user').order_by('-reputed_at') + + # prepare data for the graph - last values go in first + rep_list = ['[%s,%s]' % (calendar.timegm(datetime.datetime.now().timetuple()) * 1000, user.reputation)] for rep in reputes: - dic = '[%s,%s]' % (calendar.timegm(rep.reputed_at.timetuple()) * 1000, rep.reputation) - rep_list.append(dic) + rep_list.append('[%s,%s]' % (calendar.timegm(rep.reputed_at.timetuple()) * 1000, rep.reputation)) reps = ','.join(rep_list) reps = '[%s]' % reps @@ -901,22 +687,13 @@ def user_reputation(request, user, context): context.update(data) return render_into_skin('user_profile/user_reputation.html', context, request) + def user_favorites(request, user, context): - favorited_thread_id_list= models.FavoriteQuestion.objects.filter( - user = user - ).values_list('thread__id', flat=True) - questions = models.Question.objects.filter( - thread__id__in=favorited_thread_id_list - ).order_by( - '-score', '-thread__last_activity_at' - ).select_related( - 'thread__last_activity_by__id', - 'thread__last_activity_by__username', - 'thread__last_activity_by__reputation', - 'thread__last_activity_by__gold', - 'thread__last_activity_by__silver', - 'thread__last_activity_by__bronze' - )[:const.USER_VIEW_DATA_SIZE] + favorite_threads = user.user_favorite_questions.values_list('thread', flat=True) + questions = models.Post.objects.filter(post_type='question', thread__in=favorite_threads)\ + .select_related('thread', 'thread__last_activity_by')\ + .order_by('-score', '-thread__last_activity_at')[:const.USER_VIEW_DATA_SIZE] + data = { 'active_tab':'users', 'page_class': 'user-profile-page', @@ -924,12 +701,11 @@ def user_favorites(request, user, context): 'tab_description' : _('users favorite questions'), 'page_title' : _('profile - favorite questions'), 'questions' : questions, -# Commented out, doesn't seem to be used -# 'favorited_myself': favorited_q_id_list, } context.update(data) return render_into_skin('user_profile/user_favorites.html', context, request) + @owner_or_moderator_required @csrf.csrf_protect def user_email_subscriptions(request, user, context): @@ -984,7 +760,7 @@ def user_email_subscriptions(request, user, context): request ) -user_view_call_table = { +USER_VIEW_CALL_TABLE = { 'stats': user_stats, 'recent': user_recent, 'inbox': user_responses, @@ -1006,16 +782,10 @@ def user(request, id, slug=None, tab_name=None): """ profile_owner = get_object_or_404(models.User, id = id) - if tab_name is None: - #sort CGI parameter tells us which tab in the user - #profile to show, the default one is 'stats' + if not tab_name: tab_name = request.GET.get('sort', 'stats') - if tab_name in user_view_call_table: - #get the actual view function - user_view_func = user_view_call_table[tab_name] - else: - user_view_func = user_stats + user_view_func = USER_VIEW_CALL_TABLE.get(tab_name, user_stats) context = { 'view_user': profile_owner, @@ -1023,6 +793,7 @@ def user(request, id, slug=None, tab_name=None): } return user_view_func(request, profile_owner, context) + def update_has_custom_avatar(request): """updates current avatar type data for the user """ |