From 09b805b00f25fe6a5509334f9091332d89e7d40d Mon Sep 17 00:00:00 2001 From: Adolfo Fitoria Date: Thu, 6 Aug 2009 00:00:13 +0800 Subject: fixing if and elses that have issues with python 2.4 Signed-off-by: Mike Chen --- forum/auth.py | 5 ++++- forum/models.py | 12 ++++++++++-- forum/views.py | 10 ++++++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/forum/auth.py b/forum/auth.py index 0608031a..082f5186 100644 --- a/forum/auth.py +++ b/forum/auth.py @@ -167,7 +167,10 @@ def can_upload_files(request_user): ########################################### def calculate_reputation(origin, offset): result = int(origin) + int(offset) - return result if result > 0 else 1 + if (result > 0): + return result + else: + return 1 @transaction.commit_on_success def onFlaggedItem(item, post, user): diff --git a/forum/models.py b/forum/models.py index 570db274..ee7e59a1 100644 --- a/forum/models.py +++ b/forum/models.py @@ -173,7 +173,10 @@ class Question(models.Model): attr = CONST['deleted'] else: attr = None - return u'%s %s' % (self.title, attr) if attr is not None else self.title + if (attr is not None): + return u'%s %s' % (self.title, attr) + else: + self.title def get_revision_url(self): return reverse('question_revisions', args=[self.id]) @@ -517,7 +520,12 @@ def record_comment_event(instance, created, **kwargs): from django.contrib.contenttypes.models import ContentType question_type = ContentType.objects.get_for_model(Question) question_type_id = question_type.id - type = TYPE_ACTIVITY_COMMENT_QUESTION if instance.content_type_id == question_type_id else TYPE_ACTIVITY_COMMENT_ANSWER + #python 2.4 issues - Adolfo Fitoria + #type = TYPE_ACTIVITY_COMMENT_QUESTION if instance.content_type_id == question_type_id else TYPE_ACTIVITY_COMMENT_ANSWER + if (instance.content_type_id == question_type_id): + type = TYPE_ACTIVITY_COMMENT_QUESTION + else: + type = TYPE_ACTIVITY_COMMENT_ANSWER activity = Activity(user=instance.user, active_at=instance.added_at, content_object=instance, activity_type=type) activity.save() diff --git a/forum/views.py b/forum/views.py index 08a0e958..5d377c3f 100644 --- a/forum/views.py +++ b/forum/views.py @@ -1037,8 +1037,14 @@ def user_recent(request, user_id, user_view): self.type_id = type self.title = title self.summary = summary - self.title_link = u'/questions/%s/%s#%s' %(question_id, title, answer_id)\ - if int(answer_id) > 0 else u'/questions/%s/%s' %(question_id, title) + #python 2.4 issue - Adolfo Fitoria + #self.title_link = u'/questions/%s/%s#%s' %(question_id, title, answer_id)\ + # if int(answer_id) > 0 else u'/questions/%s/%s' %(question_id, title) + if (int(answer_id)>0): + self.title_link = u'/questions/%s/%s#%s' %(question_id, title, answer_id) + else: + self.title_link = u'/questions/%s/%s' %(question_id, title) + class AwardEvent: def __init__(self, time, type, id): self.time = time -- cgit v1.2.3-1-g7c22 From 64b2939bfa1e60e61b51d8f59ddd40429bec1068 Mon Sep 17 00:00:00 2001 From: Mike Chen Date: Sun, 9 Aug 2009 21:44:05 +0800 Subject: merge of Evgenys changes on annoymous post --- TODO | 4 ---- cnprog.wsgi | 8 -------- drop-all-tables.sh | 4 ---- rmpyc | 1 - 4 files changed, 17 deletions(-) delete mode 100644 TODO delete mode 100644 cnprog.wsgi delete mode 100644 drop-all-tables.sh delete mode 100644 rmpyc diff --git a/TODO b/TODO deleted file mode 100644 index 3769fa08..00000000 --- a/TODO +++ /dev/null @@ -1,4 +0,0 @@ -*check change email function - there is a strange 'password' field in form -*make reusable question-block template for index questions unanswered - get rid of copy-paste -*unused votes count in user profile not working - left that commented out in templates/user_info.html -*badge award notification messages need to be set fixed at place where badges are awarded diff --git a/cnprog.wsgi b/cnprog.wsgi deleted file mode 100644 index a1bd8039..00000000 --- a/cnprog.wsgi +++ /dev/null @@ -1,8 +0,0 @@ -import os -import sys - -sys.path.append('/var/www/vhosts') -os.environ['DJANGO_SETTINGS_MODULE'] = 'cnprog.settings' - -import django.core.handlers.wsgi -application = django.core.handlers.wsgi.WSGIHandler() diff --git a/drop-all-tables.sh b/drop-all-tables.sh deleted file mode 100644 index 87783b77..00000000 --- a/drop-all-tables.sh +++ /dev/null @@ -1,4 +0,0 @@ -mysql_username='cnprog' -mysql_database='cnprog' -mysqldump -u $mysql_username -p --add-drop-table --no-data $mysql_database | grep ^DROP -#| mysql -u[USERNAME] -p[PASSWORD] [DATABASE] diff --git a/rmpyc b/rmpyc deleted file mode 100644 index 014575f6..00000000 --- a/rmpyc +++ /dev/null @@ -1 +0,0 @@ -rm `find . -name '*.pyc'` -- cgit v1.2.3-1-g7c22 -- cgit v1.2.3-1-g7c22 From b55d90628557be0d06035add1577410a74e3b3aa Mon Sep 17 00:00:00 2001 From: Mike Chen Date: Sun, 9 Aug 2009 22:15:28 +0800 Subject: changes to unix mode --- forum/auth.py | 443 ++++++++++++ forum/models.py | 808 +++++++++++++++++++++ forum/views.py | 2084 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 3335 insertions(+) diff --git a/forum/auth.py b/forum/auth.py index e69de29b..36ca54d3 100644 --- a/forum/auth.py +++ b/forum/auth.py @@ -0,0 +1,443 @@ +""" +Authorisation related functions. + +The actions a User is authorised to perform are dependent on their reputation +and superuser status. +""" +import datetime +from django.contrib.contenttypes.models import ContentType +from django.db import transaction +from models import Repute +from models import Question +from models import Answer +from const import TYPE_REPUTATION +question_type = ContentType.objects.get_for_model(Question) +answer_type = ContentType.objects.get_for_model(Answer) + +VOTE_UP = 15 +FLAG_OFFENSIVE = 15 +POST_IMAGES = 15 +LEAVE_COMMENTS = 50 +UPLOAD_FILES = 60 +VOTE_DOWN = 100 +CLOSE_OWN_QUESTIONS = 250 +RETAG_OTHER_QUESTIONS = 500 +REOPEN_OWN_QUESTIONS = 500 +EDIT_COMMUNITY_WIKI_POSTS = 750 +EDIT_OTHER_POSTS = 2000 +DELETE_COMMENTS = 2000 +VIEW_OFFENSIVE_FLAGS = 2000 +DISABLE_URL_NOFOLLOW = 2000 +CLOSE_OTHER_QUESTIONS = 3000 +LOCK_POSTS = 4000 + +VOTE_RULES = { + 'scope_votes_per_user_per_day' : 30, # how many votes of one user has everyday + 'scope_flags_per_user_per_day' : 5, # how many times user can flag posts everyday + 'scope_warn_votes_left' : 10, # start when to warn user how many votes left + 'scope_deny_unvote_days' : 1, # if 1 days passed, user can't cancel votes. + 'scope_flags_invisible_main_page' : 3, # post doesn't show on main page if has more than 3 offensive flags + 'scope_flags_delete_post' : 5, # post will be deleted if it has more than 5 offensive flags +} + +REPUTATION_RULES = { + 'initial_score' : 1, + 'scope_per_day_by_upvotes' : 200, + 'gain_by_upvoted' : 10, + 'gain_by_answer_accepted' : 15, + 'gain_by_accepting_answer' : 2, + 'gain_by_downvote_canceled' : 2, + 'gain_by_canceling_downvote' : 1, + 'lose_by_canceling_accepted_answer' : -2, + 'lose_by_accepted_answer_cancled' : -15, + 'lose_by_downvoted' : -2, + 'lose_by_flagged' : -2, + 'lose_by_downvoting' : -1, + 'lose_by_flagged_lastrevision_3_times': -30, + 'lose_by_flagged_lastrevision_5_times': -100, + 'lose_by_upvote_canceled' : -10, +} + +def can_vote_up(user): + """Determines if a User can vote Questions and Answers up.""" + return user.is_authenticated() and ( + user.reputation >= VOTE_UP or + user.is_superuser) + +def can_flag_offensive(user): + """Determines if a User can flag Questions and Answers as offensive.""" + return user.is_authenticated() and ( + user.reputation >= FLAG_OFFENSIVE or + user.is_superuser) + +def can_add_comments(user): + """Determines if a User can add comments to Questions and Answers.""" + return user.is_authenticated() and ( + user.reputation >= LEAVE_COMMENTS or + user.is_superuser) + +def can_vote_down(user): + """Determines if a User can vote Questions and Answers down.""" + return user.is_authenticated() and ( + user.reputation >= VOTE_DOWN or + user.is_superuser) + +def can_retag_questions(user): + """Determines if a User can retag Questions.""" + return user.is_authenticated() and ( + RETAG_OTHER_QUESTIONS <= user.reputation < EDIT_OTHER_POSTS or + user.is_superuser) + +def can_edit_post(user, post): + """Determines if a User can edit the given Question or Answer.""" + return user.is_authenticated() and ( + user.id == post.author_id or + (post.wiki and user.reputation >= EDIT_COMMUNITY_WIKI_POSTS) or + user.reputation >= EDIT_OTHER_POSTS or + user.is_superuser) + +def can_delete_comment(user, comment): + """Determines if a User can delete the given Comment.""" + return user.is_authenticated() and ( + user.id == comment.user_id or + user.reputation >= DELETE_COMMENTS or + user.is_superuser) + +def can_view_offensive_flags(user): + """Determines if a User can view offensive flag counts.""" + return user.is_authenticated() and ( + user.reputation >= VIEW_OFFENSIVE_FLAGS or + user.is_superuser) + +def can_close_question(user, question): + """Determines if a User can close the given Question.""" + return user.is_authenticated() and ( + (user.id == question.author_id and + user.reputation >= CLOSE_OWN_QUESTIONS) or + user.reputation >= CLOSE_OTHER_QUESTIONS or + user.is_superuser) + +def can_lock_posts(user): + """Determines if a User can lock Questions or Answers.""" + return user.is_authenticated() and ( + user.reputation >= LOCK_POSTS or + user.is_superuser) + +def can_follow_url(user): + """Determines if the URL link can be followed by Google search engine.""" + return user.reputation >= DISABLE_URL_NOFOLLOW + +def can_accept_answer(user, question, answer): + return (user.is_authenticated() and + question.author != answer.author and + question.author == user) or user.is_superuser + +# now only support to reopen own question except superuser +def can_reopen_question(user, question): + return (user.is_authenticated() and + user.id == question.author_id and + user.reputation >= REOPEN_OWN_QUESTIONS) or user.is_superuser + +def can_delete_post(user, post): + return (user.is_authenticated() and + user.id == post.author_id) or user.is_superuser + +def can_view_deleted_post(user, post): + return user.is_superuser + +# user preferences view permissions +def is_user_self(request_user, target_user): + return (request_user.is_authenticated() and request_user == target_user) + +def can_view_user_votes(request_user, target_user): + return (request_user.is_authenticated() and request_user == target_user) + +def can_view_user_preferences(request_user, target_user): + return (request_user.is_authenticated() and request_user == target_user) + +def can_view_user_edit(request_user, target_user): + return (request_user.is_authenticated() and request_user == target_user) + +def can_upload_files(request_user): + return (request_user.is_authenticated() and request_user.reputation >= UPLOAD_FILES) or \ + request_user.is_superuser + +########################################### +## actions and reputation changes event +########################################### +def calculate_reputation(origin, offset): + result = int(origin) + int(offset) + return result if result > 0 else 1 + +@transaction.commit_on_success +def onFlaggedItem(item, post, user): + + item.save() + post.offensive_flag_count = post.offensive_flag_count + 1 + post.save() + + post.author.reputation = calculate_reputation(post.author.reputation, + int(REPUTATION_RULES['lose_by_flagged'])) + post.author.save() + + question = post + if ContentType.objects.get_for_model(post) == answer_type: + question = post.question + + reputation = Repute(user=post.author, + negative=int(REPUTATION_RULES['lose_by_flagged']), + question=question, reputed_at=datetime.datetime.now(), + reputation_type=-4, + reputation=post.author.reputation) + reputation.save() + + #todo: These should be updated to work on same revisions. + if post.offensive_flag_count == VOTE_RULES['scope_flags_invisible_main_page'] : + post.author.reputation = calculate_reputation(post.author.reputation, + int(REPUTATION_RULES['lose_by_flagged_lastrevision_3_times'])) + post.author.save() + + reputation = Repute(user=post.author, + negative=int(REPUTATION_RULES['lose_by_flagged_lastrevision_3_times']), + question=question, + reputed_at=datetime.datetime.now(), + reputation_type=-6, + reputation=post.author.reputation) + reputation.save() + + elif post.offensive_flag_count == VOTE_RULES['scope_flags_delete_post']: + post.author.reputation = calculate_reputation(post.author.reputation, + int(REPUTATION_RULES['lose_by_flagged_lastrevision_5_times'])) + post.author.save() + + reputation = Repute(user=post.author, + negative=int(REPUTATION_RULES['lose_by_flagged_lastrevision_5_times']), + question=question, + reputed_at=datetime.datetime.now(), + reputation_type=-7, + reputation=post.author.reputation) + reputation.save() + + post.deleted = True + #post.deleted_at = datetime.datetime.now() + #post.deleted_by = Admin + post.save() + + +@transaction.commit_on_success +def onAnswerAccept(answer, user): + answer.accepted = True + answer.accepted_at = datetime.datetime.now() + answer.question.answer_accepted = True + answer.save() + answer.question.save() + + answer.author.reputation = calculate_reputation(answer.author.reputation, + int(REPUTATION_RULES['gain_by_answer_accepted'])) + answer.author.save() + reputation = Repute(user=answer.author, + positive=int(REPUTATION_RULES['gain_by_answer_accepted']), + question=answer.question, + reputed_at=datetime.datetime.now(), + reputation_type=2, + reputation=answer.author.reputation) + reputation.save() + + user.reputation = calculate_reputation(user.reputation, + int(REPUTATION_RULES['gain_by_accepting_answer'])) + user.save() + reputation = Repute(user=user, + positive=int(REPUTATION_RULES['gain_by_accepting_answer']), + question=answer.question, + reputed_at=datetime.datetime.now(), + reputation_type=3, + reputation=user.reputation) + reputation.save() + +@transaction.commit_on_success +def onAnswerAcceptCanceled(answer, user): + answer.accepted = False + answer.accepted_at = None + answer.question.answer_accepted = False + answer.save() + answer.question.save() + + answer.author.reputation = calculate_reputation(answer.author.reputation, + int(REPUTATION_RULES['lose_by_accepted_answer_cancled'])) + answer.author.save() + reputation = Repute(user=answer.author, + negative=int(REPUTATION_RULES['lose_by_accepted_answer_cancled']), + question=answer.question, + reputed_at=datetime.datetime.now(), + reputation_type=-2, + reputation=answer.author.reputation) + reputation.save() + + user.reputation = calculate_reputation(user.reputation, + int(REPUTATION_RULES['lose_by_canceling_accepted_answer'])) + user.save() + reputation = Repute(user=user, + negative=int(REPUTATION_RULES['lose_by_canceling_accepted_answer']), + question=answer.question, + reputed_at=datetime.datetime.now(), + reputation_type=-1, + reputation=user.reputation) + reputation.save() + +@transaction.commit_on_success +def onUpVoted(vote, post, user): + vote.save() + + post.vote_up_count = int(post.vote_up_count) + 1 + post.score = int(post.score) + 1 + post.save() + + if not post.wiki: + author = post.author + if Repute.objects.get_reputation_by_upvoted_today(author) < int(REPUTATION_RULES['scope_per_day_by_upvotes']): + author.reputation = calculate_reputation(author.reputation, + int(REPUTATION_RULES['gain_by_upvoted'])) + author.save() + + question = post + if ContentType.objects.get_for_model(post) == answer_type: + question = post.question + + reputation = Repute(user=author, + positive=int(REPUTATION_RULES['gain_by_upvoted']), + question=question, + reputed_at=datetime.datetime.now(), + reputation_type=1, + reputation=author.reputation) + reputation.save() + +@transaction.commit_on_success +def onUpVotedCanceled(vote, post, user): + vote.delete() + + post.vote_up_count = int(post.vote_up_count) - 1 + if post.vote_up_count < 0: + post.vote_up_count = 0 + post.score = int(post.score) - 1 + post.save() + + if not post.wiki: + author = post.author + author.reputation = calculate_reputation(author.reputation, + int(REPUTATION_RULES['lose_by_upvote_canceled'])) + author.save() + + question = post + if ContentType.objects.get_for_model(post) == answer_type: + question = post.question + + reputation = Repute(user=author, + negative=int(REPUTATION_RULES['lose_by_upvote_canceled']), + question=question, + reputed_at=datetime.datetime.now(), + reputation_type=-8, + reputation=author.reputation) + reputation.save() + +@transaction.commit_on_success +def onDownVoted(vote, post, user): + vote.save() + + post.vote_down_count = int(post.vote_down_count) + 1 + post.score = int(post.score) - 1 + post.save() + + if not post.wiki: + author = post.author + author.reputation = calculate_reputation(author.reputation, + int(REPUTATION_RULES['lose_by_downvoted'])) + author.save() + + question = post + if ContentType.objects.get_for_model(post) == answer_type: + question = post.question + + reputation = Repute(user=author, + negative=int(REPUTATION_RULES['lose_by_downvoted']), + question=question, + reputed_at=datetime.datetime.now(), + reputation_type=-3, + reputation=author.reputation) + reputation.save() + + user.reputation = calculate_reputation(user.reputation, + int(REPUTATION_RULES['lose_by_downvoting'])) + user.save() + + reputation = Repute(user=user, + negative=int(REPUTATION_RULES['lose_by_downvoting']), + question=question, + reputed_at=datetime.datetime.now(), + reputation_type=-5, + reputation=user.reputation) + reputation.save() + +@transaction.commit_on_success +def onDownVotedCanceled(vote, post, user): + vote.delete() + + post.vote_down_count = int(post.vote_down_count) - 1 + if post.vote_down_count < 0: + post.vote_down_count = 0 + post.score = post.score + 1 + post.save() + + if not post.wiki: + author = post.author + author.reputation = calculate_reputation(author.reputation, + int(REPUTATION_RULES['gain_by_downvote_canceled'])) + author.save() + + question = post + if ContentType.objects.get_for_model(post) == answer_type: + question = post.question + + reputation = Repute(user=author, + positive=int(REPUTATION_RULES['gain_by_downvote_canceled']), + question=question, + reputed_at=datetime.datetime.now(), + reputation_type=4, + reputation=author.reputation) + reputation.save() + + user.reputation = calculate_reputation(user.reputation, + int(REPUTATION_RULES['gain_by_canceling_downvote'])) + user.save() + + reputation = Repute(user=user, + positive=int(REPUTATION_RULES['gain_by_canceling_downvote']), + question=question, + reputed_at=datetime.datetime.now(), + reputation_type=5, + reputation=user.reputation) + reputation.save() + +def onDeleteCanceled(post, user): + post.deleted = False + post.deleted_by = None + post.deleted_at = None + post.save() + for tag in list(post.tags.all()): + if tag.used_count == 1 and tag.deleted: + tag.deleted = False + tag.deleted_by = None + tag.deleted_at = None + tag.save() + +def onDeleted(post, user): + post.deleted = True + post.deleted_by = user + post.deleted_at = datetime.datetime.now() + post.save() + + for tag in list(post.tags.all()): + if tag.used_count == 1: + tag.deleted = True + tag.deleted_by = user + tag.deleted_at = datetime.datetime.now() + tag.save() diff --git a/forum/models.py b/forum/models.py index e69de29b..255eb21f 100644 --- a/forum/models.py +++ b/forum/models.py @@ -0,0 +1,808 @@ +# encoding:utf-8 +import datetime +import hashlib +from urllib import quote_plus, urlencode +from django.db import models +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.template.defaultfilters import slugify +from django.db.models.signals import post_delete, post_save, pre_save +from django.utils.translation import ugettext as _ +import django.dispatch +import settings + +from forum.managers import * +from const import * + +class EmailFeed(models.Model): + #subscription key for unsubscribe by visiting emailed link + key = models.CharField(max_length=32) + #generic relation with feed content (i.e. question or tags) + feed_content_type = models.ForeignKey(ContentType,related_name='content_emailfeed') + feed_id = models.PositiveIntegerField() + content = generic.GenericForeignKey('feed_content_type','feed_id') + #generic relation with owner - either nameless email or User + subscriber_content_type = models.ForeignKey(ContentType,related_name='subscriber_emailfeed') + subscriber_id = models.PositiveIntegerField() + subscriber = generic.GenericForeignKey('subscriber_content_type','subscriber_id') + added_at = models.DateTimeField(default=datetime.datetime.now) + reported_at = models.DateTimeField(default=datetime.datetime.now) + + #getter functions rely on implementations of similar functions in content + #of subscriber objects + def get_update_summary(self): + return self.content.get_update_summary(last_reported_at = self.reported_at,recipient_email = self.get_email()) + + def get_email(self): + return self.subscriber.email + +class Tag(models.Model): + name = models.CharField(max_length=255, unique=True) + created_by = models.ForeignKey(User, related_name='created_tags') + 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') + email_feeds = generic.GenericRelation(EmailFeed) + # Denormalised data + used_count = models.PositiveIntegerField(default=0) + + objects = TagManager() + + class Meta: + db_table = u'tag' + ordering = ('-used_count', 'name') + + def __unicode__(self): + return self.name + +class Comment(models.Model): + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey('content_type', 'object_id') + user = models.ForeignKey(User, related_name='comments') + comment = models.CharField(max_length=300) + added_at = models.DateTimeField(default=datetime.datetime.now) + + class Meta: + ordering = ('-added_at',) + db_table = u'comment' + def __unicode__(self): + return self.comment + +class Vote(models.Model): + VOTE_UP = +1 + VOTE_DOWN = -1 + VOTE_CHOICES = ( + (VOTE_UP, u'Up'), + (VOTE_DOWN, u'Down'), + ) + + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey('content_type', 'object_id') + user = models.ForeignKey(User, related_name='votes') + vote = models.SmallIntegerField(choices=VOTE_CHOICES) + voted_at = models.DateTimeField(default=datetime.datetime.now) + + objects = VoteManager() + + class Meta: + unique_together = ('content_type', 'object_id', 'user') + db_table = u'vote' + def __unicode__(self): + return '[%s] voted at %s: %s' %(self.user, self.voted_at, self.vote) + + def is_upvote(self): + return self.vote == self.VOTE_UP + + def is_downvote(self): + return self.vote == self.VOTE_DOWN + +class FlaggedItem(models.Model): + """A flag on a Question or Answer indicating offensive content.""" + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey('content_type', 'object_id') + user = models.ForeignKey(User, related_name='flagged_items') + flagged_at = models.DateTimeField(default=datetime.datetime.now) + + objects = FlaggedItemManager() + + class 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 Question(models.Model): + title = models.CharField(max_length=300) + author = models.ForeignKey(User, related_name='questions') + added_at = models.DateTimeField(default=datetime.datetime.now) + tags = models.ManyToManyField(Tag, related_name='questions') + # Status + wiki = models.BooleanField(default=False) + wikified_at = models.DateTimeField(null=True, blank=True) + answer_accepted = models.BooleanField(default=False) + closed = models.BooleanField(default=False) + closed_by = models.ForeignKey(User, null=True, blank=True, related_name='closed_questions') + closed_at = models.DateTimeField(null=True, blank=True) + close_reason = models.SmallIntegerField(choices=CLOSE_REASONS, null=True, blank=True) + 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_questions') + locked = models.BooleanField(default=False) + locked_by = models.ForeignKey(User, null=True, blank=True, related_name='locked_questions') + locked_at = models.DateTimeField(null=True, blank=True) + # Denormalised data + score = models.IntegerField(default=0) + vote_up_count = models.IntegerField(default=0) + vote_down_count = models.IntegerField(default=0) + answer_count = models.PositiveIntegerField(default=0) + comment_count = models.PositiveIntegerField(default=0) + view_count = models.PositiveIntegerField(default=0) + offensive_flag_count = models.SmallIntegerField(default=0) + favourite_count = models.PositiveIntegerField(default=0) + last_edited_at = models.DateTimeField(null=True, blank=True) + last_edited_by = models.ForeignKey(User, null=True, blank=True, related_name='last_edited_questions') + last_activity_at = models.DateTimeField(default=datetime.datetime.now) + last_activity_by = models.ForeignKey(User, related_name='last_active_in_questions') + tagnames = models.CharField(max_length=125) + summary = models.CharField(max_length=180) + html = models.TextField() + comments = generic.GenericRelation(Comment) + votes = generic.GenericRelation(Vote) + flagged_items = generic.GenericRelation(FlaggedItem) + email_feeds = generic.GenericRelation(EmailFeed) + + objects = QuestionManager() + + def save(self, **kwargs): + """ + Overridden to manually manage addition of tags when the object + is first saved. + + 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) + if initial_addition: + tags = Tag.objects.get_or_create_multiple(self.tagname_list(), + self.author) + self.tags.add(*tags) + Tag.objects.update_use_counts(tags) + + def tagname_list(self): + """Creates a list of Tag names from the ``tagnames`` attribute.""" + return [name for name in self.tagnames.split(u' ')] + + def get_absolute_url(self): + return '%s%s' % (reverse('question', args=[self.id]), self.title.replace(' ', '-')) + + def has_favorite_by_user(self, user): + if not user.is_authenticated(): + return False + return FavoriteQuestion.objects.filter(question=self, user=user).count() > 0 + + def get_answer_count_by_user(self, user_id): + query_set = Answer.objects.filter(author__id=user_id) + return query_set.filter(question=self).count() + + def get_question_title(self): + if self.closed: + attr = CONST['closed'] + elif self.deleted: + attr = CONST['deleted'] + else: + attr = None + if attr is not None: + return u'%s %s' % (self.title, attr) + else: + return self.title + + def get_revision_url(self): + return reverse('question_revisions', args=[self.id]) + + def get_latest_revision(self): + return self.revisions.all()[0] + + def get_update_summary(self,last_reported_at=None,recipient_email=''): + edited = False + if self.last_edited_at and self.last_edited_at > last_reported_at: + if self.last_edited_by.email != recipient_email: + edited = True + comments = [] + for comment in self.comments.all(): + if comment.added_at > last_reported_at and comment.user.email != recipient_email: + comments.append(comment) + new_answers = [] + answer_comments = [] + modified_answers = [] + commented_answers = [] + import sets + commented_answers = sets.Set([]) + for answer in self.answers.all(): + if (answer.added_at > last_reported_at and answer.author.email != recipient_email): + new_answers.append(answer) + if (answer.last_edited_at + and answer.last_edited_at > last_reported_at + and answer.last_edited_by.email != recipient_email): + modified_answers.append(answer) + for comment in answer.comments.all(): + if comment.added_at > last_reported_at and comment.user.email != recipient_email: + commented_answers.add(answer) + answer_comments.append(comment) + + #create the report + if edited or comments or new_answers or modified_answers or answer_comments: + out = [] + if edited: + out.append(_('%(author)s modified the question') % {'author':self.last_edited_by.username}) + if new_answers: + names = sets.Set(map(lambda x: x.author.username,new_answers)) + people = ', '.join(names) + out.append(_('%(people)s posted %(new_answer_count)s new answers') \ + % {'new_answer_count':len(new_answers),'people':people}) + if comments: + names = sets.Set(map(lambda x: x.user.username,comments)) + people = ', '.join(names) + out.append(_('%(people)s commented the question') % {'people':people}) + if answer_comments: + names = sets.Set(map(lambda x: x.user.username,answer_comments)) + people = ', '.join(names) + if len(commented_answers) > 1: + out.append(_('%(people)s commented answers') % {'people':people}) + else: + out.append(_('%(people)s commented an answer') % {'people':people}) + url = settings.APP_URL + self.get_absolute_url() + retval = '%s:
\n' % (url,self.title) + out = map(lambda x: '
  • ' + x + '
  • ',out) + retval += '
      ' + '\n'.join(out) + '

    \n' + return retval + else: + return None + + def __unicode__(self): + return self.title + + class Meta: + db_table = u'question' + +class QuestionRevision(models.Model): + """A revision of a Question.""" + question = models.ForeignKey(Question, related_name='revisions') + revision = models.PositiveIntegerField(blank=True) + title = models.CharField(max_length=300) + author = models.ForeignKey(User, related_name='question_revisions') + revised_at = models.DateTimeField() + tagnames = models.CharField(max_length=125) + summary = models.CharField(max_length=300, blank=True) + text = models.TextField() + + class Meta: + db_table = u'question_revision' + ordering = ('-revision',) + + def get_question_title(self): + return self.question.title + + def get_absolute_url(self): + return '/questions/%s/revisions' % (self.question.id) + + def save(self, **kwargs): + """Looks up the next available revision number.""" + if not self.revision: + self.revision = QuestionRevision.objects.filter( + question=self.question).values_list('revision', + flat=True)[0] + 1 + super(QuestionRevision, self).save(**kwargs) + + def __unicode__(self): + return u'revision %s of %s' % (self.revision, self.title) + +class AnonymousAnswer(models.Model): + question = models.ForeignKey(Question, related_name='anonymous_answers') + session_key = models.CharField(max_length=40) #session id for anonymous questions + wiki = models.BooleanField(default=False) + added_at = models.DateTimeField(default=datetime.datetime.now) + ip_addr = models.IPAddressField(max_length=21) #allow high port numbers + author = models.ForeignKey(User,null=True) + text = models.TextField() + summary = models.CharField(max_length=180) + + def publish(self,user): + from forum.views import create_new_answer + added_at = datetime.datetime.now() + print user.id + create_new_answer(question=self.question,wiki=self.wiki, + added_at=added_at,text=self.text, + author=user) + self.delete() + +class AnonymousQuestion(models.Model): + title = models.CharField(max_length=300) + session_key = models.CharField(max_length=40) #session id for anonymous questions + text = models.TextField() + summary = models.CharField(max_length=180) + tagnames = models.CharField(max_length=125) + wiki = models.BooleanField(default=False) + added_at = models.DateTimeField(default=datetime.datetime.now) + ip_addr = models.IPAddressField(max_length=21) #allow high port numbers + author = models.ForeignKey(User,null=True) + + def publish(self,user): + from forum.views import create_new_question + added_at = datetime.datetime.now() + create_new_question(title=self.title, author=user, added_at=added_at, + wiki=self.wiki, tagnames=self.tagnames, + summary=self.summary, text=self.text) + self.delete() + +class Answer(models.Model): + question = models.ForeignKey(Question, related_name='answers') + author = models.ForeignKey(User, related_name='answers') + added_at = models.DateTimeField(default=datetime.datetime.now) + # Status + wiki = models.BooleanField(default=False) + wikified_at = models.DateTimeField(null=True, blank=True) + accepted = models.BooleanField(default=False) + accepted_at = models.DateTimeField(null=True, blank=True) + deleted = models.BooleanField(default=False) + deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_answers') + locked = models.BooleanField(default=False) + locked_by = models.ForeignKey(User, null=True, blank=True, related_name='locked_answers') + locked_at = models.DateTimeField(null=True, blank=True) + # Denormalised data + score = models.IntegerField(default=0) + vote_up_count = models.IntegerField(default=0) + vote_down_count = models.IntegerField(default=0) + comment_count = models.PositiveIntegerField(default=0) + offensive_flag_count = models.SmallIntegerField(default=0) + last_edited_at = models.DateTimeField(null=True, blank=True) + last_edited_by = models.ForeignKey(User, null=True, blank=True, related_name='last_edited_answers') + html = models.TextField() + comments = generic.GenericRelation(Comment) + votes = generic.GenericRelation(Vote) + flagged_items = generic.GenericRelation(FlaggedItem) + + objects = AnswerManager() + + def get_user_vote(self, user): + votes = self.votes.filter(user=user) + if votes.count() > 0: + return votes[0] + else: + return None + + def get_latest_revision(self): + return self.revisions.all()[0] + + def get_question_title(self): + return self.question.title + + def get_absolute_url(self): + return '%s%s#%s' % (reverse('question', args=[self.question.id]), self.question.title, self.id) + + class Meta: + db_table = u'answer' + + def __unicode__(self): + return self.html + +class AnswerRevision(models.Model): + """A revision of an Answer.""" + answer = models.ForeignKey(Answer, related_name='revisions') + revision = models.PositiveIntegerField() + author = models.ForeignKey(User, related_name='answer_revisions') + revised_at = models.DateTimeField() + summary = models.CharField(max_length=300, blank=True) + text = models.TextField() + + def get_absolute_url(self): + return '/answers/%s/revisions' % (self.answer.id) + + def get_question_title(self): + return self.answer.question.title + + class Meta: + db_table = u'answer_revision' + ordering = ('-revision',) + + def save(self, **kwargs): + """Looks up the next available revision number if not set.""" + if not self.revision: + self.revision = AnswerRevision.objects.filter( + answer=self.answer).values_list('revision', + flat=True)[0] + 1 + super(AnswerRevision, self).save(**kwargs) + +class FavoriteQuestion(models.Model): + """A favorite Question of a User.""" + question = models.ForeignKey(Question) + user = models.ForeignKey(User, related_name='user_favorite_questions') + added_at = models.DateTimeField(default=datetime.datetime.now) + class Meta: + db_table = u'favorite_question' + def __unicode__(self): + return '[%s] favorited at %s' %(self.user, self.added_at) + +class Badge(models.Model): + """Awarded for notable actions performed on the site by Users.""" + GOLD = 1 + SILVER = 2 + BRONZE = 3 + TYPE_CHOICES = ( + (GOLD, _('gold')), + (SILVER, _('silver')), + (BRONZE, _('bronze')), + ) + + name = models.CharField(max_length=50) + type = models.SmallIntegerField(choices=TYPE_CHOICES) + slug = models.SlugField(max_length=50, blank=True) + description = models.CharField(max_length=300) + multiple = models.BooleanField(default=False) + # Denormalised data + awarded_count = models.PositiveIntegerField(default=0) + + class Meta: + db_table = u'badge' + ordering = ('name',) + unique_together = ('name', 'type') + + def __unicode__(self): + return u'%s: %s' % (self.get_type_display(), self.name) + + def save(self, **kwargs): + if not self.slug: + self.slug = self.name#slugify(self.name) + super(Badge, self).save(**kwargs) + + def get_absolute_url(self): + return '%s%s/' % (reverse('badge', args=[self.id]), self.slug) + +class Award(models.Model): + """The awarding of a Badge to a User.""" + user = models.ForeignKey(User, related_name='award_user') + badge = models.ForeignKey(Badge, related_name='award_badge') + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey('content_type', 'object_id') + awarded_at = models.DateTimeField(default=datetime.datetime.now) + notified = models.BooleanField(default=False) + objects = AwardManager() + + def __unicode__(self): + return u'[%s] is awarded a badge [%s] at %s' % (self.user.username, self.badge.name, self.awarded_at) + + class Meta: + db_table = u'award' + +class Repute(models.Model): + """The reputation histories for user""" + user = models.ForeignKey(User) + positive = models.SmallIntegerField(default=0) + negative = models.SmallIntegerField(default=0) + question = models.ForeignKey(Question) + reputed_at = models.DateTimeField(default=datetime.datetime.now) + reputation_type = models.SmallIntegerField(choices=TYPE_REPUTATION) + reputation = models.IntegerField(default=1) + objects = ReputeManager() + + def __unicode__(self): + return u'[%s]\' reputation changed at %s' % (self.user.username, self.reputed_at) + + class Meta: + db_table = u'repute' + +class Activity(models.Model): + """ + We keep some history data for user activities + """ + user = models.ForeignKey(User) + activity_type = models.SmallIntegerField(choices=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) + + def __unicode__(self): + return u'[%s] was active at %s' % (self.user.username, self.active_at) + + class Meta: + db_table = u'activity' + +class Book(models.Model): + """ + Model for book info + """ + user = models.ForeignKey(User) + title = models.CharField(max_length=255) + short_name = models.CharField(max_length=255) + author = models.CharField(max_length=255) + price = models.DecimalField(max_digits=6, decimal_places=2) + pages = models.SmallIntegerField() + published_at = models.DateTimeField() + publication = models.CharField(max_length=255) + cover_img = models.CharField(max_length=255) + tagnames = models.CharField(max_length=125) + added_at = models.DateTimeField() + last_edited_at = models.DateTimeField() + questions = models.ManyToManyField(Question, related_name='book', db_table='book_question') + + def get_absolute_url(self): + return '%s' % reverse('book', args=[self.short_name]) + + def __unicode__(self): + return self.title + class Meta: + db_table = u'book' + +class BookAuthorInfo(models.Model): + """ + Model for book author info + """ + user = models.ForeignKey(User) + book = models.ForeignKey(Book) + blog_url = models.CharField(max_length=255) + added_at = models.DateTimeField() + last_edited_at = models.DateTimeField() + + class Meta: + db_table = u'book_author_info' + +class BookAuthorRss(models.Model): + """ + Model for book author blog rss + """ + user = models.ForeignKey(User) + book = models.ForeignKey(Book) + title = models.CharField(max_length=255) + url = models.CharField(max_length=255) + rss_created_at = models.DateTimeField() + added_at = models.DateTimeField() + + class Meta: + db_table = u'book_author_rss' + +class AnonymousEmail(models.Model): + #validation key, if used + key = models.CharField(max_length=32) + email = models.EmailField(null=False,unique=True) + isvalid = models.BooleanField(default=False) + feeds = generic.GenericRelation(EmailFeed) + +# User extend properties +QUESTIONS_PER_PAGE_CHOICES = ( + (10, u'10'), + (30, u'30'), + (50, u'50'), +) + +User.add_to_class('email_isvalid', models.BooleanField(default=False)) +User.add_to_class('email_key', models.CharField(max_length=16, null=True)) +User.add_to_class('reputation', models.PositiveIntegerField(default=1)) +User.add_to_class('gravatar', models.CharField(max_length=32)) +User.add_to_class('email_feeds', generic.GenericRelation(EmailFeed)) +User.add_to_class('favorite_questions', + models.ManyToManyField(Question, through=FavoriteQuestion, + related_name='favorited_by')) +User.add_to_class('badges', models.ManyToManyField(Badge, through=Award, + related_name='awarded_to')) +User.add_to_class('gold', models.SmallIntegerField(default=0)) +User.add_to_class('silver', models.SmallIntegerField(default=0)) +User.add_to_class('bronze', models.SmallIntegerField(default=0)) +User.add_to_class('questions_per_page', + models.SmallIntegerField(choices=QUESTIONS_PER_PAGE_CHOICES, default=10)) +User.add_to_class('last_seen', + models.DateTimeField(default=datetime.datetime.now)) +User.add_to_class('real_name', models.CharField(max_length=100, blank=True)) +User.add_to_class('website', models.URLField(max_length=200, blank=True)) +User.add_to_class('location', models.CharField(max_length=100, blank=True)) +User.add_to_class('date_of_birth', models.DateField(null=True, blank=True)) +User.add_to_class('about', models.TextField(blank=True)) + +# custom signal +tags_updated = django.dispatch.Signal(providing_args=["question"]) +edit_question_or_answer = django.dispatch.Signal(providing_args=["instance", "modified_by"]) +delete_post_or_answer = django.dispatch.Signal(providing_args=["instance", "deleted_by"]) +mark_offensive = django.dispatch.Signal(providing_args=["instance", "mark_by"]) +user_updated = django.dispatch.Signal(providing_args=["instance", "updated_by"]) +user_logged_in = django.dispatch.Signal(providing_args=["session"]) + + +def get_messages(self): + messages = [] + for m in self.message_set.all(): + messages.append(m.message) + return messages + +def delete_messages(self): + self.message_set.all().delete() + +def get_profile_url(self): + """Returns the URL for this User's profile.""" + return '%s%s/' % (reverse('user', args=[self.id]), self.username) +User.add_to_class('get_profile_url', get_profile_url) +User.add_to_class('get_messages', get_messages) +User.add_to_class('delete_messages', delete_messages) + +def calculate_gravatar_hash(instance, **kwargs): + """Calculates a User's gravatar hash from their email address.""" + if kwargs.get('raw', False): + return + instance.gravatar = hashlib.md5(instance.email).hexdigest() + +def record_ask_event(instance, created, **kwargs): + if created: + activity = Activity(user=instance.author, active_at=instance.added_at, content_object=instance, activity_type=TYPE_ACTIVITY_ASK_QUESTION) + activity.save() + +def record_answer_event(instance, created, **kwargs): + if created: + activity = Activity(user=instance.author, active_at=instance.added_at, content_object=instance, activity_type=TYPE_ACTIVITY_ANSWER) + activity.save() + +def record_comment_event(instance, created, **kwargs): + if created: + from django.contrib.contenttypes.models import ContentType + question_type = ContentType.objects.get_for_model(Question) + question_type_id = question_type.id + type = TYPE_ACTIVITY_COMMENT_QUESTION if instance.content_type_id == question_type_id else TYPE_ACTIVITY_COMMENT_ANSWER + activity = Activity(user=instance.user, active_at=instance.added_at, content_object=instance, activity_type=type) + activity.save() + +def record_revision_question_event(instance, created, **kwargs): + if created and instance.revision <> 1: + activity = Activity(user=instance.author, active_at=instance.revised_at, content_object=instance, activity_type=TYPE_ACTIVITY_UPDATE_QUESTION) + activity.save() + +def record_revision_answer_event(instance, created, **kwargs): + if created and instance.revision <> 1: + activity = Activity(user=instance.author, active_at=instance.revised_at, content_object=instance, activity_type=TYPE_ACTIVITY_UPDATE_ANSWER) + activity.save() + +def record_award_event(instance, created, **kwargs): + """ + After we awarded a badge to user, we need to record this activity and notify user. + We also recaculate awarded_count of this badge and user information. + """ + if created: + activity = Activity(user=instance.user, active_at=instance.awarded_at, content_object=instance, + activity_type=TYPE_ACTIVITY_PRIZE) + activity.save() + + instance.badge.awarded_count += 1 + instance.badge.save() + + if instance.badge.type == Badge.GOLD: + instance.user.gold += 1 + if instance.badge.type == Badge.SILVER: + instance.user.silver += 1 + if instance.badge.type == Badge.BRONZE: + instance.user.bronze += 1 + instance.user.save() + +def notify_award_message(instance, created, **kwargs): + """ + Notify users when they have been awarded badges by using Django message. + """ + if created: + user = instance.user + user.message_set.create(message=u"%s" % instance.badge.name) + +def record_answer_accepted(instance, created, **kwargs): + """ + when answer is accepted, we record this for question author - who accepted it. + """ + if not created and instance.accepted: + activity = Activity(user=instance.question.author, active_at=datetime.datetime.now(), \ + content_object=instance, activity_type=TYPE_ACTIVITY_MARK_ANSWER) + activity.save() + +def update_last_seen(instance, created, **kwargs): + """ + when user has activities, we update 'last_seen' time stamp for him + """ + user = instance.user + user.last_seen = datetime.datetime.now() + user.save() + +def record_vote(instance, created, **kwargs): + """ + when user have voted + """ + if created: + if instance.vote == 1: + vote_type = TYPE_ACTIVITY_VOTE_UP + else: + vote_type = TYPE_ACTIVITY_VOTE_DOWN + + activity = Activity(user=instance.user, active_at=instance.voted_at, content_object=instance, activity_type=vote_type) + activity.save() + +def record_cancel_vote(instance, **kwargs): + """ + when user canceled vote, the vote will be deleted. + """ + activity = Activity(user=instance.user, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_CANCEL_VOTE) + activity.save() + +def record_delete_question(instance, delete_by, **kwargs): + """ + when user deleted the question + """ + if instance.__class__ == "Question": + activity_type = TYPE_ACTIVITY_DELETE_QUESTION + else: + activity_type = TYPE_ACTIVITY_DELETE_ANSWER + + activity = Activity(user=delete_by, active_at=datetime.datetime.now(), content_object=instance, activity_type=activity_type) + activity.save() + +def record_mark_offensive(instance, mark_by, **kwargs): + activity = Activity(user=mark_by, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_MARK_OFFENSIVE) + activity.save() + +def record_update_tags(question, **kwargs): + """ + when user updated tags of the question + """ + activity = Activity(user=question.author, active_at=datetime.datetime.now(), content_object=question, activity_type=TYPE_ACTIVITY_UPDATE_TAGS) + activity.save() + +def record_favorite_question(instance, created, **kwargs): + """ + when user add the question in him favorite questions list. + """ + if created: + activity = Activity(user=instance.user, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_FAVORITE) + activity.save() + +def record_user_full_updated(instance, **kwargs): + activity = Activity(user=instance, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_USER_FULL_UPDATED) + activity.save() + +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) + import settings + if settings.EMAIL_VALIDATION == 'on':#add user to the record + for aq in aq_list: + aq.author = user + aq.save() + for aa in aa_list: + aa.author = user + aa.save() + #maybe add pending posts message? + else: #just publish the questions + for aq in aq_list: + aq.publish(user) + for aa in aa_list: + aa.publish(user) + +#signal for User modle save changes +pre_save.connect(calculate_gravatar_hash, sender=User) +post_save.connect(record_ask_event, sender=Question) +post_save.connect(record_answer_event, sender=Answer) +post_save.connect(record_comment_event, sender=Comment) +post_save.connect(record_revision_question_event, sender=QuestionRevision) +post_save.connect(record_revision_answer_event, sender=AnswerRevision) +post_save.connect(record_award_event, sender=Award) +post_save.connect(notify_award_message, sender=Award) +post_save.connect(record_answer_accepted, sender=Answer) +post_save.connect(update_last_seen, sender=Activity) +post_save.connect(record_vote, sender=Vote) +post_delete.connect(record_cancel_vote, sender=Vote) +delete_post_or_answer.connect(record_delete_question, sender=Question) +delete_post_or_answer.connect(record_delete_question, sender=Answer) +mark_offensive.connect(record_mark_offensive, sender=Question) +mark_offensive.connect(record_mark_offensive, sender=Answer) +tags_updated.connect(record_update_tags, sender=Question) +post_save.connect(record_favorite_question, sender=FavoriteQuestion) +user_updated.connect(record_user_full_updated, sender=User) +user_logged_in.connect(post_stored_anonymous_content) diff --git a/forum/views.py b/forum/views.py index e69de29b..6ef7bd6d 100644 --- a/forum/views.py +++ b/forum/views.py @@ -0,0 +1,2084 @@ +# encoding:utf-8 +import os.path +import time, datetime, calendar, random +import logging +from urllib import quote, unquote +from django.conf import settings +from django.core.files.storage import default_storage +from django.shortcuts import render_to_response, get_object_or_404 +from django.contrib.auth.decorators import login_required +from django.http import HttpResponseRedirect, HttpResponse,Http404 +from django.core.paginator import Paginator, EmptyPage, InvalidPage +from django.template import RequestContext +from django.utils.html import * +from django.utils import simplejson +from django.core import serializers +from django.db import transaction +from django.contrib.contenttypes.models import ContentType +from django.utils.translation import ugettext as _ + +from utils.html import sanitize_html +from markdown2 import Markdown +#from lxml.html.diff import htmldiff +from forum.diff import textDiff as htmldiff +from forum.forms import * +from forum.models import * +from forum.auth import * +from forum.const import * +from forum.user import * +from forum import auth + +# used in index page +INDEX_PAGE_SIZE = 20 +INDEX_AWARD_SIZE = 15 +INDEX_TAGS_SIZE = 100 +# used in tags list +DEFAULT_PAGE_SIZE = 60 +# used in questions +QUESTIONS_PAGE_SIZE = 10 +# used in users +USERS_PAGE_SIZE = 35 +# used in answers +ANSWERS_PAGE_SIZE = 10 +markdowner = Markdown(html4tags=True) +question_type = ContentType.objects.get_for_model(Question) +answer_type = ContentType.objects.get_for_model(Answer) +comment_type = ContentType.objects.get_for_model(Comment) +question_revision_type = ContentType.objects.get_for_model(QuestionRevision) +answer_revision_type = ContentType.objects.get_for_model(AnswerRevision) +repute_type =ContentType.objects.get_for_model(Repute) +question_type_id = question_type.id +answer_type_id = answer_type.id +comment_type_id = comment_type.id +question_revision_type_id = question_revision_type.id +answer_revision_type_id = answer_revision_type.id +repute_type_id = repute_type.id +def _get_tags_cache_json(): + tags = Tag.objects.filter(deleted=False).all() + tags_list = [] + for tag in tags: + dic = {'n': tag.name, 'c': tag.used_count } + tags_list.append(dic) + tags = simplejson.dumps(tags_list) + return tags + +def index(request): + view_id = request.GET.get('sort', None) + view_dic = { + "latest":"-last_activity_at", + "hottest":"-answer_count", + "mostvoted":"-score", + "trans": "-last_activity_at" + } + try: + orderby = view_dic[view_id] + except KeyError: + view_id = "latest" + orderby = "-last_activity_at" + # group questions by author_id of 28,29 + if view_id == 'trans': + questions = Question.objects.get_translation_questions(orderby, INDEX_PAGE_SIZE) + else: + questions = Question.objects.get_questions_by_pagesize(orderby, INDEX_PAGE_SIZE) + # RISK - inner join queries + questions = questions.select_related() + tags = Tag.objects.get_valid_tags(INDEX_TAGS_SIZE) + + awards = Award.objects.get_recent_awards() + + return render_to_response('index.html', { + "questions" : questions, + "tab_id" : view_id, + "tags" : tags, + "awards" : awards[:INDEX_AWARD_SIZE], + }, context_instance=RequestContext(request)) + +def about(request): + return render_to_response('about.html', context_instance=RequestContext(request)) + +def faq(request): + return render_to_response('faq.html', context_instance=RequestContext(request)) + +def privacy(request): + return render_to_response('privacy.html', context_instance=RequestContext(request)) + +def unanswered(request): + return questions(request, unanswered=True) + +def questions(request, tagname=None, unanswered=False): + """ + List of Questions, Tagged questions, and Unanswered questions. + """ + # template file + # "questions.html" or "unanswered.html" + template_file = "questions.html" + # Set flag to False by default. If it is equal to True, then need to be saved. + pagesize_changed = False + # get pagesize from session, if failed then get default value + pagesize = request.session.get("pagesize",10) + try: + page = int(request.GET.get('page', '1')) + except ValueError: + page = 1 + + view_id = request.GET.get('sort', None) + view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" } + try: + orderby = view_dic[view_id] + except KeyError: + view_id = "latest" + orderby = "-added_at" + + # check if request is from tagged questions + if tagname is not None: + objects = Question.objects.get_questions_by_tag(tagname, orderby) + elif unanswered: + #check if request is from unanswered questions + template_file = "unanswered.html" + objects = Question.objects.get_unanswered_questions(orderby) + else: + objects = Question.objects.get_questions(orderby) + + # RISK - inner join queries + objects = objects.select_related(depth=1); + objects_list = Paginator(objects, pagesize) + questions = objects_list.page(page) + + # Get related tags from this page objects + if questions.object_list.count() > 0: + related_tags = Tag.objects.get_tags_by_questions(questions.object_list) + else: + related_tags = None + return render_to_response(template_file, { + "questions" : questions, + "tab_id" : view_id, + "questions_count" : objects_list.count, + "tags" : related_tags, + "searchtag" : tagname, + "is_unanswered" : unanswered, + "context" : { + 'is_paginated' : True, + 'pages': objects_list.num_pages, + 'page': page, + 'has_previous': questions.has_previous(), + 'has_next': questions.has_next(), + 'previous': questions.previous_page_number(), + 'next': questions.next_page_number(), + 'base_url' : request.path + '?sort=%s&' % view_id, + 'pagesize' : pagesize + }}, context_instance=RequestContext(request)) + +def create_new_answer( question=None, author=None,\ + added_at=None, wiki=False,\ + text='', email_notify=False): + + html = sanitize_html(markdowner.convert(text)) + + #create answer + answer = Answer( + question = question, + author = author, + added_at = added_at, + wiki = wiki, + html = html + ) + if answer.wiki: + answer.last_edited_by = answer.author + answer.last_edited_at = added_at + answer.wikified_at = added_at + + answer.save() + + #update question data + question.last_activity_at = added_at + question.last_activity_by = author + question.save() + Question.objects.update_answer_count(question) + + #update revision + AnswerRevision.objects.create( + answer = answer, + revision = 1, + author = author, + revised_at = added_at, + summary = CONST['default_version'], + text = text + ) + + #set notification/delete + if email_notify: + try: + EmailFeed.objects.get(feed_id = question.id, subscriber_id = author.id, feed_content_type=question_type) + except EmailFeed.DoesNotExist: + feed = EmailFeed(content = question, subscriber = author) + feed.save() + else: + #not sure if this is necessary. ajax should take care of this... + try: + feed = Email.objects.get(feed_id = question.id, subscriber_id = author.id, feed_content_type=question_type) + feed.delete() + except: + pass + +def create_new_question(title=None,author=None,added_at=None, + wiki=False,tagnames=None,summary=None, + text=None): + """this is not a view + and maybe should become one of the methods on Question object? + """ + html = sanitize_html(markdowner.convert(text)) + question = Question( + title = title, + author = author, + added_at = added_at, + last_activity_at = added_at, + last_activity_by = author, + wiki = wiki, + tagnames = tagnames, + html = html, + summary = summary + ) + if question.wiki: + question.last_edited_by = question.author + question.last_edited_at = added_at + question.wikified_at = added_at + + question.save() + + # create the first revision + QuestionRevision.objects.create( + question = question, + revision = 1, + title = question.title, + author = author, + revised_at = added_at, + tagnames = question.tagnames, + summary = CONST['default_version'], + text = text + ) + return question + +#TODO: allow anynomus user to ask question by providing email and username. +#@login_required +def ask(request): + if request.method == "POST": + form = AskForm(request.POST) + if form.is_valid(): + + added_at = datetime.datetime.now() + title = strip_tags(form.cleaned_data['title']) + wiki = form.cleaned_data['wiki'] + tagnames = form.cleaned_data['tags'].strip() + text = form.cleaned_data['text'] + html = sanitize_html(markdowner.convert(text)) + summary = strip_tags(html)[:120] + + if request.user.is_authenticated(): + author = request.user + + question = create_new_question( + title = title, + author = author, + added_at = added_at, + wiki = wiki, + tagnames = tagnames, + summary = summary, + text = text + ) + + return HttpResponseRedirect(question.get_absolute_url()) + else: + request.session.flush() + session_key = request.session.session_key + question = AnonymousQuestion( + session_key = session_key, + title = title, + tagnames = tagnames, + wiki = wiki, + text = text, + summary = summary, + added_at = added_at, + ip_addr = request.META['REMOTE_ADDR'], + ) + question.save() + return HttpResponseRedirect('%s%s%s' % ( _('/account/'),_('signin/'),('newquestion/'))) + else: + form = AskForm() + + tags = _get_tags_cache_json() + return render_to_response('ask.html', { + 'form' : form, + 'tags' : tags, + }, context_instance=RequestContext(request)) + +def question(request, id): + try: + page = int(request.GET.get('page', '1')) + except ValueError: + page = 1 + view_id = request.GET.get('sort', 'votes') + view_dic = {"latest":"-added_at", "oldest":"added_at", "votes":"-score" } + try: + orderby = view_dic[view_id] + except KeyError: + view_id = "votes" + orderby = "-score" + + question = get_object_or_404(Question, id=id) + if question.deleted and not can_view_deleted_post(request.user, question): + raise Http404 + answer_form = AnswerForm(question,request.user) + answers = Answer.objects.get_answers_from_question(question, request.user) + answers = answers.select_related(depth=1) + + favorited = question.has_favorite_by_user(request.user) + question_vote = question.votes.select_related().filter(user=request.user) + if question_vote is not None and question_vote.count() > 0: + question_vote = question_vote[0] + + user_answer_votes = {} + for answer in answers: + vote = answer.get_user_vote(request.user) + if vote is not None and not user_answer_votes.has_key(answer.id): + vote_value = -1 + if vote.is_upvote(): + vote_value = 1 + user_answer_votes[answer.id] = vote_value + + + if answers is not None: + answers = answers.order_by("-accepted", orderby) + + filtered_answers = [] + for answer in answers: + if answer.deleted == True: + if answer.author_id == request.user.id: + filtered_answers.append(answer) + else: + filtered_answers.append(answer) + + objects_list = Paginator(filtered_answers, ANSWERS_PAGE_SIZE) + page_objects = objects_list.page(page) + # update view count + Question.objects.update_view_count(question) + return render_to_response('question.html', { + "question" : question, + "question_vote" : question_vote, + "question_comment_count":question.comments.count(), + "answer" : answer_form, + "answers" : page_objects.object_list, + "user_answer_votes": user_answer_votes, + "tags" : question.tags.all(), + "tab_id" : view_id, + "favorited" : favorited, + "similar_questions" : Question.objects.get_similar_questions(question), + "context" : { + 'is_paginated' : True, + 'pages': objects_list.num_pages, + 'page': page, + 'has_previous': page_objects.has_previous(), + 'has_next': page_objects.has_next(), + 'previous': page_objects.previous_page_number(), + 'next': page_objects.next_page_number(), + 'base_url' : request.path + '?sort=%s&' % view_id, + 'extend_url' : "#sort-top" + } + }, context_instance=RequestContext(request)) + +@login_required +def close(request, id): + question = get_object_or_404(Question, id=id) + if not can_close_question(request.user, question): + return HttpResponse('Permission denied.') + if request.method == 'POST': + form = CloseForm(request.POST) + if form.is_valid(): + reason = form.cleaned_data['reason'] + question.closed = True + question.closed_by = request.user + question.closed_at = datetime.datetime.now() + question.close_reason = reason + question.save() + return HttpResponseRedirect(question.get_absolute_url()) + else: + form = CloseForm() + return render_to_response('close.html', { + 'form' : form, + 'question' : question, + }, context_instance=RequestContext(request)) + +@login_required +def reopen(request, id): + question = get_object_or_404(Question, id=id) + # open question + if not can_reopen_question(request.user, question): + return HttpResponse('Permission denied.') + if request.method == 'POST' : + Question.objects.filter(id=question.id).update(closed=False, + closed_by=None, closed_at=None, close_reason=None) + return HttpResponseRedirect(question.get_absolute_url()) + else: + return render_to_response('reopen.html', { + 'question' : question, + }, context_instance=RequestContext(request)) + +@login_required +def edit_question(request, id): + question = get_object_or_404(Question, id=id) + if question.deleted and not can_view_deleted_post(request.user, question): + raise Http404 + if can_edit_post(request.user, question): + return _edit_question(request, question) + elif can_retag_questions(request.user): + return _retag_question(request, question) + else: + raise Http404 + +def _retag_question(request, question): + if request.method == 'POST': + form = RetagQuestionForm(question, request.POST) + if form.is_valid(): + if form.has_changed(): + latest_revision = question.get_latest_revision() + retagged_at = datetime.datetime.now() + # Update the Question itself + Question.objects.filter(id=question.id).update( + tagnames = form.cleaned_data['tags'], + last_edited_at = retagged_at, + last_edited_by = request.user, + last_activity_at = retagged_at, + last_activity_by = request.user + ) + # Update the Question's tag associations + tags_updated = Question.objects.update_tags(question, + form.cleaned_data['tags'], request.user) + # Create a new revision + QuestionRevision.objects.create( + question = question, + title = latest_revision.title, + author = request.user, + revised_at = retagged_at, + tagnames = form.cleaned_data['tags'], + summary = CONST['retagged'], + text = latest_revision.text + ) + # send tags updated singal + tags_updated.send(sender=question.__class__, question=question) + + return HttpResponseRedirect(question.get_absolute_url()) + else: + form = RetagQuestionForm(question) + return render_to_response('question_retag.html', { + 'question': question, + 'form' : form, + 'tags' : _get_tags_cache_json(), + }, context_instance=RequestContext(request)) + + +def _edit_question(request, question): + latest_revision = question.get_latest_revision() + revision_form = None + if request.method == 'POST': + if 'select_revision' in request.POST: + # user has changed revistion number + revision_form = RevisionForm(question, latest_revision, request.POST) + if revision_form.is_valid(): + # Replace with those from the selected revision + form = EditQuestionForm(question, + QuestionRevision.objects.get(question=question, + revision=revision_form.cleaned_data['revision'])) + else: + form = EditQuestionForm(question, latest_revision, request.POST) + else: + # Always check modifications against the latest revision + form = EditQuestionForm(question, latest_revision, request.POST) + if form.is_valid(): + html = sanitize_html(markdowner.convert(form.cleaned_data['text'])) + if form.has_changed(): + edited_at = datetime.datetime.now() + tags_changed = (latest_revision.tagnames != + form.cleaned_data['tags']) + tags_updated = False + # Update the Question itself + updated_fields = { + 'title': form.cleaned_data['title'], + 'last_edited_at': edited_at, + 'last_edited_by': request.user, + 'last_activity_at': edited_at, + 'last_activity_by': request.user, + 'tagnames': form.cleaned_data['tags'], + 'summary': strip_tags(html)[:120], + 'html': html, + } + + # only save when it's checked + # because wiki doesn't allow to be edited if last version has been enabled already + # and we make sure this in forms. + if ('wiki' in form.cleaned_data and + form.cleaned_data['wiki']): + updated_fields['wiki'] = True + updated_fields['wikified_at'] = edited_at + + Question.objects.filter( + id=question.id).update(**updated_fields) + # Update the Question's tag associations + if tags_changed: + tags_updated = Question.objects.update_tags( + question, form.cleaned_data['tags'], request.user) + # Create a new revision + revision = QuestionRevision( + question = question, + title = form.cleaned_data['title'], + author = request.user, + revised_at = edited_at, + tagnames = form.cleaned_data['tags'], + text = form.cleaned_data['text'], + ) + if form.cleaned_data['summary']: + revision.summary = form.cleaned_data['summary'] + else: + revision.summary = 'No.%s Revision' % latest_revision.revision + revision.save() + + return HttpResponseRedirect(question.get_absolute_url()) + else: + + revision_form = RevisionForm(question, latest_revision) + form = EditQuestionForm(question, latest_revision) + return render_to_response('question_edit.html', { + 'question': question, + 'revision_form': revision_form, + 'form' : form, + 'tags' : _get_tags_cache_json() + }, context_instance=RequestContext(request)) + + +@login_required +def edit_answer(request, id): + answer = get_object_or_404(Answer, id=id) + if answer.deleted and not can_view_deleted_post(request.user, answer): + raise Http404 + elif not can_edit_post(request.user, answer): + raise Http404 + else: + latest_revision = answer.get_latest_revision() + if request.method == "POST": + if 'select_revision' in request.POST: + # user has changed revistion number + revision_form = RevisionForm(answer, latest_revision, request.POST) + if revision_form.is_valid(): + # Replace with those from the selected revision + form = EditAnswerForm(answer, + AnswerRevision.objects.get(answer=answer, + revision=revision_form.cleaned_data['revision'])) + else: + form = EditAnswerForm(answer, latest_revision, request.POST) + else: + form = EditAnswerForm(answer, latest_revision, request.POST) + if form.is_valid(): + html = sanitize_html(markdowner.convert(form.cleaned_data['text'])) + if form.has_changed(): + edited_at = datetime.datetime.now() + updated_fields = { + 'last_edited_at': edited_at, + 'last_edited_by': request.user, + 'html': html, + } + Answer.objects.filter(id=answer.id).update(**updated_fields) + + revision = AnswerRevision( + answer = answer, + author = request.user, + revised_at = edited_at, + text = form.cleaned_data['text'] + ) + + if form.cleaned_data['summary']: + revision.summary = form.cleaned_data['summary'] + else: + revision.summary = 'No.%s Revision' % latest_revision.revision + revision.save() + + answer.question.last_activity_at = edited_at + answer.question.last_activity_by = request.user + answer.question.save() + + return HttpResponseRedirect(answer.get_absolute_url()) + else: + revision_form = RevisionForm(answer, latest_revision) + form = EditAnswerForm(answer, latest_revision) + return render_to_response('answer_edit.html', { + 'answer': answer, + 'revision_form': revision_form, + 'form' : form, + }, context_instance=RequestContext(request)) + +QUESTION_REVISION_TEMPLATE = ('

    %(title)s

    \n' + '
    %(html)s
    \n' + '
    %(tags)s
    ') +def question_revisions(request, id): + post = get_object_or_404(Question, id=id) + revisions = list(post.revisions.all()) + for i, revision in enumerate(revisions): + revision.html = QUESTION_REVISION_TEMPLATE % { + 'title': revision.title, + 'html': sanitize_html(markdowner.convert(revision.text)), + 'tags': ' '.join(['' % tag + for tag in revision.tagnames.split(' ')]), + } + if i > 0: + revisions[i - 1].diff = htmldiff(revision.html, + revisions[i - 1].html) + else: + revisions[i - 1].diff = QUESTION_REVISION_TEMPLATE % { + 'title': revisions[0].title, + 'html': sanitize_html(markdowner.convert(revisions[0].text)), + 'tags': ' '.join(['' % tag + for tag in revisions[0].tagnames.split(' ')]), + } + revisions[i - 1].summary = None + return render_to_response('revisions_question.html', { + 'post': post, + 'revisions': revisions, + }, context_instance=RequestContext(request)) + +ANSWER_REVISION_TEMPLATE = ('
    %(html)s
    ') +def answer_revisions(request, id): + post = get_object_or_404(Answer, id=id) + revisions = list(post.revisions.all()) + for i, revision in enumerate(revisions): + revision.html = ANSWER_REVISION_TEMPLATE % { + 'html': sanitize_html(markdowner.convert(revision.text)) + } + if i > 0: + revisions[i - 1].diff = htmldiff(revision.html, + revisions[i - 1].html) + else: + revisions[i - 1].diff = revisions[i-1].text + revisions[i - 1].summary = None + return render_to_response('revisions_answer.html', { + 'post': post, + 'revisions': revisions, + }, context_instance=RequestContext(request)) + +def answer(request, id): + question = get_object_or_404(Question, id=id) + if request.method == "POST": + form = AnswerForm(question, request.user, request.POST) + if form.is_valid(): + wiki = form.cleaned_data['wiki'] + text = form.cleaned_data['text'] + update_time = datetime.datetime.now() + + if request.user.is_authenticated(): + create_new_answer( + question=question, + author=request.user, + added_at=update_time, + wiki=wiki, + text=text, + email_notify=form.cleaned_data['email_notify'] + ) + else: + request.session.flush() + html = sanitize_html(markdowner.convert(text)) + summary = strip_tags(html)[:120] + anon = AnonymousAnswer( + question = question, + wiki = wiki, + text = text, + summary = summary, + session_key = request.session.session_key, + ip_addr = request.META['REMOTE_ADDR'], + ) + anon.save() + return HttpResponseRedirect('/account/signin/newanswer') + + return HttpResponseRedirect(question.get_absolute_url()) + +def tags(request): + stag = "" + is_paginated = True + sortby = request.GET.get('sort', 'used') + try: + page = int(request.GET.get('page', '1')) + except ValueError: + page = 1 + + if request.method == "GET": + stag = request.GET.get("q", "").strip() + if stag is not None: + objects_list = Paginator(Tag.objects.filter(deleted=False).exclude(used_count=0).extra(where=['name like %s'], params=['%' + stag + '%']), DEFAULT_PAGE_SIZE) + else: + if sortby == "name": + objects_list = Paginator(Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("name"), DEFAULT_PAGE_SIZE) + else: + objects_list = Paginator(Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("-used_count"), DEFAULT_PAGE_SIZE) + + try: + tags = objects_list.page(page) + except (EmptyPage, InvalidPage): + tags = objects_list.page(objects_list.num_pages) + + return render_to_response('tags.html', { + "tags" : tags, + "stag" : stag, + "tab_id" : sortby, + "keywords" : stag, + "context" : { + 'is_paginated' : is_paginated, + 'pages': objects_list.num_pages, + 'page': page, + 'has_previous': tags.has_previous(), + 'has_next': tags.has_next(), + 'previous': tags.previous_page_number(), + 'next': tags.next_page_number(), + 'base_url' : '/tags/?sort=%s&' % sortby + } + + }, context_instance=RequestContext(request)) + +def tag(request, tag): + return questions(request, tagname=tag) + +def vote(request, id): + """ + vote_type: + acceptAnswer : 0, + questionUpVote : 1, + questionDownVote : 2, + favorite : 4, + answerUpVote: 5, + answerDownVote:6, + offensiveQuestion : 7, + offensiveAnswer:8, + removeQuestion: 9, + removeAnswer:10 + questionSubscribeUpdates:11 + + accept answer code: + response_data['allowed'] = -1, Accept his own answer 0, no allowed - Anonymous 1, Allowed - by default + response_data['success'] = 0, failed 1, Success - by default + response_data['status'] = 0, By default 1, Answer has been accepted already(Cancel) + + vote code: + allowed = -3, Don't have enough votes left + -2, Don't have enough reputation score + -1, Vote his own post + 0, no allowed - Anonymous + 1, Allowed - by default + status = 0, By default + 1, Cancel + 2, Vote is too old to be canceled + + offensive code: + allowed = -3, Don't have enough flags left + -2, Don't have enough reputation score to do this + 0, not allowed + 1, allowed + status = 0, by default + 1, can't do it again + """ + response_data = { + "allowed": 1, + "success": 1, + "status" : 0, + "count" : 0, + "message" : '' + } + + def can_vote(vote_score, user): + if vote_score == 1: + return can_vote_up(request.user) + else: + return can_vote_down(request.user) + + try: + if not request.user.is_authenticated(): + response_data['allowed'] = 0 + response_data['success'] = 0 + + elif request.is_ajax(): + question = get_object_or_404(Question, id=id) + vote_type = request.POST.get('type') + + #accept answer + if vote_type == '0': + answer_id = request.POST.get('postId') + answer = get_object_or_404(Answer, id=answer_id) + # make sure question author is current user + if question.author == request.user: + # answer user who is also question author is not allow to accept answer + if answer.author == question.author: + response_data['success'] = 0 + response_data['allowed'] = -1 + # check if answer has been accepted already + elif answer.accepted: + onAnswerAcceptCanceled(answer, request.user) + response_data['status'] = 1 + else: + # set other answers in this question not accepted first + for answer_of_question in Answer.objects.get_answers_from_question(question, request.user): + if answer_of_question != answer and answer_of_question.accepted: + onAnswerAcceptCanceled(answer_of_question, request.user) + + #make sure retrieve data again after above author changes, they may have related data + answer = get_object_or_404(Answer, id=answer_id) + onAnswerAccept(answer, request.user) + else: + response_data['allowed'] = 0 + response_data['success'] = 0 + # favorite + elif vote_type == '4': + has_favorited = False + fav_questions = FavoriteQuestion.objects.filter(question=question) + # if the same question has been favorited before, then delete it + if fav_questions is not None: + for item in fav_questions: + if item.user == request.user: + item.delete() + response_data['status'] = 1 + response_data['count'] = len(fav_questions) - 1 + if response_data['count'] < 0: + response_data['count'] = 0 + has_favorited = True + # if above deletion has not been executed, just insert a new favorite question + if not has_favorited: + new_item = FavoriteQuestion(question=question, user=request.user) + new_item.save() + response_data['count'] = FavoriteQuestion.objects.filter(question=question).count() + Question.objects.update_favorite_count(question) + + elif vote_type in ['1', '2', '5', '6']: + post_id = id + post = question + vote_score = 1 + if vote_type in ['5', '6']: + answer_id = request.POST.get('postId') + answer = get_object_or_404(Answer, id=answer_id) + post_id = answer_id + post = answer + if vote_type in ['2', '6']: + vote_score = -1 + + if post.author == request.user: + response_data['allowed'] = -1 + elif not can_vote(vote_score, request.user): + response_data['allowed'] = -2 + elif post.votes.filter(user=request.user).count() > 0: + vote = post.votes.filter(user=request.user)[0] + # unvote should be less than certain time + if (datetime.datetime.now().day - vote.voted_at.day) >= VOTE_RULES['scope_deny_unvote_days']: + response_data['status'] = 2 + else: + voted = vote.vote + if voted > 0: + # cancel upvote + onUpVotedCanceled(vote, post, request.user) + + else: + # cancel downvote + onDownVotedCanceled(vote, post, request.user) + + response_data['status'] = 1 + response_data['count'] = post.score + elif Vote.objects.get_votes_count_today_from_user(request.user) >= VOTE_RULES['scope_votes_per_user_per_day']: + response_data['allowed'] = -3 + else: + vote = Vote(user=request.user, content_object=post, vote=vote_score, voted_at=datetime.datetime.now()) + if vote_score > 0: + # upvote + onUpVoted(vote, post, request.user) + else: + # downvote + onDownVoted(vote, post, request.user) + + votes_left = VOTE_RULES['scope_votes_per_user_per_day'] - Vote.objects.get_votes_count_today_from_user(request.user) + if votes_left <= VOTE_RULES['scope_warn_votes_left']: + response_data['message'] = u'%s votes left' % votes_left + response_data['count'] = post.score + elif vote_type in ['7', '8']: + post = question + post_id = id + if vote_type == '8': + post_id = request.POST.get('postId') + post = get_object_or_404(Answer, id=post_id) + + if FlaggedItem.objects.get_flagged_items_count_today(request.user) >= VOTE_RULES['scope_flags_per_user_per_day']: + response_data['allowed'] = -3 + elif not can_flag_offensive(request.user): + response_data['allowed'] = -2 + elif post.flagged_items.filter(user=request.user).count() > 0: + response_data['status'] = 1 + else: + item = FlaggedItem(user=request.user, content_object=post, flagged_at=datetime.datetime.now()) + onFlaggedItem(item, post, request.user) + response_data['count'] = post.offensive_flag_count + # send signal when question or answer be marked offensive + mark_offensive.send(sender=post.__class__, instance=post, mark_by=request.user) + elif vote_type in ['9', '10']: + post = question + post_id = id + if vote_type == '10': + post_id = request.POST.get('postId') + post = get_object_or_404(Answer, id=post_id) + + if not can_delete_post(request.user, post): + response_data['allowed'] = -2 + elif post.deleted: + onDeleteCanceled(post, request.user) + response_data['status'] = 1 + else: + onDeleted(post, request.user) + delete_post_or_answer.send(sender=post.__class__, instance=post, delete_by=request.user) + elif vote_type == '11':#subscribe q updates + user = request.user + if user.is_authenticated(): + try: + EmailFeed.objects.get(feed_id=question.id,subscriber_id=user.id,feed_content_type=question_type) + except EmailFeed.DoesNotExist: + feed = EmailFeed(subscriber=user,content=question) + feed.save() + if settings.EMAIL_VALIDATION == 'on' and user.email_isvalid == False: + response_data['message'] = _('subscription saved, %(email)s needs validation') % {'email':user.email} + #response_data['status'] = 1 + #responst_data['allowed'] = 1 + else: + pass + #response_data['status'] = 0 + #response_data['allowed'] = 0 + elif vote_type == '12':#unsubscribe q updates + user = request.user + if user.is_authenticated(): + try: + feed = EmailFeed.objects.get(feed_id=question.id,subscriber_id=user.id) + feed.delete() + except EmailFeed.DoesNotExist: + pass + + else: + response_data['success'] = 0 + response_data['message'] = u'Request mode is not supported. Please try again.' + + data = simplejson.dumps(response_data) + + except Exception, e: + response_data['message'] = str(e) + data = simplejson.dumps(response_data) + return HttpResponse(data, mimetype="application/json") + +def users(request): + is_paginated = True + sortby = request.GET.get('sort', 'reputation') + suser = request.REQUEST.get('q', "") + try: + page = int(request.GET.get('page', '1')) + except ValueError: + page = 1 + + if suser == "": + if sortby == "newest": + objects_list = Paginator(User.objects.all().order_by('-date_joined'), USERS_PAGE_SIZE) + elif sortby == "last": + objects_list = Paginator(User.objects.all().order_by('date_joined'), USERS_PAGE_SIZE) + elif sortby == "user": + objects_list = Paginator(User.objects.all().order_by('username'), USERS_PAGE_SIZE) + # default + else: + objects_list = Paginator(User.objects.all().order_by('-reputation'), USERS_PAGE_SIZE) + base_url = '/users/?sort=%s&' % sortby + else: + sortby = "reputation" + objects_list = Paginator(User.objects.extra(where=['username like %s'], params=['%' + suser + '%']).order_by('-reputation'), USERS_PAGE_SIZE) + base_url = '/users/?name=%s&sort=%s&' % (suser, sortby) + + try: + users = objects_list.page(page) + except (EmptyPage, InvalidPage): + users = objects_list.page(objects_list.num_pages) + + return render_to_response('users.html', { + "users" : users, + "suser" : suser, + "keywords" : suser, + "tab_id" : sortby, + "context" : { + 'is_paginated' : is_paginated, + 'pages': objects_list.num_pages, + 'page': page, + 'has_previous': users.has_previous(), + 'has_next': users.has_next(), + 'previous': users.previous_page_number(), + 'next': users.next_page_number(), + 'base_url' : base_url + } + + }, context_instance=RequestContext(request)) + +def user(request, id): + sort = request.GET.get('sort', 'stats') + user_view = dict((v.id, v) for v in USER_TEMPLATE_VIEWS).get(sort, USER_TEMPLATE_VIEWS[0]) + from forum import views + func = getattr(views, user_view.view_name) + return func(request, id, user_view) + +@login_required +def edit_user(request, id): + user = get_object_or_404(User, id=id) + if request.user != user: + raise Http404 + if request.method == "POST": + form = EditUserForm(user, request.POST) + if form.is_valid(): + new_email = sanitize_html(form.cleaned_data['email']) + + from django_authopenid.views import set_new_email + set_new_email(user, new_email) + + user.real_name = sanitize_html(form.cleaned_data['realname']) + user.website = sanitize_html(form.cleaned_data['website']) + user.location = sanitize_html(form.cleaned_data['city']) + user.date_of_birth = sanitize_html(form.cleaned_data['birthday']) + if len(user.date_of_birth) == 0: + user.date_of_birth = '1900-01-01' + user.about = sanitize_html(form.cleaned_data['about']) + + user.save() + # send user updated singal if full fields have been updated + if user.email and user.real_name and user.website and user.location and \ + user.date_of_birth and user.about: + user_updated.send(sender=user.__class__, instance=user, updated_by=user) + return HttpResponseRedirect(user.get_profile_url()) + else: + form = EditUserForm(user) + return render_to_response('user_edit.html', { + 'form' : form, + }, context_instance=RequestContext(request)) + +def user_stats(request, user_id, user_view): + user = get_object_or_404(User, id=user_id) + questions = Question.objects.extra( + select={ + 'vote_count' : 'question.score', + 'favorited_myself' : 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s AND f.question_id = question.id', + 'la_user_id' : 'auth_user.id', + 'la_username' : 'auth_user.username', + 'la_user_gold' : 'auth_user.gold', + 'la_user_silver' : 'auth_user.silver', + 'la_user_bronze' : 'auth_user.bronze', + 'la_user_reputation' : 'auth_user.reputation' + }, + select_params=[user_id], + tables=['question', 'auth_user'], + where=['question.deleted = 0 AND question.author_id=%s AND question.last_activity_by_id = auth_user.id'], + params=[user_id], + order_by=['-vote_count', '-last_activity_at'] + ).values('vote_count', + 'favorited_myself', + 'id', + 'title', + 'author_id', + 'added_at', + 'answer_accepted', + 'answer_count', + 'comment_count', + 'view_count', + 'favourite_count', + 'summary', + 'tagnames', + 'vote_up_count', + 'vote_down_count', + 'last_activity_at', + 'la_user_id', + 'la_username', + 'la_user_gold', + 'la_user_silver', + 'la_user_bronze', + 'la_user_reputation')[:100] + + answered_questions = Question.objects.extra( + select={ + 'vote_up_count' : 'answer.vote_up_count', + 'vote_down_count' : 'answer.vote_down_count', + 'answer_id' : 'answer.id', + 'accepted' : 'answer.accepted', + 'vote_count' : 'answer.score', + 'comment_count' : 'answer.comment_count' + }, + tables=['question', 'answer'], + where=['answer.deleted=0 AND answer.author_id=%s AND answer.question_id=question.id'], + params=[user_id], + order_by=['-vote_count', '-answer_id'], + select_params=[user_id] + ).distinct().values('comment_count', + 'id', + 'answer_id', + 'title', + 'author_id', + 'accepted', + 'vote_count', + 'answer_count', + 'vote_up_count', + 'vote_down_count')[:100] + up_votes = Vote.objects.get_up_vote_count_from_user(user) + down_votes = Vote.objects.get_down_vote_count_from_user(user) + votes_today = Vote.objects.get_votes_count_today_from_user(user) + votes_total = VOTE_RULES['scope_votes_per_user_per_day'] + tags = user.created_tags.all().order_by('-used_count')[:50] + awards = Award.objects.extra( + select={'id': 'badge.id', 'count': 'count(badge_id)', 'name':'badge.name', 'description': 'badge.description', 'type': 'badge.type'}, + tables=['award', 'badge'], + order_by=['-awarded_at'], + where=['user_id=%s AND badge_id=badge.id'], + params=[user.id] + ).values('id', 'count', 'name', 'description', 'type') + total_awards = awards.count() + awards.query.group_by = ['badge_id'] + + return render_to_response(user_view.template_file,{ + "tab_name" : user_view.id, + "tab_description" : user_view.tab_description, + "page_title" : user_view.page_title, + "view_user" : user, + "questions" : questions, + "answered_questions" : answered_questions, + "up_votes" : up_votes, + "down_votes" : down_votes, + "total_votes": up_votes + down_votes, + "votes_today_left": votes_total-votes_today, + "votes_total_per_day": votes_total, + "tags" : tags, + "awards": awards, + "total_awards" : total_awards, + }, context_instance=RequestContext(request)) + +def user_recent(request, user_id, user_view): + user = get_object_or_404(User, id=user_id) + def get_type_name(type_id): + for item in TYPE_ACTIVITY: + if type_id in item: + return item[1] + + class Event: + def __init__(self, time, type, title, summary, answer_id, question_id): + self.time = time + self.type = get_type_name(type) + self.type_id = type + self.title = title + self.summary = summary + self.title_link = u'/questions/%s/%s#%s' %(question_id, title, answer_id)\ + if int(answer_id) > 0 else u'/questions/%s/%s' %(question_id, title) + class AwardEvent: + def __init__(self, time, type, id): + self.time = time + self.type = get_type_name(type) + self.type_id = type + self.badge = get_object_or_404(Badge, id=id) + + activities = [] + # ask questions + questions = Activity.objects.extra( + select={ + 'title' : 'question.title', + 'question_id' : 'question.id', + 'active_at' : 'activity.active_at', + 'activity_type' : 'activity.activity_type' + }, + tables=['activity', 'question'], + where=['activity.content_type_id = %s AND activity.object_id = ' + + 'question.id AND activity.user_id = %s AND activity.activity_type = %s'], + params=[question_type_id, user_id, TYPE_ACTIVITY_ASK_QUESTION], + order_by=['-activity.active_at'] + ).values( + 'title', + 'question_id', + 'active_at', + 'activity_type' + ) + if len(questions) > 0: + questions = [(Event(q['active_at'], q['activity_type'], q['title'], '', '0', \ + q['question_id'])) for q in questions] + activities.extend(questions) + + # answers + answers = Activity.objects.extra( + select={ + 'title' : 'question.title', + 'question_id' : 'question.id', + 'answer_id' : 'answer.id', + 'active_at' : 'activity.active_at', + 'activity_type' : 'activity.activity_type' + }, + tables=['activity', 'answer', 'question'], + where=['activity.content_type_id = %s AND activity.object_id = answer.id AND ' + + 'answer.question_id=question.id AND activity.user_id=%s AND activity.activity_type=%s'], + params=[answer_type_id, user_id, TYPE_ACTIVITY_ANSWER], + order_by=['-activity.active_at'] + ).values( + 'title', + 'question_id', + 'answer_id', + 'active_at', + 'activity_type' + ) + if len(answers) > 0: + answers = [(Event(q['active_at'], q['activity_type'], q['title'], '', q['answer_id'], \ + q['question_id'])) for q in answers] + activities.extend(answers) + + # question comments + comments = Activity.objects.extra( + select={ + 'title' : 'question.title', + 'question_id' : 'comment.object_id', + 'added_at' : 'comment.added_at', + 'activity_type' : 'activity.activity_type' + }, + tables=['activity', 'question', 'comment'], + + 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 '+ + 'comment.content_type_id=%s AND activity.user_id = %s AND activity.activity_type=%s'], + params=[comment_type_id, question_type_id, user_id, 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 = Activity.objects.extra( + select={ + 'title' : 'question.title', + 'question_id' : 'question.id', + 'answer_id' : 'answer.id', + 'added_at' : 'comment.added_at', + 'activity_type' : 'activity.activity_type' + }, + tables=['activity', 'question', 'answer', 'comment'], + + 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 '+ + 'activity.user_id = %s AND activity.activity_type=%s'], + params=[comment_type_id, answer_type_id, user_id, 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 = Activity.objects.extra( + select={ + 'title' : 'question_revision.title', + 'question_id' : 'question_revision.question_id', + 'added_at' : 'activity.active_at', + 'activity_type' : 'activity.activity_type', + 'summary' : 'question_revision.summary' + }, + tables=['activity', 'question_revision'], + where=['activity.content_type_id = %s AND activity.object_id = question_revision.id AND '+ + 'activity.user_id = question_revision.author_id AND activity.user_id = %s AND '+ + 'activity.activity_type=%s'], + params=[question_revision_type_id, user_id, 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 = Activity.objects.extra( + select={ + 'title' : 'question.title', + 'question_id' : 'question.id', + 'answer_id' : 'answer.id', + 'added_at' : 'activity.active_at', + 'activity_type' : 'activity.activity_type', + 'summary' : 'answer_revision.summary' + }, + tables=['activity', 'answer_revision', 'question', 'answer'], + + where=['activity.content_type_id = %s AND activity.object_id = answer_revision.id AND '+ + 'activity.user_id = answer_revision.author_id AND activity.user_id = %s AND '+ + 'answer_revision.answer_id=answer.id AND answer.question_id = question.id AND '+ + 'activity.activity_type=%s'], + params=[answer_revision_type_id, user_id, 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 = Activity.objects.extra( + select={ + 'title' : 'question.title', + 'question_id' : 'question.id', + 'added_at' : 'activity.active_at', + 'activity_type' : 'activity.activity_type', + }, + tables=['activity', 'answer', 'question'], + 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 '+ + 'answer.question_id=question.id AND activity.activity_type=%s'], + params=[answer_type_id, user_id, 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 = Activity.objects.extra( + select={ + 'badge_id' : 'badge.id', + 'awarded_at': 'award.awarded_at', + 'activity_type' : 'activity.activity_type' + }, + tables=['activity', 'award', 'badge'], + where=['activity.user_id = award.user_id AND activity.user_id = %s AND '+ + 'award.badge_id=badge.id AND activity.object_id=award.id AND activity.activity_type=%s'], + params=[user_id, TYPE_ACTIVITY_PRIZE], + order_by=['-activity.active_at'] + ).values( + 'badge_id', + 'awarded_at', + 'activity_type' + ) + if len(awards) > 0: + awards = [(AwardEvent(q['awarded_at'], q['activity_type'], q['badge_id'])) for q in awards] + activities.extend(awards) + + activities.sort(lambda x,y: cmp(y.time, x.time)) + + return render_to_response(user_view.template_file,{ + "tab_name" : user_view.id, + "tab_description" : user_view.tab_description, + "page_title" : user_view.page_title, + "view_user" : user, + "activities" : activities[:user_view.data_size] + }, context_instance=RequestContext(request)) + +def user_responses(request, user_id, user_view): + """ + We list answers for question, comments, and answer accepted by others for this user. + """ + class Response: + def __init__(self, type, title, question_id, answer_id, time, username, user_id, content): + self.type = type + self.title = title + self.titlelink = u'/questions/%s/%s#%s' % (question_id, title, answer_id) + self.time = time + self.userlink = u'/users/%s/%s/' % (user_id, username) + self.username = username + self.content = u'%s ...' % strip_tags(content)[:300] + + def __unicode__(self): + return u'%s %s' % (self.type, self.titlelink) + + user = get_object_or_404(User, id=user_id) + responses = [] + answers = Answer.objects.extra( + select={ + 'title' : 'question.title', + 'question_id' : 'question.id', + 'answer_id' : 'answer.id', + 'added_at' : 'answer.added_at', + 'html' : 'answer.html', + 'username' : 'auth_user.username', + 'user_id' : 'auth_user.id' + }, + select_params=[user_id], + tables=['answer', 'question', 'auth_user'], + where=['answer.question_id = question.id AND answer.deleted=0 AND question.deleted = 0 AND '+ + 'question.author_id = %s AND answer.author_id <> %s AND answer.author_id=auth_user.id'], + params=[user_id, user_id], + order_by=['-answer.id'] + ).values( + 'title', + 'question_id', + 'answer_id', + 'added_at', + 'html', + 'username', + 'user_id' + ) + if len(answers) > 0: + answers = [(Response(TYPE_RESPONSE['QUESTION_ANSWERED'], a['title'], a['question_id'], + a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers] + responses.extend(answers) + + + # question comments + comments = Comment.objects.extra( + select={ + 'title' : 'question.title', + 'question_id' : 'comment.object_id', + 'added_at' : 'comment.added_at', + 'comment' : 'comment.comment', + 'username' : 'auth_user.username', + 'user_id' : 'auth_user.id' + }, + tables=['question', 'auth_user', 'comment'], + where=['question.deleted = 0 AND question.author_id = %s AND comment.object_id=question.id AND '+ + 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id'], + params=[user_id, question_type_id, user_id], + order_by=['-comment.added_at'] + ).values( + 'title', + 'question_id', + 'added_at', + 'comment', + 'username', + 'user_id' + ) + + if len(comments) > 0: + comments = [(Response(TYPE_RESPONSE['QUESTION_COMMENTED'], c['title'], c['question_id'], + '', c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments] + responses.extend(comments) + + # answer comments + comments = Comment.objects.extra( + select={ + 'title' : 'question.title', + 'question_id' : 'question.id', + 'answer_id' : 'answer.id', + 'added_at' : 'comment.added_at', + 'comment' : 'comment.comment', + 'username' : 'auth_user.username', + 'user_id' : 'auth_user.id' + }, + tables=['answer', 'auth_user', 'comment', 'question'], + where=['answer.deleted = 0 AND answer.author_id = %s AND comment.object_id=answer.id AND '+ + 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id '+ + 'AND question.id = answer.question_id'], + params=[user_id, answer_type_id, user_id], + order_by=['-comment.added_at'] + ).values( + 'title', + 'question_id', + 'answer_id', + 'added_at', + 'comment', + 'username', + 'user_id' + ) + + if len(comments) > 0: + comments = [(Response(TYPE_RESPONSE['ANSWER_COMMENTED'], c['title'], c['question_id'], + c['answer_id'], c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments] + responses.extend(comments) + + # answer has been accepted + answers = Answer.objects.extra( + select={ + 'title' : 'question.title', + 'question_id' : 'question.id', + 'answer_id' : 'answer.id', + 'added_at' : 'answer.accepted_at', + 'html' : 'answer.html', + 'username' : 'auth_user.username', + 'user_id' : 'auth_user.id' + }, + select_params=[user_id], + tables=['answer', 'question', 'auth_user'], + where=['answer.question_id = question.id AND answer.deleted=0 AND question.deleted = 0 AND '+ + 'answer.author_id = %s AND answer.accepted=1 AND question.author_id=auth_user.id'], + params=[user_id], + order_by=['-answer.id'] + ).values( + 'title', + 'question_id', + 'answer_id', + 'added_at', + 'html', + 'username', + 'user_id' + ) + if len(answers) > 0: + answers = [(Response(TYPE_RESPONSE['ANSWER_ACCEPTED'], a['title'], a['question_id'], + a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers] + responses.extend(answers) + + # sort posts by time + responses.sort(lambda x,y: cmp(y.time, x.time)) + + return render_to_response(user_view.template_file,{ + "tab_name" : user_view.id, + "tab_description" : user_view.tab_description, + "page_title" : user_view.page_title, + "view_user" : user, + "responses" : responses[:user_view.data_size], + + }, context_instance=RequestContext(request)) + +def user_votes(request, user_id, user_view): + user = get_object_or_404(User, id=user_id) + if not can_view_user_votes(request.user, user): + raise Http404 + votes = [] + question_votes = Vote.objects.extra( + select={ + 'title' : 'question.title', + 'question_id' : 'question.id', + 'answer_id' : 0, + 'voted_at' : 'vote.voted_at', + 'vote' : 'vote', + }, + select_params=[user_id], + tables=['vote', 'question', 'auth_user'], + where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = question.id '+ + 'AND vote.user_id=auth_user.id'], + params=[question_type_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 = Vote.objects.extra( + select={ + 'title' : 'question.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'], + where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = answer.id '+ + 'AND answer.question_id = question.id AND vote.user_id=auth_user.id'], + params=[answer_type_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'])) + return render_to_response(user_view.template_file,{ + "tab_name" : user_view.id, + "tab_description" : user_view.tab_description, + "page_title" : user_view.page_title, + "view_user" : user, + "votes" : votes[:user_view.data_size] + + }, context_instance=RequestContext(request)) + +def user_reputation(request, user_id, user_view): + user = get_object_or_404(User, id=user_id) + reputation = Repute.objects.extra( + select={'positive': 'sum(positive)', 'negative': 'sum(negative)', 'question_id':'question_id', + 'title': 'question.title'}, + tables=['repute', 'question'], + order_by=['-reputed_at'], + where=['user_id=%s AND question_id=question.id'], + params=[user.id] + ).values('positive', 'negative', 'question_id', 'title', 'reputed_at', 'reputation') + + reputation.query.group_by = ['question_id'] + + rep_list = [] + for rep in Repute.objects.filter(user=user).order_by('reputed_at'): + dic = '[%s,%s]' % (calendar.timegm(rep.reputed_at.timetuple()) * 1000, rep.reputation) + rep_list.append(dic) + reps = ','.join(rep_list) + reps = '[%s]' % reps + + return render_to_response(user_view.template_file,{ + "tab_name" : user_view.id, + "tab_description" : user_view.tab_description, + "page_title" : user_view.page_title, + "view_user" : user, + "reputation" : reputation, + "reps" : reps + }, context_instance=RequestContext(request)) + +def user_favorites(request, user_id, user_view): + user = get_object_or_404(User, id=user_id) + questions = Question.objects.extra( + select={ + 'vote_count' : 'question.vote_up_count + question.vote_down_count', + 'favorited_myself' : 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s '+ + 'AND f.question_id = question.id', + 'la_user_id' : 'auth_user.id', + 'la_username' : 'auth_user.username', + 'la_user_gold' : 'auth_user.gold', + 'la_user_silver' : 'auth_user.silver', + 'la_user_bronze' : 'auth_user.bronze', + 'la_user_reputation' : 'auth_user.reputation' + }, + select_params=[user_id], + tables=['question', 'auth_user', 'favorite_question'], + where=['question.deleted = 0 AND question.last_activity_by_id = auth_user.id '+ + 'AND favorite_question.question_id = question.id AND favorite_question.user_id = %s'], + params=[user_id], + order_by=['-vote_count', '-question.id'] + ).values('vote_count', + 'favorited_myself', + 'id', + 'title', + 'author_id', + 'added_at', + 'answer_accepted', + 'answer_count', + 'comment_count', + 'view_count', + 'favourite_count', + 'summary', + 'tagnames', + 'vote_up_count', + 'vote_down_count', + 'last_activity_at', + 'la_user_id', + 'la_username', + 'la_user_gold', + 'la_user_silver', + 'la_user_bronze', + 'la_user_reputation') + return render_to_response(user_view.template_file,{ + "tab_name" : user_view.id, + "tab_description" : user_view.tab_description, + "page_title" : user_view.page_title, + "questions" : questions[:user_view.data_size], + "view_user" : user + }, context_instance=RequestContext(request)) + + +def user_preferences(request, user_id, user_view): + user = get_object_or_404(User, id=user_id) + return render_to_response(user_view.template_file,{ + "tab_name" : user_view.id, + "tab_description" : user_view.tab_description, + "page_title" : user_view.page_title, + "view_user" : user, + }, context_instance=RequestContext(request)) + +def question_comments(request, id): + question = get_object_or_404(Question, id=id) + user = request.user + return __comments(request, question, 'question', user) + +def answer_comments(request, id): + answer = get_object_or_404(Answer, id=id) + user = request.user + return __comments(request, answer, 'answer', user) + +def __comments(request, obj, type, user): + # only support get comments by ajax now + if request.is_ajax(): + if request.method == "GET": + return __generate_comments_json(obj, type, user) + elif request.method == "POST": + comment_data = request.POST.get('comment') + comment = Comment(content_object=obj, comment=comment_data, user=request.user) + comment.save() + obj.comment_count = obj.comment_count + 1 + obj.save() + return __generate_comments_json(obj, type, user) + +def __generate_comments_json(obj, type, user): + comments = obj.comments.all().order_by('-id') + # {"Id":6,"PostId":38589,"CreationDate":"an hour ago","Text":"hello there!","UserDisplayName":"Jarrod Dixon","UserUrl":"/users/3/jarrod-dixon","DeleteUrl":null} + json_comments = [] + for comment in comments: + comment_user = comment.user + delete_url = "" + if user != None and auth.can_delete_comment(user, comment): + #/posts/392845/comments/219852/delete + delete_url = "/" + type + "s/%s/comments/%s/delete/" % (obj.id, comment.id) + json_comments.append({"id" : comment.id, + "object_id" : obj.id, + "add_date" : comment.added_at.strftime('%Y-%m-%d'), + "text" : comment.comment, + "user_display_name" : comment_user.username, + "user_url" : "/users/%s/%s" % (comment_user.id, comment_user.username), + "delete_url" : delete_url + }) + + data = simplejson.dumps(json_comments) + return HttpResponse(data, mimetype="application/json") + +def delete_question_comment(request, question_id, comment_id): + if request.is_ajax(): + question = get_object_or_404(Question, id=question_id) + comment = get_object_or_404(Comment, id=comment_id) + + question.comments.remove(comment) + question.comment_count = question.comment_count - 1 + question.save() + user = request.user + return __generate_comments_json(question, 'question', user) + +def delete_answer_comment(request, answer_id, comment_id): + if request.is_ajax(): + answer = get_object_or_404(Answer, id=answer_id) + comment = get_object_or_404(Comment, id=comment_id) + + answer.comments.remove(comment) + answer.comment_count = answer.comment_count - 1 + answer.save() + user = request.user + return __generate_comments_json(answer, 'answer', user) + +def logout(request): + url = request.GET.get('next') + return render_to_response('logout.html', { + 'next' : url, + }, context_instance=RequestContext(request)) + +def badges(request): + badges = Badge.objects.all().order_by('type') + my_badges = [] + if request.user.is_authenticated(): + my_badges = Award.objects.filter(user=request.user) + my_badges.query.group_by = ['badge_id'] + + return render_to_response('badges.html', { + 'badges' : badges, + 'mybadges' : my_badges, + }, context_instance=RequestContext(request)) + +def badge(request, id): + badge = get_object_or_404(Badge, id=id) + awards = Award.objects.extra( + select={'id': 'auth_user.id', + 'name': 'auth_user.username', + 'rep':'auth_user.reputation', + 'gold': 'auth_user.gold', + 'silver': 'auth_user.silver', + 'bronze': 'auth_user.bronze'}, + tables=['award', 'auth_user'], + where=['badge_id=%s AND user_id=auth_user.id'], + params=[id] + ).values('id').distinct() + + return render_to_response('badge.html', { + 'awards' : awards, + 'badge' : badge, + }, context_instance=RequestContext(request)) + +def read_message(request): + if request.method == "POST": + if request.POST['formdata'] == 'required': + request.session['message_silent'] = 1 + if request.user.is_authenticated(): + request.user.delete_messages() + return HttpResponse('') + +def upload(request): + class FileTypeNotAllow(Exception): + pass + class FileSizeNotAllow(Exception): + pass + class UploadPermissionNotAuthorized(Exception): + pass + + #%s + xml_template = "%s" + + try: + f = request.FILES['file-upload'] + # check upload permission + if not can_upload_files(request.user): + raise UploadPermissionNotAuthorized + + # check file type + file_name_suffix = os.path.splitext(f.name)[1].lower() + if not file_name_suffix in settings.ALLOW_FILE_TYPES: + raise FileTypeNotAllow + + # genetate new file name + new_file_name = str(time.time()).replace('.', str(random.randint(0,100000))) + file_name_suffix + # use default storage to store file + default_storage.save(new_file_name, f) + # check file size + # byte + size = default_storage.size(new_file_name) + if size > settings.ALLOW_MAX_FILE_SIZE: + default_storage.delete(new_file_name) + raise FileSizeNotAllow + + result = xml_template % ('Good', '', default_storage.url(new_file_name)) + except UploadPermissionNotAuthorized: + result = xml_template % ('', _('uploading images is limited to users with >60 reputation points'), '') + except FileTypeNotAllow: + result = xml_template % ('', _("allowed file types are 'jpg', 'jpeg', 'gif', 'bmp', 'png', 'tiff'"), '') + except FileSizeNotAllow: + result = xml_template % ('', _("maximum upload file size is %sK") % settings.ALLOW_MAX_FILE_SIZE / 1024, '') + except Exception: + result = xml_template % ('', _('Error uploading file. Please contact the site administrator. Thank you. %s' % Exception), '') + + return HttpResponse(result, mimetype="application/xml") + +def books(request): + return HttpResponseRedirect("/books/mysql-zhaoyang") + +def book(request, short_name, unanswered=False): + """ + 1. questions list + 2. book info + 3. author info and blog rss items + """ + """ + List of Questions, Tagged questions, and Unanswered questions. + """ + books = Book.objects.extra(where=['short_name = %s'], params=[short_name]) + match_count = len(books) + if match_count == 0 : + raise Http404 + else: + # the book info + book = books[0] + # get author info + author_info = BookAuthorInfo.objects.get(book=book) + # get author rss info + author_rss = BookAuthorRss.objects.filter(book=book) + + # get pagesize from session, if failed then get default value + user_page_size = request.session.get("pagesize", QUESTIONS_PAGE_SIZE) + # set pagesize equal to logon user specified value in database + if request.user.is_authenticated() and request.user.questions_per_page > 0: + user_page_size = request.user.questions_per_page + + try: + page = int(request.GET.get('page', '1')) + except ValueError: + page = 1 + + view_id = request.GET.get('sort', None) + view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" } + try: + orderby = view_dic[view_id] + except KeyError: + view_id = "latest" + orderby = "-added_at" + + # check if request is from tagged questions + if unanswered: + # check if request is from unanswered questions + # Article.objects.filter(publications__id__exact=1) + objects = Question.objects.filter(book__id__exact=book.id, deleted=False, answer_count=0).order_by(orderby) + else: + objects = Question.objects.filter(book__id__exact=book.id, deleted=False).order_by(orderby) + + # RISK - inner join queries + objects = objects.select_related(); + objects_list = Paginator(objects, user_page_size) + questions = objects_list.page(page) + + return render_to_response('book.html', { + "book" : book, + "author_info" : author_info, + "author_rss" : author_rss, + "questions" : questions, + "context" : { + 'is_paginated' : True, + 'pages': objects_list.num_pages, + 'page': page, + 'has_previous': questions.has_previous(), + 'has_next': questions.has_next(), + 'previous': questions.previous_page_number(), + 'next': questions.next_page_number(), + 'base_url' : request.path + '?sort=%s&' % view_id, + 'pagesize' : user_page_size + } + }, context_instance=RequestContext(request)) + +@login_required +def ask_book(request, short_name): + if request.method == "POST": + form = AskForm(request.POST) + if form.is_valid(): + added_at = datetime.datetime.now() + html = sanitize_html(markdowner.convert(form.cleaned_data['text'])) + question = Question( + title = strip_tags(form.cleaned_data['title']), + author = request.user, + added_at = added_at, + last_activity_at = added_at, + last_activity_by = request.user, + wiki = form.cleaned_data['wiki'], + tagnames = form.cleaned_data['tags'].strip(), + html = html, + summary = strip_tags(html)[:120] + ) + if question.wiki: + question.last_edited_by = question.author + question.last_edited_at = added_at + question.wikified_at = added_at + + question.save() + + # create the first revision + QuestionRevision.objects.create( + question = question, + revision = 1, + title = question.title, + author = request.user, + revised_at = added_at, + tagnames = question.tagnames, + summary = CONST['default_version'], + text = form.cleaned_data['text'] + ) + + books = Book.objects.extra(where=['short_name = %s'], params=[short_name]) + match_count = len(books) + if match_count == 1: + # the book info + book = books[0] + book.questions.add(question) + + return HttpResponseRedirect(question.get_absolute_url()) + else: + form = AskForm() + + tags = _get_tags_cache_json() + return render_to_response('ask.html', { + 'form' : form, + 'tags' : tags, + }, context_instance=RequestContext(request)) + +def search(request): + """ + Search by question, user and tag keywords. + For questions now we only search keywords in question title. + """ + if request.method == "GET": + keywords = request.GET.get("q") + search_type = request.GET.get("t") + try: + page = int(request.GET.get('page', '1')) + except ValueError: + page = 1 + if keywords is None: + return HttpResponseRedirect('/') + if search_type == 'tag': + return HttpResponseRedirect('/tags/?q=%s&page=%s' % (keywords.strip(), page)) + elif search_type == "user": + return HttpResponseRedirect('/users/?q=%s&page=%s' % (keywords.strip(), page)) + elif search_type == "question": + + template_file = "questions.html" + # Set flag to False by default. If it is equal to True, then need to be saved. + pagesize_changed = False + # get pagesize from session, if failed then get default value + user_page_size = request.session.get("pagesize", QUESTIONS_PAGE_SIZE) + # set pagesize equal to logon user specified value in database + if request.user.is_authenticated() and request.user.questions_per_page > 0: + user_page_size = request.user.questions_per_page + + try: + page = int(request.GET.get('page', '1')) + # get new pagesize from UI selection + pagesize = int(request.GET.get('pagesize', user_page_size)) + if pagesize <> user_page_size: + pagesize_changed = True + + except ValueError: + page = 1 + pagesize = user_page_size + + # save this pagesize to user database + if pagesize_changed: + request.session["pagesize"] = pagesize + if request.user.is_authenticated(): + user = request.user + user.questions_per_page = pagesize + user.save() + + view_id = request.GET.get('sort', None) + view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" } + try: + orderby = view_dic[view_id] + except KeyError: + view_id = "latest" + orderby = "-added_at" + + objects = Question.objects.filter(deleted=False).extra(where=['title like %s'], params=['%' + keywords + '%']).order_by(orderby) + + # RISK - inner join queries + objects = objects.select_related(); + objects_list = Paginator(objects, pagesize) + questions = objects_list.page(page) + + # Get related tags from this page objects + related_tags = [] + for question in questions.object_list: + tags = list(question.tags.all()) + for tag in tags: + if tag not in related_tags: + related_tags.append(tag) + + return render_to_response(template_file, { + "questions" : questions, + "tab_id" : view_id, + "questions_count" : objects_list.count, + "tags" : related_tags, + "searchtag" : None, + "searchtitle" : keywords, + "keywords" : keywords, + "is_unanswered" : False, + "context" : { + 'is_paginated' : True, + 'pages': objects_list.num_pages, + 'page': page, + 'has_previous': questions.has_previous(), + 'has_next': questions.has_next(), + 'previous': questions.previous_page_number(), + 'next': questions.next_page_number(), + 'base_url' : request.path + '?t=question&q=%s&sort=%s&' % (keywords, view_id), + 'pagesize' : pagesize + }}, context_instance=RequestContext(request)) + + else: + raise Http404 -- cgit v1.2.3-1-g7c22 From 7b559daeac59b56adb7e911dad4e692a7e6fa6c4 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 12 Aug 2009 00:20:14 +0800 Subject: remove settings_local.py --- .gitignore | 1 + LICENSE | 28 +++++++++++++-------------- settings.py | 2 +- sql_scripts/update_2009_01_25_001.sql | 2 +- sql_scripts/update_2009_02_26_001.sql | 36 +++++++++++++++++------------------ sql_scripts/update_2009_04_10_001.sql | 4 ++-- templates/content/js/compress.bat | 10 +++++----- templates/content/js/flot-build.bat | 4 ++-- 8 files changed, 44 insertions(+), 43 deletions(-) diff --git a/.gitignore b/.gitignore index 4f83efab..bb9e4490 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.pyc *.swp nbproject + diff --git a/LICENSE b/LICENSE index cb24f678..803781c5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,14 +1,14 @@ -Copyright (C) 2009. Chen Gang - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . +Copyright (C) 2009. Chen Gang + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . diff --git a/settings.py b/settings.py index fac3c0d5..5eb7ceda 100644 --- a/settings.py +++ b/settings.py @@ -91,7 +91,7 @@ INSTALLED_APPS = ( 'django.contrib.humanize', 'forum', 'django_authopenid', - 'debug_toolbar' , + #'debug_toolbar' , ) # User settings diff --git a/sql_scripts/update_2009_01_25_001.sql b/sql_scripts/update_2009_01_25_001.sql index 1f1942e3..f6f3bed2 100644 --- a/sql_scripts/update_2009_01_25_001.sql +++ b/sql_scripts/update_2009_01_25_001.sql @@ -1,2 +1,2 @@ -ALTER TABLE `award` ADD `content_type_id` INT NULL +ALTER TABLE `award` ADD `content_type_id` INT NULL ALTER TABLE `award` ADD `object_id` INT NULL \ No newline at end of file diff --git a/sql_scripts/update_2009_02_26_001.sql b/sql_scripts/update_2009_02_26_001.sql index 9cc80974..4113d8f5 100644 --- a/sql_scripts/update_2009_02_26_001.sql +++ b/sql_scripts/update_2009_02_26_001.sql @@ -1,19 +1,19 @@ -ALTER TABLE answer ADD COLUMN `accepted_at` datetime default null; - -/* Update accepted_at column with answer added datetime for existing data */ -UPDATE answer -SET accepted_at = added_at -WHERE accepted = 1 AND accepted_at IS NULL; - -/* workround for c# url problem on bluehost server */ -UPDATE tag -SET name = 'csharp' -WHERE name = 'c#' - -UPDATE question -SET tagnames = replace(tagnames, 'c#', 'csharp') -WHERE tagnames like '%c#%' - -UPDATE question_revision -SET tagnames = replace(tagnames, 'c#', 'csharp') +ALTER TABLE answer ADD COLUMN `accepted_at` datetime default null; + +/* Update accepted_at column with answer added datetime for existing data */ +UPDATE answer +SET accepted_at = added_at +WHERE accepted = 1 AND accepted_at IS NULL; + +/* workround for c# url problem on bluehost server */ +UPDATE tag +SET name = 'csharp' +WHERE name = 'c#' + +UPDATE question +SET tagnames = replace(tagnames, 'c#', 'csharp') +WHERE tagnames like '%c#%' + +UPDATE question_revision +SET tagnames = replace(tagnames, 'c#', 'csharp') WHERE tagnames like '%c#%' \ No newline at end of file diff --git a/sql_scripts/update_2009_04_10_001.sql b/sql_scripts/update_2009_04_10_001.sql index b0d05ac7..e1ceb3bc 100644 --- a/sql_scripts/update_2009_04_10_001.sql +++ b/sql_scripts/update_2009_04_10_001.sql @@ -1,3 +1,3 @@ -ALTER TABLE Tag ADD COLUMN deleted_at datetime default null; -ALTER TABLE Tag ADD COLUMN deleted_by_id INTEGER NULL; +ALTER TABLE Tag ADD COLUMN deleted_at datetime default null; +ALTER TABLE Tag ADD COLUMN deleted_by_id INTEGER NULL; ALTER TABLE Tag ADD COLUMN deleted TINYINT NOT NULL; \ No newline at end of file diff --git a/templates/content/js/compress.bat b/templates/content/js/compress.bat index aa31271c..41e1882a 100644 --- a/templates/content/js/compress.bat +++ b/templates/content/js/compress.bat @@ -1,6 +1,6 @@ -#java -jar yuicompressor-2.4.2.jar --type js --charset utf-8 wmd\wmd.js -o wmd\wmd-min.js -#java -jar yuicompressor-2.4.2.jar --type js --charset utf-8 wmd\showdown.js -o wmd\showdown-min.js -#java -jar yuicompressor-2.4.2.jar --type js --charset utf-8 com.cnprog.post.js -o com.cnprog.post.pack.js -java -jar yuicompressor-2.4.2.jar --type js --charset utf-8 se_hilite_src.js -o se_hilite.js - +#java -jar yuicompressor-2.4.2.jar --type js --charset utf-8 wmd\wmd.js -o wmd\wmd-min.js +#java -jar yuicompressor-2.4.2.jar --type js --charset utf-8 wmd\showdown.js -o wmd\showdown-min.js +#java -jar yuicompressor-2.4.2.jar --type js --charset utf-8 com.cnprog.post.js -o com.cnprog.post.pack.js +java -jar yuicompressor-2.4.2.jar --type js --charset utf-8 se_hilite_src.js -o se_hilite.js + pause \ No newline at end of file diff --git a/templates/content/js/flot-build.bat b/templates/content/js/flot-build.bat index 28304966..fc715e3a 100644 --- a/templates/content/js/flot-build.bat +++ b/templates/content/js/flot-build.bat @@ -1,3 +1,3 @@ -java -jar yuicompressor-2.4.2.jar --type js --charset utf-8 jquery.flot.js -o jquery.flot.pack.js - +java -jar yuicompressor-2.4.2.jar --type js --charset utf-8 jquery.flot.js -o jquery.flot.pack.js + pause \ No newline at end of file -- cgit v1.2.3-1-g7c22 From 57785f8db55e71d81c03d158cf6035768e22c63f Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 12 Aug 2009 00:21:27 +0800 Subject: update .gitignore to exclude settings_local.py --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index bb9e4490..05a341b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ *.pyc *.swp nbproject - +settings_local.py -- cgit v1.2.3-1-g7c22 From 42a226eac8e9c4b06c1c25b6d813d0eb342bc0d0 Mon Sep 17 00:00:00 2001 From: Mike Chen Date: Wed, 12 Aug 2009 00:50:09 +0800 Subject: update settings.py and settings_local.py.dist suggested by Adolfo Fitoria --- settings.py | 31 ++---------------------- settings_local.py.dist | 65 +++++++++++++++++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 51 deletions(-) diff --git a/settings.py b/settings.py index 5eb7ceda..ce8c9d89 100644 --- a/settings.py +++ b/settings.py @@ -17,27 +17,8 @@ ADMINS = ( ) MANAGERS = ADMINS -#email server settings -SERVER_EMAIL = '' -DEFAULT_FROM_EMAIL = 'team@cnprog.com' -EMAIL_HOST_USER = '' -EMAIL_HOST_PASSWORD = '' -EMAIL_SUBJECT_PREFIX = '[cnprog.com]' -EMAIL_HOST='smtp.gmail.com' -EMAIL_PORT='587' -EMAIL_USE_TLS=True - -#LOCALIZATIONS -TIME_ZONE = 'Asia/Chongqing Asia/Chungking' -# LANGUAGE_CODE = 'en-us' SITE_ID = 1 -#OTHER SETTINGS -APP_TITLE = u'CNProg.com 程序员问答社区' -APP_KEYWORDS = u'技术问答社区,中国程序员,编程技术社区,程序员社区,程序员论坛,程序员wiki,程序员博客' -APP_DESCRIPTION = u'中国程序员的编程技术问答社区。我们做专业的、可协作编辑的技术问答社区。' -APP_INTRO = u'

    CNProg是一个面向程序员的可协作编辑的开放源代码问答社区

    您可以在这里提问各类程序技术问题 - 问题不分语言和平台。 同时也希望您对力所能及的问题,给予您的宝贵答案。

    ' -APP_COPYRIGHT = 'Copyright CNPROG.COM 2009' ADMIN_MEDIA_PREFIX = '/admin/media/' SECRET_KEY = '$oo^&_m&qwbib=(_4m_n*zn-d=g#s0he5fx9xonnym#8p6yigm' # List of callables that know how to import templates from various sources. @@ -64,6 +45,7 @@ TEMPLATE_CONTEXT_PROCESSORS = ( 'context.auth_processor', 'context.application_settings', #'django.core.context_processors.i18n', + 'django.core.context_processors.auth' #this is required for admin ) ROOT_URLCONF = 'urls' @@ -95,13 +77,4 @@ INSTALLED_APPS = ( ) # User settings -from settings_local import * - -USE_I18N = True -LANGUAGE_CODE = 'en' -EMAIL_VALIDATION = 'off' -MIN_USERNAME_LENGTH = 1 -EMAIL_UNIQUE = False -APP_URL = 'http://server.com' #used by email notif system and RSS -GOOGLE_SITEMAP_CODE = '55uGNnQVJW8p1bbXeF/Xbh9I7nZBM/wLhRz6N/I1kkA=' -GOOGLE_ANALYTICS_KEY = '' +from settings_local import * \ No newline at end of file diff --git a/settings_local.py.dist b/settings_local.py.dist index 2c8f939d..453bbcaf 100644 --- a/settings_local.py.dist +++ b/settings_local.py.dist @@ -1,25 +1,46 @@ -SITE_SRC_ROOT = '/Users/sailing/Development/cnprog_beta2' - +# encoding:utf-8 +import os.path + +SITE_SRC_ROOT = os.path.dirname(__file__) +LOG_FILENAME = 'django.lanai.log' + #for logging import logging -LOG_FILENAME = '/Users/sailing/Development/cnprog_beta2/django.lanai.log' -logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG,) - - -#Database configuration -DATABASE_ENGINE = 'mysql' -DATABASE_HOST = '' -DATABASE_PORT = '' -DATABASE_NAME = 'cnprog' # Or path to database file if using sqlite3. -DATABASE_USER = 'root' # Not used with sqlite3. +logging.basicConfig(filename=os.path.join(SITE_SRC_ROOT, 'log', LOG_FILENAME), level=logging.DEBUG,) + +DATABASE_NAME = '' # Or path to database file if using sqlite3. +DATABASE_USER = '' # Not used with sqlite3. DATABASE_PASSWORD = '' # Not used with sqlite3. - - -# Absolute path to the directory that holds media. -# Example: "/home/media/media.lawrence.com/" -MEDIA_ROOT = '/Users/sailing/Development/cnprog_beta2/templates/upfiles/' - -# URL that handles the media served from MEDIA_ROOT. Make sure to use a -# trailing slash if there is a path component (optional in other cases). -# Examples: "http://media.lawrence.com", "http://example.com/media/" -MEDIA_URL = 'http://127.0.0.1:8000/upfiles/' +DATABASE_ENGINE = '' #mysql, etc + +#Moved from settings.py for better organization. (please check it up to clean up settings.py) + +#email server settings +SERVER_EMAIL = '' +DEFAULT_FROM_EMAIL = 'team@cnprog.com' +EMAIL_HOST_USER = '' +EMAIL_HOST_PASSWORD = '' +EMAIL_SUBJECT_PREFIX = '[cnprog.com]' +EMAIL_HOST='smtp.gmail.com' +EMAIL_PORT='587' +EMAIL_USE_TLS=True + +#LOCALIZATIONS +TIME_ZONE = 'Asia/Chongqing Asia/Chungking' +# LANGUAGE_CODE = 'en-us' + +#OTHER SETTINGS +APP_TITLE = u'CNProg.com 程序员问答社区' +APP_KEYWORDS = u'技术问答社区,中国程序员,编程技术社区,程序员社区,程序员论坛,程序员wiki,程序员博客' +APP_DESCRIPTION = u'中国程序员的编程技术问答社区。我们做专业的、可协作编辑的技术问答社区。' +APP_INTRO = u'

    CNProg是一个面向程序员的可协作编辑的开放源代码问答社区

    您可以在这里提问各类程序技术问题 - 问题不分语言和平台。 同时也希望您对力所能及的问题,给予您的宝贵答案。

    ' +APP_COPYRIGHT = 'Copyright CNPROG.COM 2009' + +USE_I18N = True +LANGUAGE_CODE = 'en' +EMAIL_VALIDATION = 'off' +MIN_USERNAME_LENGTH = 1 +EMAIL_UNIQUE = False +APP_URL = 'http://server.com' #used by email notif system and RSS +GOOGLE_SITEMAP_CODE = '55uGNnQVJW8p1bbXeF/Xbh9I7nZBM/wLhRz6N/I1kkA=' +GOOGLE_ANALYTICS_KEY = '' \ No newline at end of file -- cgit v1.2.3-1-g7c22 From c928d679ca8513a0d5a64bcd2831fd85bb396bb9 Mon Sep 17 00:00:00 2001 From: Mike Chen Date: Fri, 14 Aug 2009 00:01:51 +0800 Subject: temporary fix for 1.1 aggregation compatible issue --- forum/views.py | 30 +++++++++++++++++++++--------- settings.py | 7 ++++--- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/forum/views.py b/forum/views.py index 6ef7bd6d..e47b9111 100644 --- a/forum/views.py +++ b/forum/views.py @@ -1125,15 +1125,27 @@ def user_stats(request, user_id, user_view): votes_today = Vote.objects.get_votes_count_today_from_user(user) votes_total = VOTE_RULES['scope_votes_per_user_per_day'] tags = user.created_tags.all().order_by('-used_count')[:50] - awards = Award.objects.extra( - select={'id': 'badge.id', 'count': 'count(badge_id)', 'name':'badge.name', 'description': 'badge.description', 'type': 'badge.type'}, - tables=['award', 'badge'], - order_by=['-awarded_at'], - where=['user_id=%s AND badge_id=badge.id'], - params=[user.id] - ).values('id', 'count', 'name', 'description', 'type') - total_awards = awards.count() - awards.query.group_by = ['badge_id'] + if settings.DJANGO_VERSION < 1.1: + awards = Award.objects.extra( + select={'id': 'badge.id', 'count': 'count(badge_id)', 'name':'badge.name', 'description': 'badge.description', 'type': 'badge.type'}, + tables=['award', 'badge'], + order_by=['-awarded_at'], + where=['user_id=%s AND badge_id=badge.id'], + params=[user.id] + ).values('id', 'count', 'name', 'description', 'type') + total_awards = awards.count() + awards.query.group_by = ['badge_id'] + else: + awards = Award.objects.extra( + select={'id': 'badge.id', 'name':'badge.name', 'description': 'badge.description', 'type': 'badge.type'}, + tables=['award', 'badge'], + order_by=['-awarded_at'], + where=['user_id=%s AND badge_id=badge.id'], + params=[user.id] + ).values('id', 'name', 'description', 'type') + total_awards = awards.count() + from django.db.models import Count + awards = awards.annotate(count = Count('badge__id')) return render_to_response(user_view.template_file,{ "tab_name" : user_view.id, diff --git a/settings.py b/settings.py index ce8c9d89..aa991898 100644 --- a/settings.py +++ b/settings.py @@ -37,7 +37,7 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.transaction.TransactionMiddleware', #'django.middleware.sqlprint.SqlPrintingMiddleware', #'middleware.pagesize.QuestionsPageSizeMiddleware', - #'debug_toolbar.middleware.DebugToolbarMiddleware', + 'debug_toolbar.middleware.DebugToolbarMiddleware', ) TEMPLATE_CONTEXT_PROCESSORS = ( @@ -73,8 +73,9 @@ INSTALLED_APPS = ( 'django.contrib.humanize', 'forum', 'django_authopenid', - #'debug_toolbar' , + 'debug_toolbar' , ) - +import django +DJANGO_VERSION = django.get_version() # User settings from settings_local import * \ No newline at end of file -- cgit v1.2.3-1-g7c22 From 90dffc13c16494bca294bb2eef2353f970509cad Mon Sep 17 00:00:00 2001 From: Mike Chen Date: Wed, 19 Aug 2009 22:02:52 +0800 Subject: fix django_authopenid register bug --- django_authopenid/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_authopenid/views.py b/django_authopenid/views.py index 1cb8928b..1d072852 100644 --- a/django_authopenid/views.py +++ b/django_authopenid/views.py @@ -367,7 +367,7 @@ def register(request): return render('authopenid/complete.html', { 'form1': form1, 'form2': form2, - 'provider':providers[provider_name], + 'provider':provider_logo, 'nickname': nickname, 'email': email }, context_instance=RequestContext(request)) -- cgit v1.2.3-1-g7c22 From 0b725addffa936ffb2cfa0be2d8d85f784661ee7 Mon Sep 17 00:00:00 2001 From: Mike Chen Date: Sun, 23 Aug 2009 23:08:15 +0800 Subject: update user_reputation for Django aggregation changes --- forum/views.py | 1673 ++++++++++++++++++++++++++++---------------------------- settings.py | 4 +- 2 files changed, 846 insertions(+), 831 deletions(-) diff --git a/forum/views.py b/forum/views.py index e47b9111..0d2b3ca2 100644 --- a/forum/views.py +++ b/forum/views.py @@ -1,32 +1,35 @@ # encoding:utf-8 -import os.path -import time, datetime, calendar, random -import logging -from urllib import quote, unquote +import calendar from django.conf import settings -from django.core.files.storage import default_storage -from django.shortcuts import render_to_response, get_object_or_404 from django.contrib.auth.decorators import login_required -from django.http import HttpResponseRedirect, HttpResponse,Http404 -from django.core.paginator import Paginator, EmptyPage, InvalidPage +from django.contrib.contenttypes.models import ContentType +from django.core.files.storage import default_storage +from django.core.paginator import EmptyPage +from django.core.paginator import InvalidPage +from django.core.paginator import Paginator +from django.http import Http404 +from django.http import HttpResponse +from django.http import HttpResponseRedirect +from django.shortcuts import get_object_or_404 +from django.shortcuts import render_to_response from django.template import RequestContext -from django.utils.html import * from django.utils import simplejson -from django.core import serializers -from django.db import transaction -from django.contrib.contenttypes.models import ContentType +from django.utils.html import * from django.utils.translation import ugettext as _ - -from utils.html import sanitize_html from markdown2 import Markdown -#from lxml.html.diff import htmldiff +import os.path +import random +import time + +import datetime +from forum import auth +from forum.auth import * +from forum.const import * from forum.diff import textDiff as htmldiff from forum.forms import * from forum.models import * -from forum.auth import * -from forum.const import * from forum.user import * -from forum import auth +from utils.html import sanitize_html # used in index page INDEX_PAGE_SIZE = 20 @@ -46,7 +49,7 @@ answer_type = ContentType.objects.get_for_model(Answer) comment_type = ContentType.objects.get_for_model(Comment) question_revision_type = ContentType.objects.get_for_model(QuestionRevision) answer_revision_type = ContentType.objects.get_for_model(AnswerRevision) -repute_type =ContentType.objects.get_for_model(Repute) +repute_type = ContentType.objects.get_for_model(Repute) question_type_id = question_type.id answer_type_id = answer_type.id comment_type_id = comment_type.id @@ -57,7 +60,7 @@ def _get_tags_cache_json(): tags = Tag.objects.filter(deleted=False).all() tags_list = [] for tag in tags: - dic = {'n': tag.name, 'c': tag.used_count } + dic = {'n': tag.name, 'c': tag.used_count} tags_list.append(dic) tags = simplejson.dumps(tags_list) return tags @@ -65,11 +68,11 @@ def _get_tags_cache_json(): def index(request): view_id = request.GET.get('sort', None) view_dic = { - "latest":"-last_activity_at", - "hottest":"-answer_count", - "mostvoted":"-score", - "trans": "-last_activity_at" - } + "latest":"-last_activity_at", + "hottest":"-answer_count", + "mostvoted":"-score", + "trans": "-last_activity_at" + } try: orderby = view_dic[view_id] except KeyError: @@ -87,11 +90,11 @@ def index(request): awards = Award.objects.get_recent_awards() return render_to_response('index.html', { - "questions" : questions, - "tab_id" : view_id, - "tags" : tags, - "awards" : awards[:INDEX_AWARD_SIZE], - }, context_instance=RequestContext(request)) + "questions": questions, + "tab_id": view_id, + "tags": tags, + "awards": awards[:INDEX_AWARD_SIZE], + }, context_instance=RequestContext(request)) def about(request): return render_to_response('about.html', context_instance=RequestContext(request)) @@ -115,14 +118,14 @@ def questions(request, tagname=None, unanswered=False): # Set flag to False by default. If it is equal to True, then need to be saved. pagesize_changed = False # get pagesize from session, if failed then get default value - pagesize = request.session.get("pagesize",10) + pagesize = request.session.get("pagesize", 10) try: page = int(request.GET.get('page', '1')) except ValueError: page = 1 view_id = request.GET.get('sort', None) - view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" } + view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score"} try: orderby = view_dic[view_id] except KeyError: @@ -150,38 +153,38 @@ def questions(request, tagname=None, unanswered=False): else: related_tags = None return render_to_response(template_file, { - "questions" : questions, - "tab_id" : view_id, - "questions_count" : objects_list.count, - "tags" : related_tags, - "searchtag" : tagname, - "is_unanswered" : unanswered, - "context" : { - 'is_paginated' : True, - 'pages': objects_list.num_pages, - 'page': page, - 'has_previous': questions.has_previous(), - 'has_next': questions.has_next(), - 'previous': questions.previous_page_number(), - 'next': questions.next_page_number(), - 'base_url' : request.path + '?sort=%s&' % view_id, - 'pagesize' : pagesize - }}, context_instance=RequestContext(request)) - -def create_new_answer( question=None, author=None,\ - added_at=None, wiki=False,\ - text='', email_notify=False): + "questions": questions, + "tab_id": view_id, + "questions_count": objects_list.count, + "tags": related_tags, + "searchtag": tagname, + "is_unanswered": unanswered, + "context": { + 'is_paginated': True, + 'pages': objects_list.num_pages, + 'page': page, + 'has_previous': questions.has_previous(), + 'has_next': questions.has_next(), + 'previous': questions.previous_page_number(), + 'next': questions.next_page_number(), + 'base_url': request.path + '?sort=%s&' % view_id, + 'pagesize': pagesize + }}, context_instance=RequestContext(request)) + +def create_new_answer(question=None, author=None, \ + added_at=None, wiki=False, \ + text='', email_notify=False): html = sanitize_html(markdowner.convert(text)) #create answer answer = Answer( - question = question, - author = author, - added_at = added_at, - wiki = wiki, - html = html - ) + question=question, + author=author, + added_at=added_at, + wiki=wiki, + html=html + ) if answer.wiki: answer.last_edited_by = answer.author answer.last_edited_at = added_at @@ -197,47 +200,47 @@ def create_new_answer( question=None, author=None,\ #update revision AnswerRevision.objects.create( - answer = answer, - revision = 1, - author = author, - revised_at = added_at, - summary = CONST['default_version'], - text = text - ) + answer=answer, + revision=1, + author=author, + revised_at=added_at, + summary=CONST['default_version'], + text=text + ) #set notification/delete if email_notify: try: - EmailFeed.objects.get(feed_id = question.id, subscriber_id = author.id, feed_content_type=question_type) + EmailFeed.objects.get(feed_id=question.id, subscriber_id=author.id, feed_content_type=question_type) except EmailFeed.DoesNotExist: - feed = EmailFeed(content = question, subscriber = author) + feed = EmailFeed(content=question, subscriber=author) feed.save() else: #not sure if this is necessary. ajax should take care of this... try: - feed = Email.objects.get(feed_id = question.id, subscriber_id = author.id, feed_content_type=question_type) + feed = Email.objects.get(feed_id=question.id, subscriber_id=author.id, feed_content_type=question_type) feed.delete() except: pass -def create_new_question(title=None,author=None,added_at=None, - wiki=False,tagnames=None,summary=None, +def create_new_question(title=None, author=None, added_at=None, + wiki=False, tagnames=None, summary=None, text=None): """this is not a view and maybe should become one of the methods on Question object? """ html = sanitize_html(markdowner.convert(text)) question = Question( - title = title, - author = author, - added_at = added_at, - last_activity_at = added_at, - last_activity_by = author, - wiki = wiki, - tagnames = tagnames, - html = html, - summary = summary - ) + title=title, + author=author, + added_at=added_at, + last_activity_at=added_at, + last_activity_by=author, + wiki=wiki, + tagnames=tagnames, + html=html, + summary=summary + ) if question.wiki: question.last_edited_by = question.author question.last_edited_at = added_at @@ -247,15 +250,15 @@ def create_new_question(title=None,author=None,added_at=None, # create the first revision QuestionRevision.objects.create( - question = question, - revision = 1, - title = question.title, - author = author, - revised_at = added_at, - tagnames = question.tagnames, - summary = CONST['default_version'], - text = text - ) + question=question, + revision=1, + title=question.title, + author=author, + revised_at=added_at, + tagnames=question.tagnames, + summary=CONST['default_version'], + text=text + ) return question #TODO: allow anynomus user to ask question by providing email and username. @@ -277,39 +280,39 @@ def ask(request): author = request.user question = create_new_question( - title = title, - author = author, - added_at = added_at, - wiki = wiki, - tagnames = tagnames, - summary = summary, - text = text - ) + title=title, + author=author, + added_at=added_at, + wiki=wiki, + tagnames=tagnames, + summary=summary, + text=text + ) return HttpResponseRedirect(question.get_absolute_url()) else: request.session.flush() session_key = request.session.session_key question = AnonymousQuestion( - session_key = session_key, - title = title, - tagnames = tagnames, - wiki = wiki, - text = text, - summary = summary, - added_at = added_at, - ip_addr = request.META['REMOTE_ADDR'], - ) + session_key=session_key, + title=title, + tagnames=tagnames, + wiki=wiki, + text=text, + summary=summary, + added_at=added_at, + ip_addr=request.META['REMOTE_ADDR'], + ) question.save() - return HttpResponseRedirect('%s%s%s' % ( _('/account/'),_('signin/'),('newquestion/'))) + return HttpResponseRedirect('%s%s%s' % (_('/account/'), _('signin/'), ('newquestion/'))) else: form = AskForm() tags = _get_tags_cache_json() return render_to_response('ask.html', { - 'form' : form, - 'tags' : tags, - }, context_instance=RequestContext(request)) + 'form': form, + 'tags': tags, + }, context_instance=RequestContext(request)) def question(request, id): try: @@ -317,7 +320,7 @@ def question(request, id): except ValueError: page = 1 view_id = request.GET.get('sort', 'votes') - view_dic = {"latest":"-added_at", "oldest":"added_at", "votes":"-score" } + view_dic = {"latest":"-added_at", "oldest":"added_at", "votes":"-score"} try: orderby = view_dic[view_id] except KeyError: @@ -327,7 +330,7 @@ def question(request, id): question = get_object_or_404(Question, id=id) if question.deleted and not can_view_deleted_post(request.user, question): raise Http404 - answer_form = AnswerForm(question,request.user) + answer_form = AnswerForm(question, request.user) answers = Answer.objects.get_answers_from_question(question, request.user) answers = answers.select_related(depth=1) @@ -362,28 +365,28 @@ def question(request, id): # update view count Question.objects.update_view_count(question) return render_to_response('question.html', { - "question" : question, - "question_vote" : question_vote, - "question_comment_count":question.comments.count(), - "answer" : answer_form, - "answers" : page_objects.object_list, - "user_answer_votes": user_answer_votes, - "tags" : question.tags.all(), - "tab_id" : view_id, - "favorited" : favorited, - "similar_questions" : Question.objects.get_similar_questions(question), - "context" : { - 'is_paginated' : True, - 'pages': objects_list.num_pages, - 'page': page, - 'has_previous': page_objects.has_previous(), - 'has_next': page_objects.has_next(), - 'previous': page_objects.previous_page_number(), - 'next': page_objects.next_page_number(), - 'base_url' : request.path + '?sort=%s&' % view_id, - 'extend_url' : "#sort-top" - } - }, context_instance=RequestContext(request)) + "question": question, + "question_vote": question_vote, + "question_comment_count":question.comments.count(), + "answer": answer_form, + "answers": page_objects.object_list, + "user_answer_votes": user_answer_votes, + "tags": question.tags.all(), + "tab_id": view_id, + "favorited": favorited, + "similar_questions": Question.objects.get_similar_questions(question), + "context": { + 'is_paginated': True, + 'pages': objects_list.num_pages, + 'page': page, + 'has_previous': page_objects.has_previous(), + 'has_next': page_objects.has_next(), + 'previous': page_objects.previous_page_number(), + 'next': page_objects.next_page_number(), + 'base_url': request.path + '?sort=%s&' % view_id, + 'extend_url': "#sort-top" + } + }, context_instance=RequestContext(request)) @login_required def close(request, id): @@ -403,9 +406,9 @@ def close(request, id): else: form = CloseForm() return render_to_response('close.html', { - 'form' : form, - 'question' : question, - }, context_instance=RequestContext(request)) + 'form': form, + 'question': question, + }, context_instance=RequestContext(request)) @login_required def reopen(request, id): @@ -413,14 +416,14 @@ def reopen(request, id): # open question if not can_reopen_question(request.user, question): return HttpResponse('Permission denied.') - if request.method == 'POST' : + if request.method == 'POST': Question.objects.filter(id=question.id).update(closed=False, - closed_by=None, closed_at=None, close_reason=None) + closed_by=None, closed_at=None, close_reason=None) return HttpResponseRedirect(question.get_absolute_url()) else: return render_to_response('reopen.html', { - 'question' : question, - }, context_instance=RequestContext(request)) + 'question': question, + }, context_instance=RequestContext(request)) @login_required def edit_question(request, id): @@ -443,25 +446,25 @@ def _retag_question(request, question): retagged_at = datetime.datetime.now() # Update the Question itself Question.objects.filter(id=question.id).update( - tagnames = form.cleaned_data['tags'], - last_edited_at = retagged_at, - last_edited_by = request.user, - last_activity_at = retagged_at, - last_activity_by = request.user - ) + tagnames=form.cleaned_data['tags'], + last_edited_at=retagged_at, + last_edited_by=request.user, + last_activity_at=retagged_at, + last_activity_by=request.user + ) # Update the Question's tag associations tags_updated = Question.objects.update_tags(question, - form.cleaned_data['tags'], request.user) + form.cleaned_data['tags'], request.user) # Create a new revision QuestionRevision.objects.create( - question = question, - title = latest_revision.title, - author = request.user, - revised_at = retagged_at, - tagnames = form.cleaned_data['tags'], - summary = CONST['retagged'], - text = latest_revision.text - ) + question=question, + title=latest_revision.title, + author=request.user, + revised_at=retagged_at, + tagnames=form.cleaned_data['tags'], + summary=CONST['retagged'], + text=latest_revision.text + ) # send tags updated singal tags_updated.send(sender=question.__class__, question=question) @@ -469,10 +472,10 @@ def _retag_question(request, question): else: form = RetagQuestionForm(question) return render_to_response('question_retag.html', { - 'question': question, - 'form' : form, - 'tags' : _get_tags_cache_json(), - }, context_instance=RequestContext(request)) + 'question': question, + 'form': form, + 'tags': _get_tags_cache_json(), + }, context_instance=RequestContext(request)) def _edit_question(request, question): @@ -485,8 +488,8 @@ def _edit_question(request, question): if revision_form.is_valid(): # Replace with those from the selected revision form = EditQuestionForm(question, - QuestionRevision.objects.get(question=question, - revision=revision_form.cleaned_data['revision'])) + QuestionRevision.objects.get(question=question, + revision=revision_form.cleaned_data['revision'])) else: form = EditQuestionForm(question, latest_revision, request.POST) else: @@ -520,20 +523,20 @@ def _edit_question(request, question): updated_fields['wikified_at'] = edited_at Question.objects.filter( - id=question.id).update(**updated_fields) + id=question.id).update(** updated_fields) # Update the Question's tag associations if tags_changed: tags_updated = Question.objects.update_tags( - question, form.cleaned_data['tags'], request.user) + question, form.cleaned_data['tags'], request.user) # Create a new revision revision = QuestionRevision( - question = question, - title = form.cleaned_data['title'], - author = request.user, - revised_at = edited_at, - tagnames = form.cleaned_data['tags'], - text = form.cleaned_data['text'], - ) + question=question, + title=form.cleaned_data['title'], + author=request.user, + revised_at=edited_at, + tagnames=form.cleaned_data['tags'], + text=form.cleaned_data['text'], + ) if form.cleaned_data['summary']: revision.summary = form.cleaned_data['summary'] else: @@ -546,11 +549,11 @@ def _edit_question(request, question): revision_form = RevisionForm(question, latest_revision) form = EditQuestionForm(question, latest_revision) return render_to_response('question_edit.html', { - 'question': question, - 'revision_form': revision_form, - 'form' : form, - 'tags' : _get_tags_cache_json() - }, context_instance=RequestContext(request)) + 'question': question, + 'revision_form': revision_form, + 'form': form, + 'tags': _get_tags_cache_json() + }, context_instance=RequestContext(request)) @login_required @@ -569,8 +572,8 @@ def edit_answer(request, id): if revision_form.is_valid(): # Replace with those from the selected revision form = EditAnswerForm(answer, - AnswerRevision.objects.get(answer=answer, - revision=revision_form.cleaned_data['revision'])) + AnswerRevision.objects.get(answer=answer, + revision=revision_form.cleaned_data['revision'])) else: form = EditAnswerForm(answer, latest_revision, request.POST) else: @@ -584,14 +587,14 @@ def edit_answer(request, id): 'last_edited_by': request.user, 'html': html, } - Answer.objects.filter(id=answer.id).update(**updated_fields) + Answer.objects.filter(id=answer.id).update(** updated_fields) revision = AnswerRevision( - answer = answer, - author = request.user, - revised_at = edited_at, - text = form.cleaned_data['text'] - ) + answer=answer, + author=request.user, + revised_at=edited_at, + text=form.cleaned_data['text'] + ) if form.cleaned_data['summary']: revision.summary = form.cleaned_data['summary'] @@ -608,14 +611,14 @@ def edit_answer(request, id): revision_form = RevisionForm(answer, latest_revision) form = EditAnswerForm(answer, latest_revision) return render_to_response('answer_edit.html', { - 'answer': answer, - 'revision_form': revision_form, - 'form' : form, - }, context_instance=RequestContext(request)) + 'answer': answer, + 'revision_form': revision_form, + 'form': form, + }, context_instance=RequestContext(request)) QUESTION_REVISION_TEMPLATE = ('

    %(title)s

    \n' - '
    %(html)s
    \n' - '
    %(tags)s
    ') + '
    %(html)s
    \n' + '
    %(tags)s
    ') def question_revisions(request, id): post = get_object_or_404(Question, id=id) revisions = list(post.revisions.all()) @@ -624,7 +627,7 @@ def question_revisions(request, id): 'title': revision.title, 'html': sanitize_html(markdowner.convert(revision.text)), 'tags': ' '.join(['' % tag - for tag in revision.tagnames.split(' ')]), + for tag in revision.tagnames.split(' ')]), } if i > 0: revisions[i - 1].diff = htmldiff(revision.html, @@ -634,13 +637,13 @@ def question_revisions(request, id): 'title': revisions[0].title, 'html': sanitize_html(markdowner.convert(revisions[0].text)), 'tags': ' '.join(['' % tag - for tag in revisions[0].tagnames.split(' ')]), + for tag in revisions[0].tagnames.split(' ')]), } revisions[i - 1].summary = None return render_to_response('revisions_question.html', { - 'post': post, - 'revisions': revisions, - }, context_instance=RequestContext(request)) + 'post': post, + 'revisions': revisions, + }, context_instance=RequestContext(request)) ANSWER_REVISION_TEMPLATE = ('
    %(html)s
    ') def answer_revisions(request, id): @@ -657,9 +660,9 @@ def answer_revisions(request, id): revisions[i - 1].diff = revisions[i-1].text revisions[i - 1].summary = None return render_to_response('revisions_answer.html', { - 'post': post, - 'revisions': revisions, - }, context_instance=RequestContext(request)) + 'post': post, + 'revisions': revisions, + }, context_instance=RequestContext(request)) def answer(request, id): question = get_object_or_404(Question, id=id) @@ -672,25 +675,25 @@ def answer(request, id): if request.user.is_authenticated(): create_new_answer( - question=question, - author=request.user, - added_at=update_time, - wiki=wiki, - text=text, - email_notify=form.cleaned_data['email_notify'] - ) + question=question, + author=request.user, + added_at=update_time, + wiki=wiki, + text=text, + email_notify=form.cleaned_data['email_notify'] + ) else: request.session.flush() html = sanitize_html(markdowner.convert(text)) summary = strip_tags(html)[:120] anon = AnonymousAnswer( - question = question, - wiki = wiki, - text = text, - summary = summary, - session_key = request.session.session_key, - ip_addr = request.META['REMOTE_ADDR'], - ) + question=question, + wiki=wiki, + text=text, + summary=summary, + session_key=request.session.session_key, + ip_addr=request.META['REMOTE_ADDR'], + ) anon.save() return HttpResponseRedirect('/account/signin/newanswer') @@ -721,22 +724,22 @@ def tags(request): tags = objects_list.page(objects_list.num_pages) return render_to_response('tags.html', { - "tags" : tags, - "stag" : stag, - "tab_id" : sortby, - "keywords" : stag, - "context" : { - 'is_paginated' : is_paginated, - 'pages': objects_list.num_pages, - 'page': page, - 'has_previous': tags.has_previous(), - 'has_next': tags.has_next(), - 'previous': tags.previous_page_number(), - 'next': tags.next_page_number(), - 'base_url' : '/tags/?sort=%s&' % sortby - } - - }, context_instance=RequestContext(request)) + "tags": tags, + "stag": stag, + "tab_id": sortby, + "keywords": stag, + "context": { + 'is_paginated': is_paginated, + 'pages': objects_list.num_pages, + 'page': page, + 'has_previous': tags.has_previous(), + 'has_next': tags.has_next(), + 'previous': tags.previous_page_number(), + 'next': tags.next_page_number(), + 'base_url': '/tags/?sort=%s&' % sortby + } + + }, context_instance=RequestContext(request)) def tag(request, tag): return questions(request, tagname=tag) @@ -782,9 +785,9 @@ def vote(request, id): response_data = { "allowed": 1, "success": 1, - "status" : 0, - "count" : 0, - "message" : '' + "status": 0, + "count": 0, + "message": '' } def can_vote(vote_score, user): @@ -935,9 +938,9 @@ def vote(request, id): user = request.user if user.is_authenticated(): try: - EmailFeed.objects.get(feed_id=question.id,subscriber_id=user.id,feed_content_type=question_type) + EmailFeed.objects.get(feed_id=question.id, subscriber_id=user.id, feed_content_type=question_type) except EmailFeed.DoesNotExist: - feed = EmailFeed(subscriber=user,content=question) + feed = EmailFeed(subscriber=user, content=question) feed.save() if settings.EMAIL_VALIDATION == 'on' and user.email_isvalid == False: response_data['message'] = _('subscription saved, %(email)s needs validation') % {'email':user.email} @@ -951,7 +954,7 @@ def vote(request, id): user = request.user if user.is_authenticated(): try: - feed = EmailFeed.objects.get(feed_id=question.id,subscriber_id=user.id) + feed = EmailFeed.objects.get(feed_id=question.id, subscriber_id=user.id) feed.delete() except EmailFeed.DoesNotExist: pass @@ -970,7 +973,7 @@ def vote(request, id): def users(request): is_paginated = True sortby = request.GET.get('sort', 'reputation') - suser = request.REQUEST.get('q', "") + suser = request.REQUEST.get('q', "") try: page = int(request.GET.get('page', '1')) except ValueError: @@ -998,22 +1001,22 @@ def users(request): users = objects_list.page(objects_list.num_pages) return render_to_response('users.html', { - "users" : users, - "suser" : suser, - "keywords" : suser, - "tab_id" : sortby, - "context" : { - 'is_paginated' : is_paginated, - 'pages': objects_list.num_pages, - 'page': page, - 'has_previous': users.has_previous(), - 'has_next': users.has_next(), - 'previous': users.previous_page_number(), - 'next': users.next_page_number(), - 'base_url' : base_url - } - - }, context_instance=RequestContext(request)) + "users": users, + "suser": suser, + "keywords": suser, + "tab_id": sortby, + "context": { + 'is_paginated': is_paginated, + 'pages': objects_list.num_pages, + 'page': page, + 'has_previous': users.has_previous(), + 'has_next': users.has_next(), + 'previous': users.previous_page_number(), + 'next': users.next_page_number(), + 'base_url': base_url + } + + }, context_instance=RequestContext(request)) def user(request, id): sort = request.GET.get('sort', 'stats') @@ -1052,117 +1055,118 @@ def edit_user(request, id): else: form = EditUserForm(user) return render_to_response('user_edit.html', { - 'form' : form, - }, context_instance=RequestContext(request)) + 'form': form, + }, context_instance=RequestContext(request)) def user_stats(request, user_id, user_view): user = get_object_or_404(User, id=user_id) questions = Question.objects.extra( - select={ - 'vote_count' : 'question.score', - 'favorited_myself' : 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s AND f.question_id = question.id', - 'la_user_id' : 'auth_user.id', - 'la_username' : 'auth_user.username', - 'la_user_gold' : 'auth_user.gold', - 'la_user_silver' : 'auth_user.silver', - 'la_user_bronze' : 'auth_user.bronze', - 'la_user_reputation' : 'auth_user.reputation' - }, - select_params=[user_id], - tables=['question', 'auth_user'], - where=['question.deleted = 0 AND question.author_id=%s AND question.last_activity_by_id = auth_user.id'], - params=[user_id], - order_by=['-vote_count', '-last_activity_at'] - ).values('vote_count', - 'favorited_myself', - 'id', - 'title', - 'author_id', - 'added_at', - 'answer_accepted', - 'answer_count', - 'comment_count', - 'view_count', - 'favourite_count', - 'summary', - 'tagnames', - 'vote_up_count', - 'vote_down_count', - 'last_activity_at', - 'la_user_id', - 'la_username', - 'la_user_gold', - 'la_user_silver', - 'la_user_bronze', - 'la_user_reputation')[:100] + select={ + 'vote_count': 'question.score', + 'favorited_myself': 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s AND f.question_id = question.id', + 'la_user_id': 'auth_user.id', + 'la_username': 'auth_user.username', + 'la_user_gold': 'auth_user.gold', + 'la_user_silver': 'auth_user.silver', + 'la_user_bronze': 'auth_user.bronze', + 'la_user_reputation': 'auth_user.reputation' + }, + select_params=[user_id], + tables=['question', 'auth_user'], + where=['question.deleted = 0 AND question.author_id=%s AND question.last_activity_by_id = auth_user.id'], + params=[user_id], + order_by=['-vote_count', '-last_activity_at'] + ).values('vote_count', + 'favorited_myself', + 'id', + 'title', + 'author_id', + 'added_at', + 'answer_accepted', + 'answer_count', + 'comment_count', + 'view_count', + 'favourite_count', + 'summary', + 'tagnames', + 'vote_up_count', + 'vote_down_count', + 'last_activity_at', + 'la_user_id', + 'la_username', + 'la_user_gold', + 'la_user_silver', + 'la_user_bronze', + 'la_user_reputation')[:100] answered_questions = Question.objects.extra( - select={ - 'vote_up_count' : 'answer.vote_up_count', - 'vote_down_count' : 'answer.vote_down_count', - 'answer_id' : 'answer.id', - 'accepted' : 'answer.accepted', - 'vote_count' : 'answer.score', - 'comment_count' : 'answer.comment_count' - }, - tables=['question', 'answer'], - where=['answer.deleted=0 AND answer.author_id=%s AND answer.question_id=question.id'], - params=[user_id], - order_by=['-vote_count', '-answer_id'], - select_params=[user_id] - ).distinct().values('comment_count', - 'id', - 'answer_id', - 'title', - 'author_id', - 'accepted', - 'vote_count', - 'answer_count', - 'vote_up_count', - 'vote_down_count')[:100] + select={ + 'vote_up_count': 'answer.vote_up_count', + 'vote_down_count': 'answer.vote_down_count', + 'answer_id': 'answer.id', + 'accepted': 'answer.accepted', + 'vote_count': 'answer.score', + 'comment_count': 'answer.comment_count' + }, + tables=['question', 'answer'], + where=['answer.deleted=0 AND answer.author_id=%s AND answer.question_id=question.id'], + params=[user_id], + order_by=['-vote_count', '-answer_id'], + select_params=[user_id] + ).distinct().values('comment_count', + 'id', + 'answer_id', + 'title', + 'author_id', + 'accepted', + 'vote_count', + 'answer_count', + 'vote_up_count', + 'vote_down_count')[:100] up_votes = Vote.objects.get_up_vote_count_from_user(user) down_votes = Vote.objects.get_down_vote_count_from_user(user) votes_today = Vote.objects.get_votes_count_today_from_user(user) votes_total = VOTE_RULES['scope_votes_per_user_per_day'] tags = user.created_tags.all().order_by('-used_count')[:50] - if settings.DJANGO_VERSION < 1.1: + try: + from django.db.models import Count awards = Award.objects.extra( - select={'id': 'badge.id', 'count': 'count(badge_id)', 'name':'badge.name', 'description': 'badge.description', 'type': 'badge.type'}, - tables=['award', 'badge'], - order_by=['-awarded_at'], - where=['user_id=%s AND badge_id=badge.id'], - params=[user.id] - ).values('id', 'count', 'name', 'description', 'type') + select={'id': 'badge.id', 'name':'badge.name', 'description': 'badge.description', 'type': 'badge.type'}, + tables=['award', 'badge'], + order_by=['-awarded_at'], + where=['user_id=%s AND badge_id=badge.id'], + params=[user.id] + ).values('id', 'name', 'description', 'type') total_awards = awards.count() - awards.query.group_by = ['badge_id'] - else: + awards = awards.annotate(count=Count('badge__id')) + except ImportError: awards = Award.objects.extra( - select={'id': 'badge.id', 'name':'badge.name', 'description': 'badge.description', 'type': 'badge.type'}, - tables=['award', 'badge'], - order_by=['-awarded_at'], - where=['user_id=%s AND badge_id=badge.id'], - params=[user.id] - ).values('id', 'name', 'description', 'type') + select={'id': 'badge.id', 'count': 'count(badge_id)', 'name':'badge.name', 'description': 'badge.description', 'type': 'badge.type'}, + tables=['award', 'badge'], + order_by=['-awarded_at'], + where=['user_id=%s AND badge_id=badge.id'], + params=[user.id] + ).values('id', 'count', 'name', 'description', 'type') total_awards = awards.count() - from django.db.models import Count - awards = awards.annotate(count = Count('badge__id')) - - return render_to_response(user_view.template_file,{ - "tab_name" : user_view.id, - "tab_description" : user_view.tab_description, - "page_title" : user_view.page_title, - "view_user" : user, - "questions" : questions, - "answered_questions" : answered_questions, - "up_votes" : up_votes, - "down_votes" : down_votes, - "total_votes": up_votes + down_votes, - "votes_today_left": votes_total-votes_today, - "votes_total_per_day": votes_total, - "tags" : tags, - "awards": awards, - "total_awards" : total_awards, - }, context_instance=RequestContext(request)) + awards.query.group_by = ['badge_id'] + + + return render_to_response(user_view.template_file, { + "tab_name": user_view.id, + "tab_description": user_view.tab_description, + "page_title": user_view.page_title, + "view_user": user, + "questions": questions, + "answered_questions": answered_questions, + "up_votes": up_votes, + "down_votes": down_votes, + "total_votes": up_votes + down_votes, + "votes_today_left": votes_total-votes_today, + "votes_total_per_day": votes_total, + "tags": tags, + "awards": awards, + "total_awards": total_awards, + }, context_instance=RequestContext(request)) def user_recent(request, user_id, user_view): user = get_object_or_404(User, id=user_id) @@ -1178,8 +1182,8 @@ def user_recent(request, user_id, user_view): self.type_id = type self.title = title self.summary = summary - self.title_link = u'/questions/%s/%s#%s' %(question_id, title, answer_id)\ - if int(answer_id) > 0 else u'/questions/%s/%s' %(question_id, title) + self.title_link = u'/questions/%s/%s#%s' % (question_id, title, answer_id)\ + if int(answer_id) > 0 else u'/questions/%s/%s' % (question_id, title) class AwardEvent: def __init__(self, time, type, id): self.time = time @@ -1190,225 +1194,225 @@ def user_recent(request, user_id, user_view): activities = [] # ask questions questions = Activity.objects.extra( - select={ - 'title' : 'question.title', - 'question_id' : 'question.id', - 'active_at' : 'activity.active_at', - 'activity_type' : 'activity.activity_type' - }, - tables=['activity', 'question'], - where=['activity.content_type_id = %s AND activity.object_id = ' + - 'question.id AND activity.user_id = %s AND activity.activity_type = %s'], - params=[question_type_id, user_id, TYPE_ACTIVITY_ASK_QUESTION], - order_by=['-activity.active_at'] - ).values( - 'title', - 'question_id', - 'active_at', - 'activity_type' - ) + select={ + 'title': 'question.title', + 'question_id': 'question.id', + 'active_at': 'activity.active_at', + 'activity_type': 'activity.activity_type' + }, + tables=['activity', 'question'], + where=['activity.content_type_id = %s AND activity.object_id = ' + + 'question.id AND activity.user_id = %s AND activity.activity_type = %s'], + params=[question_type_id, user_id, TYPE_ACTIVITY_ASK_QUESTION], + order_by=['-activity.active_at'] + ).values( + 'title', + 'question_id', + 'active_at', + 'activity_type' + ) if len(questions) > 0: questions = [(Event(q['active_at'], q['activity_type'], q['title'], '', '0', \ - q['question_id'])) for q in questions] + q['question_id'])) for q in questions] activities.extend(questions) # answers answers = Activity.objects.extra( - select={ - 'title' : 'question.title', - 'question_id' : 'question.id', - 'answer_id' : 'answer.id', - 'active_at' : 'activity.active_at', - 'activity_type' : 'activity.activity_type' - }, - tables=['activity', 'answer', 'question'], - where=['activity.content_type_id = %s AND activity.object_id = answer.id AND ' + - 'answer.question_id=question.id AND activity.user_id=%s AND activity.activity_type=%s'], - params=[answer_type_id, user_id, TYPE_ACTIVITY_ANSWER], - order_by=['-activity.active_at'] - ).values( - 'title', - 'question_id', - 'answer_id', - 'active_at', - 'activity_type' - ) + select={ + 'title': 'question.title', + 'question_id': 'question.id', + 'answer_id': 'answer.id', + 'active_at': 'activity.active_at', + 'activity_type': 'activity.activity_type' + }, + tables=['activity', 'answer', 'question'], + where=['activity.content_type_id = %s AND activity.object_id = answer.id AND ' + + 'answer.question_id=question.id AND activity.user_id=%s AND activity.activity_type=%s'], + params=[answer_type_id, user_id, TYPE_ACTIVITY_ANSWER], + order_by=['-activity.active_at'] + ).values( + 'title', + 'question_id', + 'answer_id', + 'active_at', + 'activity_type' + ) if len(answers) > 0: answers = [(Event(q['active_at'], q['activity_type'], q['title'], '', q['answer_id'], \ - q['question_id'])) for q in answers] + q['question_id'])) for q in answers] activities.extend(answers) # question comments comments = Activity.objects.extra( - select={ - 'title' : 'question.title', - 'question_id' : 'comment.object_id', - 'added_at' : 'comment.added_at', - 'activity_type' : 'activity.activity_type' - }, - tables=['activity', 'question', 'comment'], - - 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 '+ - 'comment.content_type_id=%s AND activity.user_id = %s AND activity.activity_type=%s'], - params=[comment_type_id, question_type_id, user_id, TYPE_ACTIVITY_COMMENT_QUESTION], - order_by=['-comment.added_at'] - ).values( - 'title', - 'question_id', - 'added_at', - 'activity_type' - ) + select={ + 'title': 'question.title', + 'question_id': 'comment.object_id', + 'added_at': 'comment.added_at', + 'activity_type': 'activity.activity_type' + }, + tables=['activity', 'question', 'comment'], + + 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 ' + + 'comment.content_type_id=%s AND activity.user_id = %s AND activity.activity_type=%s'], + params=[comment_type_id, question_type_id, user_id, 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] + q['question_id'])) for q in comments] activities.extend(comments) # answer comments comments = Activity.objects.extra( - select={ - 'title' : 'question.title', - 'question_id' : 'question.id', - 'answer_id' : 'answer.id', - 'added_at' : 'comment.added_at', - 'activity_type' : 'activity.activity_type' - }, - tables=['activity', 'question', 'answer', 'comment'], - - 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 '+ - 'activity.user_id = %s AND activity.activity_type=%s'], - params=[comment_type_id, answer_type_id, user_id, TYPE_ACTIVITY_COMMENT_ANSWER], - order_by=['-comment.added_at'] - ).values( - 'title', - 'question_id', - 'answer_id', - 'added_at', - 'activity_type' - ) + select={ + 'title': 'question.title', + 'question_id': 'question.id', + 'answer_id': 'answer.id', + 'added_at': 'comment.added_at', + 'activity_type': 'activity.activity_type' + }, + tables=['activity', 'question', 'answer', 'comment'], + + 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 ' + + 'activity.user_id = %s AND activity.activity_type=%s'], + params=[comment_type_id, answer_type_id, user_id, 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] + q['question_id'])) for q in comments] activities.extend(comments) # question revisions revisions = Activity.objects.extra( - select={ - 'title' : 'question_revision.title', - 'question_id' : 'question_revision.question_id', - 'added_at' : 'activity.active_at', - 'activity_type' : 'activity.activity_type', - 'summary' : 'question_revision.summary' - }, - tables=['activity', 'question_revision'], - where=['activity.content_type_id = %s AND activity.object_id = question_revision.id AND '+ - 'activity.user_id = question_revision.author_id AND activity.user_id = %s AND '+ - 'activity.activity_type=%s'], - params=[question_revision_type_id, user_id, TYPE_ACTIVITY_UPDATE_QUESTION], - order_by=['-activity.active_at'] - ).values( - 'title', - 'question_id', - 'added_at', - 'activity_type', - 'summary' - ) + select={ + 'title': 'question_revision.title', + 'question_id': 'question_revision.question_id', + 'added_at': 'activity.active_at', + 'activity_type': 'activity.activity_type', + 'summary': 'question_revision.summary' + }, + tables=['activity', 'question_revision'], + where=['activity.content_type_id = %s AND activity.object_id = question_revision.id AND ' + + 'activity.user_id = question_revision.author_id AND activity.user_id = %s AND ' + + 'activity.activity_type=%s'], + params=[question_revision_type_id, user_id, 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] + q['question_id'])) for q in revisions] activities.extend(revisions) # answer revisions revisions = Activity.objects.extra( - select={ - 'title' : 'question.title', - 'question_id' : 'question.id', - 'answer_id' : 'answer.id', - 'added_at' : 'activity.active_at', - 'activity_type' : 'activity.activity_type', - 'summary' : 'answer_revision.summary' - }, - tables=['activity', 'answer_revision', 'question', 'answer'], - - where=['activity.content_type_id = %s AND activity.object_id = answer_revision.id AND '+ - 'activity.user_id = answer_revision.author_id AND activity.user_id = %s AND '+ - 'answer_revision.answer_id=answer.id AND answer.question_id = question.id AND '+ - 'activity.activity_type=%s'], - params=[answer_revision_type_id, user_id, TYPE_ACTIVITY_UPDATE_ANSWER], - order_by=['-activity.active_at'] - ).values( - 'title', - 'question_id', - 'added_at', - 'answer_id', - 'activity_type', - 'summary' - ) + select={ + 'title': 'question.title', + 'question_id': 'question.id', + 'answer_id': 'answer.id', + 'added_at': 'activity.active_at', + 'activity_type': 'activity.activity_type', + 'summary': 'answer_revision.summary' + }, + tables=['activity', 'answer_revision', 'question', 'answer'], + + where=['activity.content_type_id = %s AND activity.object_id = answer_revision.id AND ' + + 'activity.user_id = answer_revision.author_id AND activity.user_id = %s AND ' + + 'answer_revision.answer_id=answer.id AND answer.question_id = question.id AND ' + + 'activity.activity_type=%s'], + params=[answer_revision_type_id, user_id, 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] + q['answer_id'], q['question_id'])) for q in revisions] activities.extend(revisions) # accepted answers accept_answers = Activity.objects.extra( - select={ - 'title' : 'question.title', - 'question_id' : 'question.id', - 'added_at' : 'activity.active_at', - 'activity_type' : 'activity.activity_type', - }, - tables=['activity', 'answer', 'question'], - 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 '+ - 'answer.question_id=question.id AND activity.activity_type=%s'], - params=[answer_type_id, user_id, TYPE_ACTIVITY_MARK_ANSWER], - order_by=['-activity.active_at'] - ).values( - 'title', - 'question_id', - 'added_at', - 'activity_type', - ) + select={ + 'title': 'question.title', + 'question_id': 'question.id', + 'added_at': 'activity.active_at', + 'activity_type': 'activity.activity_type', + }, + tables=['activity', 'answer', 'question'], + 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 ' + + 'answer.question_id=question.id AND activity.activity_type=%s'], + params=[answer_type_id, user_id, 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] + q['question_id'])) for q in accept_answers] activities.extend(accept_answers) #award history awards = Activity.objects.extra( - select={ - 'badge_id' : 'badge.id', - 'awarded_at': 'award.awarded_at', - 'activity_type' : 'activity.activity_type' - }, - tables=['activity', 'award', 'badge'], - where=['activity.user_id = award.user_id AND activity.user_id = %s AND '+ - 'award.badge_id=badge.id AND activity.object_id=award.id AND activity.activity_type=%s'], - params=[user_id, TYPE_ACTIVITY_PRIZE], - order_by=['-activity.active_at'] - ).values( - 'badge_id', - 'awarded_at', - 'activity_type' - ) + select={ + 'badge_id': 'badge.id', + 'awarded_at': 'award.awarded_at', + 'activity_type': 'activity.activity_type' + }, + tables=['activity', 'award', 'badge'], + where=['activity.user_id = award.user_id AND activity.user_id = %s AND ' + + 'award.badge_id=badge.id AND activity.object_id=award.id AND activity.activity_type=%s'], + params=[user_id, TYPE_ACTIVITY_PRIZE], + order_by=['-activity.active_at'] + ).values( + 'badge_id', + 'awarded_at', + 'activity_type' + ) if len(awards) > 0: awards = [(AwardEvent(q['awarded_at'], q['activity_type'], q['badge_id'])) for q in awards] activities.extend(awards) - activities.sort(lambda x,y: cmp(y.time, x.time)) + activities.sort(lambda x, y: cmp(y.time, x.time)) - return render_to_response(user_view.template_file,{ - "tab_name" : user_view.id, - "tab_description" : user_view.tab_description, - "page_title" : user_view.page_title, - "view_user" : user, - "activities" : activities[:user_view.data_size] - }, context_instance=RequestContext(request)) + return render_to_response(user_view.template_file, { + "tab_name": user_view.id, + "tab_description": user_view.tab_description, + "page_title": user_view.page_title, + "view_user": user, + "activities": activities[:user_view.data_size] + }, context_instance=RequestContext(request)) def user_responses(request, user_id, user_view): """ @@ -1430,139 +1434,139 @@ def user_responses(request, user_id, user_view): user = get_object_or_404(User, id=user_id) responses = [] answers = Answer.objects.extra( - select={ - 'title' : 'question.title', - 'question_id' : 'question.id', - 'answer_id' : 'answer.id', - 'added_at' : 'answer.added_at', - 'html' : 'answer.html', - 'username' : 'auth_user.username', - 'user_id' : 'auth_user.id' - }, - select_params=[user_id], - tables=['answer', 'question', 'auth_user'], - where=['answer.question_id = question.id AND answer.deleted=0 AND question.deleted = 0 AND '+ - 'question.author_id = %s AND answer.author_id <> %s AND answer.author_id=auth_user.id'], - params=[user_id, user_id], - order_by=['-answer.id'] - ).values( - 'title', - 'question_id', - 'answer_id', - 'added_at', - 'html', - 'username', - 'user_id' - ) + select={ + 'title': 'question.title', + 'question_id': 'question.id', + 'answer_id': 'answer.id', + 'added_at': 'answer.added_at', + 'html': 'answer.html', + 'username': 'auth_user.username', + 'user_id': 'auth_user.id' + }, + select_params=[user_id], + tables=['answer', 'question', 'auth_user'], + where=['answer.question_id = question.id AND answer.deleted=0 AND question.deleted = 0 AND ' + + 'question.author_id = %s AND answer.author_id <> %s AND answer.author_id=auth_user.id'], + params=[user_id, user_id], + order_by=['-answer.id'] + ).values( + 'title', + 'question_id', + 'answer_id', + 'added_at', + 'html', + 'username', + 'user_id' + ) if len(answers) > 0: answers = [(Response(TYPE_RESPONSE['QUESTION_ANSWERED'], a['title'], a['question_id'], - a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers] + a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers] responses.extend(answers) # question comments comments = Comment.objects.extra( - select={ - 'title' : 'question.title', - 'question_id' : 'comment.object_id', - 'added_at' : 'comment.added_at', - 'comment' : 'comment.comment', - 'username' : 'auth_user.username', - 'user_id' : 'auth_user.id' - }, - tables=['question', 'auth_user', 'comment'], - where=['question.deleted = 0 AND question.author_id = %s AND comment.object_id=question.id AND '+ - 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id'], - params=[user_id, question_type_id, user_id], - order_by=['-comment.added_at'] - ).values( - 'title', - 'question_id', - 'added_at', - 'comment', - 'username', - 'user_id' - ) + select={ + 'title': 'question.title', + 'question_id': 'comment.object_id', + 'added_at': 'comment.added_at', + 'comment': 'comment.comment', + 'username': 'auth_user.username', + 'user_id': 'auth_user.id' + }, + tables=['question', 'auth_user', 'comment'], + where=['question.deleted = 0 AND question.author_id = %s AND comment.object_id=question.id AND ' + + 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id'], + params=[user_id, question_type_id, user_id], + order_by=['-comment.added_at'] + ).values( + 'title', + 'question_id', + 'added_at', + 'comment', + 'username', + 'user_id' + ) if len(comments) > 0: comments = [(Response(TYPE_RESPONSE['QUESTION_COMMENTED'], c['title'], c['question_id'], - '', c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments] + '', c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments] responses.extend(comments) # answer comments comments = Comment.objects.extra( - select={ - 'title' : 'question.title', - 'question_id' : 'question.id', - 'answer_id' : 'answer.id', - 'added_at' : 'comment.added_at', - 'comment' : 'comment.comment', - 'username' : 'auth_user.username', - 'user_id' : 'auth_user.id' - }, - tables=['answer', 'auth_user', 'comment', 'question'], - where=['answer.deleted = 0 AND answer.author_id = %s AND comment.object_id=answer.id AND '+ - 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id '+ - 'AND question.id = answer.question_id'], - params=[user_id, answer_type_id, user_id], - order_by=['-comment.added_at'] - ).values( - 'title', - 'question_id', - 'answer_id', - 'added_at', - 'comment', - 'username', - 'user_id' - ) + select={ + 'title': 'question.title', + 'question_id': 'question.id', + 'answer_id': 'answer.id', + 'added_at': 'comment.added_at', + 'comment': 'comment.comment', + 'username': 'auth_user.username', + 'user_id': 'auth_user.id' + }, + tables=['answer', 'auth_user', 'comment', 'question'], + where=['answer.deleted = 0 AND answer.author_id = %s AND comment.object_id=answer.id AND ' + + 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id ' + + 'AND question.id = answer.question_id'], + params=[user_id, answer_type_id, user_id], + order_by=['-comment.added_at'] + ).values( + 'title', + 'question_id', + 'answer_id', + 'added_at', + 'comment', + 'username', + 'user_id' + ) if len(comments) > 0: comments = [(Response(TYPE_RESPONSE['ANSWER_COMMENTED'], c['title'], c['question_id'], - c['answer_id'], c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments] + c['answer_id'], c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments] responses.extend(comments) # answer has been accepted answers = Answer.objects.extra( - select={ - 'title' : 'question.title', - 'question_id' : 'question.id', - 'answer_id' : 'answer.id', - 'added_at' : 'answer.accepted_at', - 'html' : 'answer.html', - 'username' : 'auth_user.username', - 'user_id' : 'auth_user.id' - }, - select_params=[user_id], - tables=['answer', 'question', 'auth_user'], - where=['answer.question_id = question.id AND answer.deleted=0 AND question.deleted = 0 AND '+ - 'answer.author_id = %s AND answer.accepted=1 AND question.author_id=auth_user.id'], - params=[user_id], - order_by=['-answer.id'] - ).values( - 'title', - 'question_id', - 'answer_id', - 'added_at', - 'html', - 'username', - 'user_id' - ) + select={ + 'title': 'question.title', + 'question_id': 'question.id', + 'answer_id': 'answer.id', + 'added_at': 'answer.accepted_at', + 'html': 'answer.html', + 'username': 'auth_user.username', + 'user_id': 'auth_user.id' + }, + select_params=[user_id], + tables=['answer', 'question', 'auth_user'], + where=['answer.question_id = question.id AND answer.deleted=0 AND question.deleted = 0 AND ' + + 'answer.author_id = %s AND answer.accepted=1 AND question.author_id=auth_user.id'], + params=[user_id], + order_by=['-answer.id'] + ).values( + 'title', + 'question_id', + 'answer_id', + 'added_at', + 'html', + 'username', + 'user_id' + ) if len(answers) > 0: answers = [(Response(TYPE_RESPONSE['ANSWER_ACCEPTED'], a['title'], a['question_id'], - a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers] + a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers] responses.extend(answers) # sort posts by time - responses.sort(lambda x,y: cmp(y.time, x.time)) + responses.sort(lambda x, y: cmp(y.time, x.time)) - return render_to_response(user_view.template_file,{ - "tab_name" : user_view.id, - "tab_description" : user_view.tab_description, - "page_title" : user_view.page_title, - "view_user" : user, - "responses" : responses[:user_view.data_size], + return render_to_response(user_view.template_file, { + "tab_name": user_view.id, + "tab_description": user_view.tab_description, + "page_title": user_view.page_title, + "view_user": user, + "responses": responses[:user_view.data_size], - }, context_instance=RequestContext(request)) + }, context_instance=RequestContext(request)) def user_votes(request, user_id, user_view): user = get_object_or_404(User, id=user_id) @@ -1570,74 +1574,85 @@ def user_votes(request, user_id, user_view): raise Http404 votes = [] question_votes = Vote.objects.extra( - select={ - 'title' : 'question.title', - 'question_id' : 'question.id', - 'answer_id' : 0, - 'voted_at' : 'vote.voted_at', - 'vote' : 'vote', - }, - select_params=[user_id], - tables=['vote', 'question', 'auth_user'], - where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = question.id '+ - 'AND vote.user_id=auth_user.id'], - params=[question_type_id, user_id], - order_by=['-vote.id'] - ).values( - 'title', - 'question_id', - 'answer_id', - 'voted_at', - 'vote', - ) + select={ + 'title': 'question.title', + 'question_id': 'question.id', + 'answer_id': 0, + 'voted_at': 'vote.voted_at', + 'vote': 'vote', + }, + select_params=[user_id], + tables=['vote', 'question', 'auth_user'], + where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = question.id ' + + 'AND vote.user_id=auth_user.id'], + params=[question_type_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 = Vote.objects.extra( - select={ - 'title' : 'question.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'], - where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = answer.id '+ - 'AND answer.question_id = question.id AND vote.user_id=auth_user.id'], - params=[answer_type_id, user_id], - order_by=['-vote.id'] - ).values( - 'title', - 'question_id', - 'answer_id', - 'voted_at', - 'vote', - ) + select={ + 'title': 'question.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'], + where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = answer.id ' + + 'AND answer.question_id = question.id AND vote.user_id=auth_user.id'], + params=[answer_type_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'])) - return render_to_response(user_view.template_file,{ - "tab_name" : user_view.id, - "tab_description" : user_view.tab_description, - "page_title" : user_view.page_title, - "view_user" : user, - "votes" : votes[:user_view.data_size] + votes.sort(lambda x, y: cmp(y['voted_at'], x['voted_at'])) + return render_to_response(user_view.template_file, { + "tab_name": user_view.id, + "tab_description": user_view.tab_description, + "page_title": user_view.page_title, + "view_user": user, + "votes": votes[:user_view.data_size] - }, context_instance=RequestContext(request)) + }, context_instance=RequestContext(request)) def user_reputation(request, user_id, user_view): user = get_object_or_404(User, id=user_id) - reputation = Repute.objects.extra( - select={'positive': 'sum(positive)', 'negative': 'sum(negative)', 'question_id':'question_id', - 'title': 'question.title'}, - tables=['repute', 'question'], - order_by=['-reputed_at'], - where=['user_id=%s AND question_id=question.id'], - params=[user.id] - ).values('positive', 'negative', 'question_id', 'title', 'reputed_at', 'reputation') - - reputation.query.group_by = ['question_id'] + try: + from django.db.models import Sum + reputation = Repute.objects.extra( + select={'question_id':'question_id', + 'title': 'question.title'}, + tables=['repute', 'question'], + order_by=['-reputed_at'], + where=['user_id=%s AND question_id=question.id'], + params=[user.id] + ).values('question_id', 'title', 'reputed_at', 'reputation') + reputation = reputation.annotate(positive=Sum("positive"), negative=Sum("negative")) + except ImportError: + reputation = Repute.objects.extra( + select={'positive':'sum(positive)', 'negative':'sum(negative)', 'question_id':'question_id', + 'title': 'question.title'}, + tables=['repute', 'question'], + order_by=['-reputed_at'], + where=['user_id=%s AND question_id=question.id'], + params=[user.id] + ).values('positive', 'negative', 'question_id', 'title', 'reputed_at', 'reputation') + reputation.query.group_by = ['question_id'] rep_list = [] for rep in Repute.objects.filter(user=user).order_by('reputed_at'): @@ -1646,74 +1661,74 @@ def user_reputation(request, user_id, user_view): reps = ','.join(rep_list) reps = '[%s]' % reps - return render_to_response(user_view.template_file,{ - "tab_name" : user_view.id, - "tab_description" : user_view.tab_description, - "page_title" : user_view.page_title, - "view_user" : user, - "reputation" : reputation, - "reps" : reps - }, context_instance=RequestContext(request)) + return render_to_response(user_view.template_file, { + "tab_name": user_view.id, + "tab_description": user_view.tab_description, + "page_title": user_view.page_title, + "view_user": user, + "reputation": reputation, + "reps": reps + }, context_instance=RequestContext(request)) def user_favorites(request, user_id, user_view): user = get_object_or_404(User, id=user_id) questions = Question.objects.extra( - select={ - 'vote_count' : 'question.vote_up_count + question.vote_down_count', - 'favorited_myself' : 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s '+ - 'AND f.question_id = question.id', - 'la_user_id' : 'auth_user.id', - 'la_username' : 'auth_user.username', - 'la_user_gold' : 'auth_user.gold', - 'la_user_silver' : 'auth_user.silver', - 'la_user_bronze' : 'auth_user.bronze', - 'la_user_reputation' : 'auth_user.reputation' - }, - select_params=[user_id], - tables=['question', 'auth_user', 'favorite_question'], - where=['question.deleted = 0 AND question.last_activity_by_id = auth_user.id '+ - 'AND favorite_question.question_id = question.id AND favorite_question.user_id = %s'], - params=[user_id], - order_by=['-vote_count', '-question.id'] - ).values('vote_count', - 'favorited_myself', - 'id', - 'title', - 'author_id', - 'added_at', - 'answer_accepted', - 'answer_count', - 'comment_count', - 'view_count', - 'favourite_count', - 'summary', - 'tagnames', - 'vote_up_count', - 'vote_down_count', - 'last_activity_at', - 'la_user_id', - 'la_username', - 'la_user_gold', - 'la_user_silver', - 'la_user_bronze', - 'la_user_reputation') - return render_to_response(user_view.template_file,{ - "tab_name" : user_view.id, - "tab_description" : user_view.tab_description, - "page_title" : user_view.page_title, - "questions" : questions[:user_view.data_size], - "view_user" : user - }, context_instance=RequestContext(request)) + select={ + 'vote_count': 'question.vote_up_count + question.vote_down_count', + 'favorited_myself': 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s ' + + 'AND f.question_id = question.id', + 'la_user_id': 'auth_user.id', + 'la_username': 'auth_user.username', + 'la_user_gold': 'auth_user.gold', + 'la_user_silver': 'auth_user.silver', + 'la_user_bronze': 'auth_user.bronze', + 'la_user_reputation': 'auth_user.reputation' + }, + select_params=[user_id], + tables=['question', 'auth_user', 'favorite_question'], + where=['question.deleted = 0 AND question.last_activity_by_id = auth_user.id ' + + 'AND favorite_question.question_id = question.id AND favorite_question.user_id = %s'], + params=[user_id], + order_by=['-vote_count', '-question.id'] + ).values('vote_count', + 'favorited_myself', + 'id', + 'title', + 'author_id', + 'added_at', + 'answer_accepted', + 'answer_count', + 'comment_count', + 'view_count', + 'favourite_count', + 'summary', + 'tagnames', + 'vote_up_count', + 'vote_down_count', + 'last_activity_at', + 'la_user_id', + 'la_username', + 'la_user_gold', + 'la_user_silver', + 'la_user_bronze', + 'la_user_reputation') + return render_to_response(user_view.template_file, { + "tab_name": user_view.id, + "tab_description": user_view.tab_description, + "page_title": user_view.page_title, + "questions": questions[:user_view.data_size], + "view_user": user + }, context_instance=RequestContext(request)) def user_preferences(request, user_id, user_view): user = get_object_or_404(User, id=user_id) - return render_to_response(user_view.template_file,{ - "tab_name" : user_view.id, - "tab_description" : user_view.tab_description, - "page_title" : user_view.page_title, - "view_user" : user, - }, context_instance=RequestContext(request)) + return render_to_response(user_view.template_file, { + "tab_name": user_view.id, + "tab_description": user_view.tab_description, + "page_title": user_view.page_title, + "view_user": user, + }, context_instance=RequestContext(request)) def question_comments(request, id): question = get_object_or_404(Question, id=id) @@ -1748,14 +1763,14 @@ def __generate_comments_json(obj, type, user): if user != None and auth.can_delete_comment(user, comment): #/posts/392845/comments/219852/delete delete_url = "/" + type + "s/%s/comments/%s/delete/" % (obj.id, comment.id) - json_comments.append({"id" : comment.id, - "object_id" : obj.id, - "add_date" : comment.added_at.strftime('%Y-%m-%d'), - "text" : comment.comment, - "user_display_name" : comment_user.username, - "user_url" : "/users/%s/%s" % (comment_user.id, comment_user.username), - "delete_url" : delete_url - }) + json_comments.append({"id": comment.id, + "object_id": obj.id, + "add_date": comment.added_at.strftime('%Y-%m-%d'), + "text": comment.comment, + "user_display_name": comment_user.username, + "user_url": "/users/%s/%s" % (comment_user.id, comment_user.username), + "delete_url": delete_url + }) data = simplejson.dumps(json_comments) return HttpResponse(data, mimetype="application/json") @@ -1785,8 +1800,8 @@ def delete_answer_comment(request, answer_id, comment_id): def logout(request): url = request.GET.get('next') return render_to_response('logout.html', { - 'next' : url, - }, context_instance=RequestContext(request)) + 'next': url, + }, context_instance=RequestContext(request)) def badges(request): badges = Badge.objects.all().order_by('type') @@ -1796,28 +1811,28 @@ def badges(request): my_badges.query.group_by = ['badge_id'] return render_to_response('badges.html', { - 'badges' : badges, - 'mybadges' : my_badges, - }, context_instance=RequestContext(request)) + 'badges': badges, + 'mybadges': my_badges, + }, context_instance=RequestContext(request)) def badge(request, id): badge = get_object_or_404(Badge, id=id) awards = Award.objects.extra( - select={'id': 'auth_user.id', - 'name': 'auth_user.username', - 'rep':'auth_user.reputation', - 'gold': 'auth_user.gold', - 'silver': 'auth_user.silver', - 'bronze': 'auth_user.bronze'}, - tables=['award', 'auth_user'], - where=['badge_id=%s AND user_id=auth_user.id'], - params=[id] - ).values('id').distinct() + select={'id': 'auth_user.id', + 'name': 'auth_user.username', + 'rep':'auth_user.reputation', + 'gold': 'auth_user.gold', + 'silver': 'auth_user.silver', + 'bronze': 'auth_user.bronze'}, + tables=['award', 'auth_user'], + where=['badge_id=%s AND user_id=auth_user.id'], + params=[id] + ).values('id').distinct() return render_to_response('badge.html', { - 'awards' : awards, - 'badge' : badge, - }, context_instance=RequestContext(request)) + 'awards': awards, + 'badge': badge, + }, context_instance=RequestContext(request)) def read_message(request): if request.method == "POST": @@ -1850,7 +1865,7 @@ def upload(request): raise FileTypeNotAllow # genetate new file name - new_file_name = str(time.time()).replace('.', str(random.randint(0,100000))) + file_name_suffix + new_file_name = str(time.time()).replace('.', str(random.randint(0, 100000))) + file_name_suffix # use default storage to store file default_storage.save(new_file_name, f) # check file size @@ -1886,7 +1901,7 @@ def book(request, short_name, unanswered=False): """ books = Book.objects.extra(where=['short_name = %s'], params=[short_name]) match_count = len(books) - if match_count == 0 : + if match_count == 0: raise Http404 else: # the book info @@ -1908,7 +1923,7 @@ def book(request, short_name, unanswered=False): page = 1 view_id = request.GET.get('sort', None) - view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" } + view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score"} try: orderby = view_dic[view_id] except KeyError: @@ -1929,22 +1944,22 @@ def book(request, short_name, unanswered=False): questions = objects_list.page(page) return render_to_response('book.html', { - "book" : book, - "author_info" : author_info, - "author_rss" : author_rss, - "questions" : questions, - "context" : { - 'is_paginated' : True, - 'pages': objects_list.num_pages, - 'page': page, - 'has_previous': questions.has_previous(), - 'has_next': questions.has_next(), - 'previous': questions.previous_page_number(), - 'next': questions.next_page_number(), - 'base_url' : request.path + '?sort=%s&' % view_id, - 'pagesize' : user_page_size - } - }, context_instance=RequestContext(request)) + "book": book, + "author_info": author_info, + "author_rss": author_rss, + "questions": questions, + "context": { + 'is_paginated': True, + 'pages': objects_list.num_pages, + 'page': page, + 'has_previous': questions.has_previous(), + 'has_next': questions.has_next(), + 'previous': questions.previous_page_number(), + 'next': questions.next_page_number(), + 'base_url': request.path + '?sort=%s&' % view_id, + 'pagesize': user_page_size + } + }, context_instance=RequestContext(request)) @login_required def ask_book(request, short_name): @@ -1954,16 +1969,16 @@ def ask_book(request, short_name): added_at = datetime.datetime.now() html = sanitize_html(markdowner.convert(form.cleaned_data['text'])) question = Question( - title = strip_tags(form.cleaned_data['title']), - author = request.user, - added_at = added_at, - last_activity_at = added_at, - last_activity_by = request.user, - wiki = form.cleaned_data['wiki'], - tagnames = form.cleaned_data['tags'].strip(), - html = html, - summary = strip_tags(html)[:120] - ) + title=strip_tags(form.cleaned_data['title']), + author=request.user, + added_at=added_at, + last_activity_at=added_at, + last_activity_by=request.user, + wiki=form.cleaned_data['wiki'], + tagnames=form.cleaned_data['tags'].strip(), + html=html, + summary=strip_tags(html)[:120] + ) if question.wiki: question.last_edited_by = question.author question.last_edited_at = added_at @@ -1973,15 +1988,15 @@ def ask_book(request, short_name): # create the first revision QuestionRevision.objects.create( - question = question, - revision = 1, - title = question.title, - author = request.user, - revised_at = added_at, - tagnames = question.tagnames, - summary = CONST['default_version'], - text = form.cleaned_data['text'] - ) + question=question, + revision=1, + title=question.title, + author=request.user, + revised_at=added_at, + tagnames=question.tagnames, + summary=CONST['default_version'], + text=form.cleaned_data['text'] + ) books = Book.objects.extra(where=['short_name = %s'], params=[short_name]) match_count = len(books) @@ -1996,9 +2011,9 @@ def ask_book(request, short_name): tags = _get_tags_cache_json() return render_to_response('ask.html', { - 'form' : form, - 'tags' : tags, - }, context_instance=RequestContext(request)) + 'form': form, + 'tags': tags, + }, context_instance=RequestContext(request)) def search(request): """ @@ -2049,7 +2064,7 @@ def search(request): user.save() view_id = request.GET.get('sort', None) - view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" } + view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score"} try: orderby = view_dic[view_id] except KeyError: @@ -2072,25 +2087,25 @@ def search(request): related_tags.append(tag) return render_to_response(template_file, { - "questions" : questions, - "tab_id" : view_id, - "questions_count" : objects_list.count, - "tags" : related_tags, - "searchtag" : None, - "searchtitle" : keywords, - "keywords" : keywords, - "is_unanswered" : False, - "context" : { - 'is_paginated' : True, - 'pages': objects_list.num_pages, - 'page': page, - 'has_previous': questions.has_previous(), - 'has_next': questions.has_next(), - 'previous': questions.previous_page_number(), - 'next': questions.next_page_number(), - 'base_url' : request.path + '?t=question&q=%s&sort=%s&' % (keywords, view_id), - 'pagesize' : pagesize - }}, context_instance=RequestContext(request)) + "questions": questions, + "tab_id": view_id, + "questions_count": objects_list.count, + "tags": related_tags, + "searchtag": None, + "searchtitle": keywords, + "keywords": keywords, + "is_unanswered": False, + "context": { + 'is_paginated': True, + 'pages': objects_list.num_pages, + 'page': page, + 'has_previous': questions.has_previous(), + 'has_next': questions.has_next(), + 'previous': questions.previous_page_number(), + 'next': questions.next_page_number(), + 'base_url': request.path + '?t=question&q=%s&sort=%s&' % (keywords, view_id), + 'pagesize': pagesize + }}, context_instance=RequestContext(request)) else: raise Http404 diff --git a/settings.py b/settings.py index aa991898..7a70ae72 100644 --- a/settings.py +++ b/settings.py @@ -37,7 +37,7 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.transaction.TransactionMiddleware', #'django.middleware.sqlprint.SqlPrintingMiddleware', #'middleware.pagesize.QuestionsPageSizeMiddleware', - 'debug_toolbar.middleware.DebugToolbarMiddleware', + #'debug_toolbar.middleware.DebugToolbarMiddleware', ) TEMPLATE_CONTEXT_PROCESSORS = ( @@ -73,7 +73,7 @@ INSTALLED_APPS = ( 'django.contrib.humanize', 'forum', 'django_authopenid', - 'debug_toolbar' , + #'debug_toolbar' , ) import django DJANGO_VERSION = django.get_version() -- cgit v1.2.3-1-g7c22 From 65fb7bb5a961342b051566b1488fae20792d90b3 Mon Sep 17 00:00:00 2001 From: Mike Chen Date: Mon, 24 Aug 2009 00:16:18 +0800 Subject: fix tags sort bug --- forum/views.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/forum/views.py b/forum/views.py index 0d2b3ca2..dc38bcdf 100644 --- a/forum/views.py +++ b/forum/views.py @@ -710,14 +710,14 @@ def tags(request): if request.method == "GET": stag = request.GET.get("q", "").strip() - if stag is not None: + if len(stag) > 0: objects_list = Paginator(Tag.objects.filter(deleted=False).exclude(used_count=0).extra(where=['name like %s'], params=['%' + stag + '%']), DEFAULT_PAGE_SIZE) else: - if sortby == "name": - objects_list = Paginator(Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("name"), DEFAULT_PAGE_SIZE) + if sortby == "used": + sortby = "-used_count" else: - objects_list = Paginator(Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("-used_count"), DEFAULT_PAGE_SIZE) - + sortby = "name" + objects_list = Paginator(Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by(sortby), DEFAULT_PAGE_SIZE) try: tags = objects_list.page(page) except (EmptyPage, InvalidPage): -- cgit v1.2.3-1-g7c22 From e8f1aef3636a2b9ef10d2d2bc4a768503a3e0d07 Mon Sep 17 00:00:00 2001 From: sailingcai Date: Mon, 12 Oct 2009 22:49:12 +0800 Subject: 1. adjust logo css, remove margin-bottom 2. add urlquote in get_absolute_url 3. enable QuestionsPageSizeMiddleware middleware 4. remove unused code --- forum/models.py | 7 ++++--- forum/views.py | 2 -- settings.py | 2 +- templates/content/style/style.css | 1 - 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/forum/models.py b/forum/models.py index 255eb21f..a6326fa9 100644 --- a/forum/models.py +++ b/forum/models.py @@ -3,6 +3,7 @@ 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 @@ -180,7 +181,7 @@ class Question(models.Model): return [name for name in self.tagnames.split(u' ')] def get_absolute_url(self): - return '%s%s' % (reverse('question', args=[self.id]), self.title.replace(' ', '-')) + return '%s%s' % (reverse('question', args=[self.id]), django_urlquote(self.title.replace(' ', '-'))) def has_favorite_by_user(self, user): if not user.is_authenticated(): @@ -384,7 +385,7 @@ class Answer(models.Model): return self.question.title def get_absolute_url(self): - return '%s%s#%s' % (reverse('question', args=[self.question.id]), self.question.title, self.id) + return '%s%s#%s' % (reverse('question', args=[self.question.id]), django_urlquote(self.question.title), self.id) class Meta: db_table = u'answer' @@ -535,7 +536,7 @@ class Book(models.Model): questions = models.ManyToManyField(Question, related_name='book', db_table='book_question') def get_absolute_url(self): - return '%s' % reverse('book', args=[self.short_name]) + return '%s' % reverse('book', args=[django_urlquote(self.short_name)]) def __unicode__(self): return self.title diff --git a/forum/views.py b/forum/views.py index dc38bcdf..377e7e8b 100644 --- a/forum/views.py +++ b/forum/views.py @@ -115,8 +115,6 @@ def questions(request, tagname=None, unanswered=False): # template file # "questions.html" or "unanswered.html" template_file = "questions.html" - # Set flag to False by default. If it is equal to True, then need to be saved. - pagesize_changed = False # get pagesize from session, if failed then get default value pagesize = request.session.get("pagesize", 10) try: diff --git a/settings.py b/settings.py index 7a70ae72..78df6859 100644 --- a/settings.py +++ b/settings.py @@ -36,7 +36,7 @@ MIDDLEWARE_CLASSES = ( 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.middleware.transaction.TransactionMiddleware', #'django.middleware.sqlprint.SqlPrintingMiddleware', - #'middleware.pagesize.QuestionsPageSizeMiddleware', + 'middleware.pagesize.QuestionsPageSizeMiddleware', #'debug_toolbar.middleware.DebugToolbarMiddleware', ) diff --git a/templates/content/style/style.css b/templates/content/style/style.css index fa20b014..7ccf1af3 100644 --- a/templates/content/style/style.css +++ b/templates/content/style/style.css @@ -108,7 +108,6 @@ blockquote margin-left:20px;text-decoration:underline; font-size:12px; color:#333333;} #logo { padding: 5px 0px 0px 0px; - margin-bottom:-3px; } #navBar {float:clear;position:relative;display:block;width:960px;} #navBar .nav {margin:20px 0px 0px 16px; -- cgit v1.2.3-1-g7c22 From 68050eb048190c0b924c8c0612540c405dcd5ea6 Mon Sep 17 00:00:00 2001 From: sailingcai Date: Mon, 12 Oct 2009 22:58:32 +0800 Subject: 1. trim question title --- forum/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forum/views.py b/forum/views.py index 377e7e8b..08f665f8 100644 --- a/forum/views.py +++ b/forum/views.py @@ -267,7 +267,7 @@ def ask(request): if form.is_valid(): added_at = datetime.datetime.now() - title = strip_tags(form.cleaned_data['title']) + title = strip_tags(form.cleaned_data['title']).strip() wiki = form.cleaned_data['wiki'] tagnames = form.cleaned_data['tags'].strip() text = form.cleaned_data['text'] -- cgit v1.2.3-1-g7c22 From 0a6e82562c5c910cef4711e30a04ef866b805e32 Mon Sep 17 00:00:00 2001 From: sailingcai Date: Tue, 13 Oct 2009 23:05:39 +0800 Subject: 1. refactor get_answers_from_question 2. refactor order_by for answers --- forum/managers.py | 17 +++++++++++------ forum/views.py | 6 +----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/forum/managers.py b/forum/managers.py index 2e3e4186..aa155415 100644 --- a/forum/managers.py +++ b/forum/managers.py @@ -165,17 +165,22 @@ class TagManager(models.Manager): class AnswerManager(models.Manager): 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): + def get_answers_from_question(self, question, user=None, other_orderby = None): """ Retrieves visibile answers for the given question. Delete answers are only visibile to the person who deleted them. - """ - + """ if user is None or not user.is_authenticated(): - return self.filter(question=question, deleted=False) + q = self.filter(question=question, deleted=False) + else: + q = self.filter(Q(question=question), + Q(deleted=False) | Q(deleted_by=user)) + if other_orderby is None: + q = q.order_by("-accepted") else: - return self.filter(Q(question=question), - Q(deleted=False) | Q(deleted_by=user)) + q = q.order_by("-accepted", other_orderby) + + return q def get_answers_from_questions(self, user_id): """ diff --git a/forum/views.py b/forum/views.py index 08f665f8..5e53644f 100644 --- a/forum/views.py +++ b/forum/views.py @@ -329,7 +329,7 @@ def question(request, id): if question.deleted and not can_view_deleted_post(request.user, question): raise Http404 answer_form = AnswerForm(question, request.user) - answers = Answer.objects.get_answers_from_question(question, request.user) + answers = Answer.objects.get_answers_from_question(question, request.user, orderby) answers = answers.select_related(depth=1) favorited = question.has_favorite_by_user(request.user) @@ -346,10 +346,6 @@ def question(request, id): vote_value = 1 user_answer_votes[answer.id] = vote_value - - if answers is not None: - answers = answers.order_by("-accepted", orderby) - filtered_answers = [] for answer in answers: if answer.deleted == True: -- cgit v1.2.3-1-g7c22 From e02cd69de6b6c534792ba12b1ac10e01e04318c2 Mon Sep 17 00:00:00 2001 From: sailingcai Date: Wed, 14 Oct 2009 00:27:59 +0800 Subject: 1. Optimize get votes in answers --- forum/models.py | 12 +++++++++++- forum/views.py | 9 ++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/forum/models.py b/forum/models.py index a6326fa9..b6722ae7 100644 --- a/forum/models.py +++ b/forum/models.py @@ -209,7 +209,17 @@ class Question(models.Model): def get_latest_revision(self): return self.revisions.all()[0] - + + def get_user_votes_in_answers(self, user): + content_type = ContentType.objects.get_for_model(Answer) + query_set = Vote.objects.extra( + tables = ['question', 'answer'], + where = ['question.id = answer.question_id AND question.id = %s AND vote.object_id = answer.id AND vote.content_type_id = %s AND vote.user_id = %s'], + params = [self.id, content_type.id, user.id] + ) + + return query_set + def get_update_summary(self,last_reported_at=None,recipient_email=''): edited = False if self.last_edited_at and self.last_edited_at > last_reported_at: diff --git a/forum/views.py b/forum/views.py index 5e53644f..cfadaa48 100644 --- a/forum/views.py +++ b/forum/views.py @@ -338,14 +338,13 @@ def question(request, id): question_vote = question_vote[0] user_answer_votes = {} - for answer in answers: - vote = answer.get_user_vote(request.user) - if vote is not None and not user_answer_votes.has_key(answer.id): + for vote in question.get_user_votes_in_answers(request.user): + if not user_answer_votes.has_key(vote.object_id): vote_value = -1 if vote.is_upvote(): vote_value = 1 - user_answer_votes[answer.id] = vote_value - + user_answer_votes[vote.object_id] = vote_value + filtered_answers = [] for answer in answers: if answer.deleted == True: -- cgit v1.2.3-1-g7c22 From b1085dec13052fa9ff64b53ed292f1ad61064cac Mon Sep 17 00:00:00 2001 From: sailingcai Date: Wed, 14 Oct 2009 23:11:41 +0800 Subject: 1. update var setting in blocktrans --- templates/user_stats.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/user_stats.html b/templates/user_stats.html index 432a5b34..c324ff48 100644 --- a/templates/user_stats.html +++ b/templates/user_stats.html @@ -40,7 +40,7 @@ {% endfor %} -- cgit v1.2.3-1-g7c22 From 18f1a07afed3208ef4424305da8c6b1a34cf3aed Mon Sep 17 00:00:00 2001 From: Mike Chen Date: Thu, 15 Oct 2009 00:27:04 +0800 Subject: update INSTALL_ --- .gitignore | 2 ++ INSTALL | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 05a341b0..975f2e73 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ *.swp nbproject settings_local.py +log +*.log diff --git a/INSTALL b/INSTALL index 35683147..0c5e76dc 100644 --- a/INSTALL +++ b/INSTALL @@ -1,9 +1,16 @@ PRE-REQUIREMENTS: ----------------------------------------------- -1. Python2.5, MySQL, Django v1.0+ +0. We recommend you to use python-setuptools to install pre-requirement libraries. +If you haven't installed it, please try to install it first. +e.g, sudo apt-get install python-setuptools + +1. Python2.5/2.6, MySQL, Django v1.0/1.1 +Make sure mysql for python provider has been installed. +sudo easy_install mysql-python 2. Python-openid v2.2 http://openidenabled.com/python-openid/ +sudo easy_install python-openid 3. django-authopenid(Included in project already) http://code.google.com/p/django-authopenid/ @@ -11,21 +18,29 @@ http://code.google.com/p/django-authopenid/ 4. html5lib http://code.google.com/p/html5lib/ Used for HTML sanitizer +sudo easy_install html5lib 5. Markdown2 http://code.google.com/p/python-markdown2/ +sudo easy_install markdown2 6. Django Debug Toolbar http://github.com/robhudson/django-debug-toolbar/tree/master INSTALL STEPS: ----------------------------------------------- +0. Make sure you have all above python libraries installed. + 1. Copy settings_local.py.dist to settings_local.py and update all your settings. Check settings.py and update it as well if necessory. 2. Prepare your database by using the same database/account configuration from above. +e.g, +create database cnprog DEFAULT CHARACTER SET UTF8 COLLATE utf8_general_ci; +grant all on cnprog.* to 'cnprog'@'localhost'; +And then run "python manage.py syncdb" to synchronize your database. 3. Run "python manager.py runserver" to startup django development environment. -- cgit v1.2.3-1-g7c22