From 65561d50def89910746d53ae58801ac6d4ba1a8a Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Sun, 28 Feb 2010 20:22:29 -0500 Subject: dos2unix --- forum/management/__init__.py | 4 +- forum/models/__init__.py | 680 +++++++++++++-------------- forum/models/answer.py | 266 +++++------ forum/models/base.py | 276 +++++------ forum/models/meta.py | 176 +++---- forum/models/question.py | 670 +++++++++++++------------- forum/models/repute.py | 218 ++++----- forum/models/tag.py | 168 +++---- forum/models/user.py | 134 +++--- forum/modules.py | 106 ++--- forum_modules/books/__init__.py | 6 +- forum_modules/books/models.py | 124 ++--- forum_modules/books/urls.py | 18 +- forum_modules/books/views.py | 282 +++++------ forum_modules/pgfulltext/__init__.py | 16 +- forum_modules/pgfulltext/handlers.py | 20 +- forum_modules/pgfulltext/management.py | 58 +-- forum_modules/sphinxfulltext/dependencies.py | 4 +- forum_modules/sphinxfulltext/handlers.py | 6 +- forum_modules/sphinxfulltext/models.py | 20 +- forum_modules/sphinxfulltext/settings.py | 10 +- settings_local.py.dist | 220 ++++----- 22 files changed, 1741 insertions(+), 1741 deletions(-) diff --git a/forum/management/__init__.py b/forum/management/__init__.py index 8266592a..b654caaa 100644 --- a/forum/management/__init__.py +++ b/forum/management/__init__.py @@ -1,3 +1,3 @@ -from forum.modules import get_modules_script - +from forum.modules import get_modules_script + get_modules_script('management') \ No newline at end of file diff --git a/forum/models/__init__.py b/forum/models/__init__.py index ccc53a7c..9b504103 100755 --- a/forum/models/__init__.py +++ b/forum/models/__init__.py @@ -1,341 +1,341 @@ -from question import Question ,QuestionRevision, QuestionView, AnonymousQuestion, FavoriteQuestion -from answer import Answer, AnonymousAnswer, AnswerRevision -from tag import Tag, MarkedTag -from meta import Vote, Comment, FlaggedItem -from user import Activity, AnonymousEmail, EmailFeedSetting -from repute import Badge, Award, Repute - -from base import * - -# User extend properties -QUESTIONS_PER_PAGE_CHOICES = ( - (10, u'10'), - (30, u'30'), - (50, u'50'), -) - -def user_is_username_taken(cls,username): - try: - cls.objects.get(username=username) - return True - except cls.MultipleObjectsReturned: - return True - except cls.DoesNotExist: - return False - -def user_get_q_sel_email_feed_frequency(self): - #print 'looking for frequency for user %s' % self - try: - feed_setting = EmailFeedSetting.objects.get(subscriber=self,feed_type='q_sel') - except Exception, e: - #print 'have error %s' % e.message - raise e - #print 'have freq=%s' % feed_setting.frequency - return feed_setting.frequency - -User.add_to_class('is_approved', models.BooleanField(default=False)) -User.add_to_class('email_isvalid', models.BooleanField(default=False)) -User.add_to_class('email_key', models.CharField(max_length=32, 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('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)) -User.add_to_class('is_username_taken',classmethod(user_is_username_taken)) -User.add_to_class('get_q_sel_email_feed_frequency',user_get_q_sel_email_feed_frequency) -User.add_to_class('hide_ignored_questions', models.BooleanField(default=False)) -User.add_to_class('tag_filter_setting', - models.CharField( - max_length=16, - choices=TAG_EMAIL_FILTER_CHOICES, - default='ignored' - ) - ) - -# 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]), slugify(self.username)) - -def get_profile_link(self): - profile_link = u'%s' % (self.get_profile_url(),self.username) - logging.debug('in get profile link %s' % profile_link) - return mark_safe(profile_link) - -User.add_to_class('get_profile_url', get_profile_url) -User.add_to_class('get_profile_link', get_profile_link) -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 - 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() - -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"Congratulations, you have received a badge '%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) - -Question = Question -QuestionRevision = QuestionRevision -QuestionView = QuestionView -FavoriteQuestion = FavoriteQuestion -AnonymousQuestion = AnonymousQuestion - -Answer = Answer -AnswerRevision = AnswerRevision -AnonymousAnswer = AnonymousAnswer - -Tag = Tag -Comment = Comment -Vote = Vote -FlaggedItem = FlaggedItem -MarkedTag = MarkedTag - -Badge = Badge -Award = Award -Repute = Repute - -Activity = Activity -EmailFeedSetting = EmailFeedSetting -AnonymousEmail = AnonymousEmail - -__all__ = [ - 'Question', - 'QuestionRevision', - 'QuestionView', - 'FavoriteQuestion', - 'AnonymousQuestion', - - 'Answer', - 'AnswerRevision', - 'AnonymousAnswer', - - 'Tag', - 'Comment', - 'Vote', - 'FlaggedItem', - 'MarkedTag', - - 'Badge', - 'Award', - 'Repute', - - 'Activity', - 'EmailFeedSetting', - 'AnonymousEmail', - - 'User' - ] - - -from forum.modules import get_modules_script_classes - -for k, v in get_modules_script_classes('models', models.Model).items(): - if not k in __all__: - __all__.append(k) +from question import Question ,QuestionRevision, QuestionView, AnonymousQuestion, FavoriteQuestion +from answer import Answer, AnonymousAnswer, AnswerRevision +from tag import Tag, MarkedTag +from meta import Vote, Comment, FlaggedItem +from user import Activity, AnonymousEmail, EmailFeedSetting +from repute import Badge, Award, Repute + +from base import * + +# User extend properties +QUESTIONS_PER_PAGE_CHOICES = ( + (10, u'10'), + (30, u'30'), + (50, u'50'), +) + +def user_is_username_taken(cls,username): + try: + cls.objects.get(username=username) + return True + except cls.MultipleObjectsReturned: + return True + except cls.DoesNotExist: + return False + +def user_get_q_sel_email_feed_frequency(self): + #print 'looking for frequency for user %s' % self + try: + feed_setting = EmailFeedSetting.objects.get(subscriber=self,feed_type='q_sel') + except Exception, e: + #print 'have error %s' % e.message + raise e + #print 'have freq=%s' % feed_setting.frequency + return feed_setting.frequency + +User.add_to_class('is_approved', models.BooleanField(default=False)) +User.add_to_class('email_isvalid', models.BooleanField(default=False)) +User.add_to_class('email_key', models.CharField(max_length=32, 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('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)) +User.add_to_class('is_username_taken',classmethod(user_is_username_taken)) +User.add_to_class('get_q_sel_email_feed_frequency',user_get_q_sel_email_feed_frequency) +User.add_to_class('hide_ignored_questions', models.BooleanField(default=False)) +User.add_to_class('tag_filter_setting', + models.CharField( + max_length=16, + choices=TAG_EMAIL_FILTER_CHOICES, + default='ignored' + ) + ) + +# 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]), slugify(self.username)) + +def get_profile_link(self): + profile_link = u'%s' % (self.get_profile_url(),self.username) + logging.debug('in get profile link %s' % profile_link) + return mark_safe(profile_link) + +User.add_to_class('get_profile_url', get_profile_url) +User.add_to_class('get_profile_link', get_profile_link) +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 + 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() + +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"Congratulations, you have received a badge '%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) + +Question = Question +QuestionRevision = QuestionRevision +QuestionView = QuestionView +FavoriteQuestion = FavoriteQuestion +AnonymousQuestion = AnonymousQuestion + +Answer = Answer +AnswerRevision = AnswerRevision +AnonymousAnswer = AnonymousAnswer + +Tag = Tag +Comment = Comment +Vote = Vote +FlaggedItem = FlaggedItem +MarkedTag = MarkedTag + +Badge = Badge +Award = Award +Repute = Repute + +Activity = Activity +EmailFeedSetting = EmailFeedSetting +AnonymousEmail = AnonymousEmail + +__all__ = [ + 'Question', + 'QuestionRevision', + 'QuestionView', + 'FavoriteQuestion', + 'AnonymousQuestion', + + 'Answer', + 'AnswerRevision', + 'AnonymousAnswer', + + 'Tag', + 'Comment', + 'Vote', + 'FlaggedItem', + 'MarkedTag', + + 'Badge', + 'Award', + 'Repute', + + 'Activity', + 'EmailFeedSetting', + 'AnonymousEmail', + + 'User' + ] + + +from forum.modules import get_modules_script_classes + +for k, v in get_modules_script_classes('models', models.Model).items(): + if not k in __all__: + __all__.append(k) exec "%s = v" % k \ No newline at end of file diff --git a/forum/models/answer.py b/forum/models/answer.py index a1580828..16e55c69 100755 --- a/forum/models/answer.py +++ b/forum/models/answer.py @@ -1,133 +1,133 @@ -from base import * - -from question import Question - -class AnswerManager(models.Manager): - def create_new(self, question=None, author=None, added_at=None, wiki=False, text='', email_notify=False): - answer = Answer( - question = question, - author = author, - added_at = added_at, - wiki = wiki, - html = text - ) - 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) - - 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: - if author not in question.followed_by.all(): - question.followed_by.add(author) - else: - #not sure if this is necessary. ajax should take care of this... - try: - question.followed_by.remove(author) - except: - pass - - #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): - """ - 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) - else: - return self.filter(models.Q(question=question), - models.Q(deleted=False) | models.Q(deleted_by=user)) - - #todo: I think this method is not being used anymore, I'll just comment it for now - #def get_answers_from_questions(self, user_id): - # """ - # Retrieves visibile answers for the given question. Which are not included own answers - # """ - # cursor = connection.cursor() - # cursor.execute(self.GET_ANSWERS_FROM_USER_QUESTIONS, [user_id, user_id]) - # return cursor.fetchall() - -class Answer(Content, DeletableContent): - question = models.ForeignKey('Question', related_name='answers') - accepted = models.BooleanField(default=False) - accepted_at = models.DateTimeField(null=True, blank=True) - - objects = AnswerManager() - - class Meta(Content.Meta): - db_table = u'answer' - - def get_user_vote(self, user): - if user.__class__.__name__ == "AnonymousUser": - return None - - votes = self.votes.filter(user=user) - if votes and 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]), django_urlquote(slugify(self.question.title)), self.id) - - def __unicode__(self): - return self.html - - -class AnswerRevision(ContentRevision): - """A revision of an Answer.""" - answer = models.ForeignKey('Answer', related_name='revisions') - - def get_absolute_url(self): - return reverse('answer_revisions', kwargs={'id':self.answer.id}) - - def get_question_title(self): - return self.answer.question.title - - class Meta(ContentRevision.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 AnonymousAnswer(AnonymousContent): - question = models.ForeignKey('Question', related_name='anonymous_answers') - - def publish(self,user): - added_at = datetime.datetime.now() - #print user.id - AnswerManager.create_new(question=self.question,wiki=self.wiki, - added_at=added_at,text=self.text, - author=user) - self.delete() +from base import * + +from question import Question + +class AnswerManager(models.Manager): + def create_new(self, question=None, author=None, added_at=None, wiki=False, text='', email_notify=False): + answer = Answer( + question = question, + author = author, + added_at = added_at, + wiki = wiki, + html = text + ) + 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) + + 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: + if author not in question.followed_by.all(): + question.followed_by.add(author) + else: + #not sure if this is necessary. ajax should take care of this... + try: + question.followed_by.remove(author) + except: + pass + + #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): + """ + 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) + else: + return self.filter(models.Q(question=question), + models.Q(deleted=False) | models.Q(deleted_by=user)) + + #todo: I think this method is not being used anymore, I'll just comment it for now + #def get_answers_from_questions(self, user_id): + # """ + # Retrieves visibile answers for the given question. Which are not included own answers + # """ + # cursor = connection.cursor() + # cursor.execute(self.GET_ANSWERS_FROM_USER_QUESTIONS, [user_id, user_id]) + # return cursor.fetchall() + +class Answer(Content, DeletableContent): + question = models.ForeignKey('Question', related_name='answers') + accepted = models.BooleanField(default=False) + accepted_at = models.DateTimeField(null=True, blank=True) + + objects = AnswerManager() + + class Meta(Content.Meta): + db_table = u'answer' + + def get_user_vote(self, user): + if user.__class__.__name__ == "AnonymousUser": + return None + + votes = self.votes.filter(user=user) + if votes and 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]), django_urlquote(slugify(self.question.title)), self.id) + + def __unicode__(self): + return self.html + + +class AnswerRevision(ContentRevision): + """A revision of an Answer.""" + answer = models.ForeignKey('Answer', related_name='revisions') + + def get_absolute_url(self): + return reverse('answer_revisions', kwargs={'id':self.answer.id}) + + def get_question_title(self): + return self.answer.question.title + + class Meta(ContentRevision.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 AnonymousAnswer(AnonymousContent): + question = models.ForeignKey('Question', related_name='anonymous_answers') + + def publish(self,user): + added_at = datetime.datetime.now() + #print user.id + AnswerManager.create_new(question=self.question,wiki=self.wiki, + added_at=added_at,text=self.text, + author=user) + self.delete() diff --git a/forum/models/base.py b/forum/models/base.py index 2c28a470..44fa6e66 100755 --- a/forum/models/base.py +++ b/forum/models/base.py @@ -1,139 +1,139 @@ -import datetime -import hashlib -from urllib import quote_plus, urlencode -from django.db import models, IntegrityError, connection, transaction -from django.utils.http import urlquote as django_urlquote -from django.utils.html import strip_tags -from django.core.urlresolvers import reverse -from django.contrib.auth.models import User -from django.contrib.contenttypes import generic -from django.contrib.contenttypes.models import ContentType -from django.template.defaultfilters import slugify -from django.db.models.signals import post_delete, post_save, pre_save -from django.utils.translation import ugettext as _ -from django.utils.safestring import mark_safe -from django.contrib.sitemaps import ping_google -import django.dispatch -from django.conf import settings -import logging - -if settings.USE_SPHINX_SEARCH == True: - from djangosphinx.models import SphinxSearch - -from forum.const import * - -class MetaContent(models.Model): - """ - Base class for Vote, Comment and FlaggedItem - """ - content_type = models.ForeignKey(ContentType) - object_id = models.PositiveIntegerField() - content_object = generic.GenericForeignKey('content_type', 'object_id') - user = models.ForeignKey(User, related_name='%(class)ss') - - class Meta: - abstract = True - app_label = 'forum' - - -class DeletableContent(models.Model): - deleted = models.BooleanField(default=False) - deleted_at = models.DateTimeField(null=True, blank=True) - deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_%(class)ss') - - class Meta: - abstract = True - app_label = 'forum' - - -class ContentRevision(models.Model): - """ - Base class for QuestionRevision and AnswerRevision - """ - revision = models.PositiveIntegerField() - author = models.ForeignKey(User, related_name='%(class)ss') - revised_at = models.DateTimeField() - summary = models.CharField(max_length=300, blank=True) - text = models.TextField() - - class Meta: - abstract = True - app_label = 'forum' - - -class AnonymousContent(models.Model): - """ - Base class for AnonymousQuestion and AnonymousAnswer - """ - 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) - - class Meta: - abstract = True - app_label = 'forum' - - -from meta import Comment, Vote, FlaggedItem - -class Content(models.Model): - """ - Base class for Question and Answer - """ - author = models.ForeignKey(User, related_name='%(class)ss') - added_at = models.DateTimeField(default=datetime.datetime.now) - - wiki = models.BooleanField(default=False) - wikified_at = models.DateTimeField(null=True, blank=True) - - locked = models.BooleanField(default=False) - locked_by = models.ForeignKey(User, null=True, blank=True, related_name='locked_%(class)ss') - locked_at = models.DateTimeField(null=True, blank=True) - - 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_%(class)ss') - - html = models.TextField() - comments = generic.GenericRelation(Comment) - votes = generic.GenericRelation(Vote) - flagged_items = generic.GenericRelation(FlaggedItem) - - class Meta: - abstract = True - app_label = 'forum' - - def save(self,**kwargs): - super(Content,self).save(**kwargs) - try: - ping_google() - except Exception: - logging.debug('problem pinging google did you register you sitemap with google?') - - def get_object_comments(self): - comments = self.comments.all().order_by('id') - return comments - - def post_get_last_update_info(self): - when = self.added_at - who = self.author - if self.last_edited_at and self.last_edited_at > when: - when = self.last_edited_at - who = self.last_edited_by - comments = self.comments.all() - if len(comments) > 0: - for c in comments: - if c.added_at > when: - when = c.added_at - who = c.user +import datetime +import hashlib +from urllib import quote_plus, urlencode +from django.db import models, IntegrityError, connection, transaction +from django.utils.http import urlquote as django_urlquote +from django.utils.html import strip_tags +from django.core.urlresolvers import reverse +from django.contrib.auth.models import User +from django.contrib.contenttypes import generic +from django.contrib.contenttypes.models import ContentType +from django.template.defaultfilters import slugify +from django.db.models.signals import post_delete, post_save, pre_save +from django.utils.translation import ugettext as _ +from django.utils.safestring import mark_safe +from django.contrib.sitemaps import ping_google +import django.dispatch +from django.conf import settings +import logging + +if settings.USE_SPHINX_SEARCH == True: + from djangosphinx.models import SphinxSearch + +from forum.const import * + +class MetaContent(models.Model): + """ + Base class for Vote, Comment and FlaggedItem + """ + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey('content_type', 'object_id') + user = models.ForeignKey(User, related_name='%(class)ss') + + class Meta: + abstract = True + app_label = 'forum' + + +class DeletableContent(models.Model): + deleted = models.BooleanField(default=False) + deleted_at = models.DateTimeField(null=True, blank=True) + deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_%(class)ss') + + class Meta: + abstract = True + app_label = 'forum' + + +class ContentRevision(models.Model): + """ + Base class for QuestionRevision and AnswerRevision + """ + revision = models.PositiveIntegerField() + author = models.ForeignKey(User, related_name='%(class)ss') + revised_at = models.DateTimeField() + summary = models.CharField(max_length=300, blank=True) + text = models.TextField() + + class Meta: + abstract = True + app_label = 'forum' + + +class AnonymousContent(models.Model): + """ + Base class for AnonymousQuestion and AnonymousAnswer + """ + 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) + + class Meta: + abstract = True + app_label = 'forum' + + +from meta import Comment, Vote, FlaggedItem + +class Content(models.Model): + """ + Base class for Question and Answer + """ + author = models.ForeignKey(User, related_name='%(class)ss') + added_at = models.DateTimeField(default=datetime.datetime.now) + + wiki = models.BooleanField(default=False) + wikified_at = models.DateTimeField(null=True, blank=True) + + locked = models.BooleanField(default=False) + locked_by = models.ForeignKey(User, null=True, blank=True, related_name='locked_%(class)ss') + locked_at = models.DateTimeField(null=True, blank=True) + + 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_%(class)ss') + + html = models.TextField() + comments = generic.GenericRelation(Comment) + votes = generic.GenericRelation(Vote) + flagged_items = generic.GenericRelation(FlaggedItem) + + class Meta: + abstract = True + app_label = 'forum' + + def save(self,**kwargs): + super(Content,self).save(**kwargs) + try: + ping_google() + except Exception: + logging.debug('problem pinging google did you register you sitemap with google?') + + def get_object_comments(self): + comments = self.comments.all().order_by('id') + return comments + + def post_get_last_update_info(self): + when = self.added_at + who = self.author + if self.last_edited_at and self.last_edited_at > when: + when = self.last_edited_at + who = self.last_edited_by + comments = self.comments.all() + if len(comments) > 0: + for c in comments: + if c.added_at > when: + when = c.added_at + who = c.user return when, who \ No newline at end of file diff --git a/forum/models/meta.py b/forum/models/meta.py index 3dfd3e86..7c3f5d36 100755 --- a/forum/models/meta.py +++ b/forum/models/meta.py @@ -1,89 +1,89 @@ -from base import * - -class VoteManager(models.Manager): - def get_up_vote_count_from_user(self, user): - if user is not None: - return self.filter(user=user, vote=1).count() - else: - return 0 - - def get_down_vote_count_from_user(self, user): - if user is not None: - return self.filter(user=user, vote=-1).count() - else: - return 0 - - def get_votes_count_today_from_user(self, user): - if user is not None: - today = datetime.date.today() - return self.filter(user=user, voted_at__range=(today, today + datetime.timedelta(1))).count() - - else: - return 0 - - -class Vote(MetaContent): - VOTE_UP = +1 - VOTE_DOWN = -1 - VOTE_CHOICES = ( - (VOTE_UP, u'Up'), - (VOTE_DOWN, u'Down'), - ) - - vote = models.SmallIntegerField(choices=VOTE_CHOICES) - voted_at = models.DateTimeField(default=datetime.datetime.now) - - objects = VoteManager() - - class Meta(MetaContent.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 FlaggedItemManager(models.Manager): - def get_flagged_items_count_today(self, user): - if user is not None: - today = datetime.date.today() - return self.filter(user=user, flagged_at__range=(today, today + datetime.timedelta(1))).count() - else: - return 0 - -class FlaggedItem(MetaContent): - """A flag on a Question or Answer indicating offensive content.""" - flagged_at = models.DateTimeField(default=datetime.datetime.now) - - objects = FlaggedItemManager() - - class Meta(MetaContent.Meta): - unique_together = ('content_type', 'object_id', 'user') - db_table = u'flagged_item' - - def __unicode__(self): - return '[%s] flagged at %s' %(self.user, self.flagged_at) - -class Comment(MetaContent): - comment = models.CharField(max_length=300) - added_at = models.DateTimeField(default=datetime.datetime.now) - - class Meta(MetaContent.Meta): - ordering = ('-added_at',) - db_table = u'comment' - - def save(self,**kwargs): - super(Comment,self).save(**kwargs) - try: - ping_google() - except Exception: - logging.debug('problem pinging google did you register you sitemap with google?') - - def __unicode__(self): +from base import * + +class VoteManager(models.Manager): + def get_up_vote_count_from_user(self, user): + if user is not None: + return self.filter(user=user, vote=1).count() + else: + return 0 + + def get_down_vote_count_from_user(self, user): + if user is not None: + return self.filter(user=user, vote=-1).count() + else: + return 0 + + def get_votes_count_today_from_user(self, user): + if user is not None: + today = datetime.date.today() + return self.filter(user=user, voted_at__range=(today, today + datetime.timedelta(1))).count() + + else: + return 0 + + +class Vote(MetaContent): + VOTE_UP = +1 + VOTE_DOWN = -1 + VOTE_CHOICES = ( + (VOTE_UP, u'Up'), + (VOTE_DOWN, u'Down'), + ) + + vote = models.SmallIntegerField(choices=VOTE_CHOICES) + voted_at = models.DateTimeField(default=datetime.datetime.now) + + objects = VoteManager() + + class Meta(MetaContent.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 FlaggedItemManager(models.Manager): + def get_flagged_items_count_today(self, user): + if user is not None: + today = datetime.date.today() + return self.filter(user=user, flagged_at__range=(today, today + datetime.timedelta(1))).count() + else: + return 0 + +class FlaggedItem(MetaContent): + """A flag on a Question or Answer indicating offensive content.""" + flagged_at = models.DateTimeField(default=datetime.datetime.now) + + objects = FlaggedItemManager() + + class Meta(MetaContent.Meta): + unique_together = ('content_type', 'object_id', 'user') + db_table = u'flagged_item' + + def __unicode__(self): + return '[%s] flagged at %s' %(self.user, self.flagged_at) + +class Comment(MetaContent): + comment = models.CharField(max_length=300) + added_at = models.DateTimeField(default=datetime.datetime.now) + + class Meta(MetaContent.Meta): + ordering = ('-added_at',) + db_table = u'comment' + + def save(self,**kwargs): + super(Comment,self).save(**kwargs) + try: + ping_google() + except Exception: + logging.debug('problem pinging google did you register you sitemap with google?') + + def __unicode__(self): return self.comment \ No newline at end of file diff --git a/forum/models/question.py b/forum/models/question.py index 27d6e9d9..37924a5a 100755 --- a/forum/models/question.py +++ b/forum/models/question.py @@ -1,335 +1,335 @@ -from base import * -from tag import Tag - -class QuestionManager(models.Manager): - def create_new(self, title=None,author=None,added_at=None, wiki=False,tagnames=None,summary=None, text=None): - question = Question( - title = title, - author = author, - added_at = added_at, - last_activity_at = added_at, - last_activity_by = author, - wiki = wiki, - tagnames = tagnames, - html = text, - 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 - - def update_tags(self, question, tagnames, user): - """ - Updates Tag associations for a question to match the given - tagname string. - - Returns ``True`` if tag usage counts were updated as a result, - ``False`` otherwise. - """ - - current_tags = list(question.tags.all()) - current_tagnames = set(t.name for t in current_tags) - updated_tagnames = set(t for t in tagnames.split(' ') if t) - modified_tags = [] - - removed_tags = [t for t in current_tags - if t.name not in updated_tagnames] - if removed_tags: - modified_tags.extend(removed_tags) - question.tags.remove(*removed_tags) - - added_tagnames = updated_tagnames - current_tagnames - if added_tagnames: - added_tags = Tag.objects.get_or_create_multiple(added_tagnames, - user) - modified_tags.extend(added_tags) - question.tags.add(*added_tags) - - if modified_tags: - Tag.objects.update_use_counts(modified_tags) - return True - - return False - - def update_answer_count(self, question): - """ - Executes an UPDATE query to update denormalised data with the - number of answers the given question has. - """ - - # for some reasons, this Answer class failed to be imported, - # although we have imported all classes from models on top. - from answer import Answer - self.filter(id=question.id).update( - answer_count=Answer.objects.get_answers_from_question(question).filter(deleted=False).count()) - - def update_view_count(self, question): - """ - update counter+1 when user browse question page - """ - self.filter(id=question.id).update(view_count = question.view_count + 1) - - def update_favorite_count(self, question): - """ - update favourite_count for given question - """ - self.filter(id=question.id).update(favourite_count = FavoriteQuestion.objects.filter(question=question).count()) - - def get_similar_questions(self, question): - """ - Get 10 similar questions for given one. - This will search the same tag list for give question(by exactly same string) first. - Questions with the individual tags will be added to list if above questions are not full. - """ - #print datetime.datetime.now() - questions = list(self.filter(tagnames = question.tagnames, deleted=False).all()) - - tags_list = question.tags.all() - for tag in tags_list: - extend_questions = self.filter(tags__id = tag.id, deleted=False)[:50] - for item in extend_questions: - if item not in questions and len(questions) < 10: - questions.append(item) - - #print datetime.datetime.now() - return questions - -class Question(Content, DeletableContent): - title = models.CharField(max_length=300) - tags = models.ManyToManyField('Tag', related_name='questions') - 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) - followed_by = models.ManyToManyField(User, related_name='followed_questions') - - # Denormalised data - answer_count = models.PositiveIntegerField(default=0) - view_count = models.PositiveIntegerField(default=0) - favourite_count = models.PositiveIntegerField(default=0) - 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) - - favorited_by = models.ManyToManyField(User, through='FavoriteQuestion', related_name='favorite_questions') - - objects = QuestionManager() - - class Meta(Content.Meta): - db_table = u'question' - - def delete(self): - super(Question, self).delete() - try: - ping_google() - except Exception: - logging.debug('problem pinging google did you register you sitemap with google?') - - 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 tagname_meta_generator(self): - return u','.join([unicode(tag) for tag in self.tagname_list()]) - - def get_absolute_url(self): - return '%s%s' % (reverse('question', args=[self.id]), django_urlquote(slugify(self.title))) - - 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): - from answer import Answer - 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_last_update_info(self): - when, who = self.post_get_last_update_info() - - answers = self.answers.all() - if len(answers) > 0: - for a in answers: - a_when, a_who = a.post_get_last_update_info() - if a_when > when: - when = a_when - who = a_who - - return when, who - - 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 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' - return retval - else: - return None - - def __unicode__(self): - return self.title - - -class QuestionView(models.Model): - question = models.ForeignKey(Question, related_name='viewed') - who = models.ForeignKey(User, related_name='question_views') - when = models.DateTimeField() - - class Meta: - app_label = 'forum' - -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: - app_label = 'forum' - db_table = u'favorite_question' - def __unicode__(self): - return '[%s] favorited at %s' %(self.user, self.added_at) - -class QuestionRevision(ContentRevision): - """A revision of a Question.""" - question = models.ForeignKey(Question, related_name='revisions') - title = models.CharField(max_length=300) - tagnames = models.CharField(max_length=125) - - class Meta(ContentRevision.Meta): - db_table = u'question_revision' - ordering = ('-revision',) - - def get_question_title(self): - return self.question.title - - def get_absolute_url(self): - #print 'in QuestionRevision.get_absolute_url()' - return reverse('question_revisions', args=[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 AnonymousQuestion(AnonymousContent): - title = models.CharField(max_length=300) - tagnames = models.CharField(max_length=125) - - def publish(self,user): - added_at = datetime.datetime.now() - QuestionManager.create_new(title=self.title, author=user, added_at=added_at, - wiki=self.wiki, tagnames=self.tagnames, - summary=self.summary, text=self.text) - self.delete() - -from answer import Answer, AnswerManager +from base import * +from tag import Tag + +class QuestionManager(models.Manager): + def create_new(self, title=None,author=None,added_at=None, wiki=False,tagnames=None,summary=None, text=None): + question = Question( + title = title, + author = author, + added_at = added_at, + last_activity_at = added_at, + last_activity_by = author, + wiki = wiki, + tagnames = tagnames, + html = text, + 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 + + def update_tags(self, question, tagnames, user): + """ + Updates Tag associations for a question to match the given + tagname string. + + Returns ``True`` if tag usage counts were updated as a result, + ``False`` otherwise. + """ + + current_tags = list(question.tags.all()) + current_tagnames = set(t.name for t in current_tags) + updated_tagnames = set(t for t in tagnames.split(' ') if t) + modified_tags = [] + + removed_tags = [t for t in current_tags + if t.name not in updated_tagnames] + if removed_tags: + modified_tags.extend(removed_tags) + question.tags.remove(*removed_tags) + + added_tagnames = updated_tagnames - current_tagnames + if added_tagnames: + added_tags = Tag.objects.get_or_create_multiple(added_tagnames, + user) + modified_tags.extend(added_tags) + question.tags.add(*added_tags) + + if modified_tags: + Tag.objects.update_use_counts(modified_tags) + return True + + return False + + def update_answer_count(self, question): + """ + Executes an UPDATE query to update denormalised data with the + number of answers the given question has. + """ + + # for some reasons, this Answer class failed to be imported, + # although we have imported all classes from models on top. + from answer import Answer + self.filter(id=question.id).update( + answer_count=Answer.objects.get_answers_from_question(question).filter(deleted=False).count()) + + def update_view_count(self, question): + """ + update counter+1 when user browse question page + """ + self.filter(id=question.id).update(view_count = question.view_count + 1) + + def update_favorite_count(self, question): + """ + update favourite_count for given question + """ + self.filter(id=question.id).update(favourite_count = FavoriteQuestion.objects.filter(question=question).count()) + + def get_similar_questions(self, question): + """ + Get 10 similar questions for given one. + This will search the same tag list for give question(by exactly same string) first. + Questions with the individual tags will be added to list if above questions are not full. + """ + #print datetime.datetime.now() + questions = list(self.filter(tagnames = question.tagnames, deleted=False).all()) + + tags_list = question.tags.all() + for tag in tags_list: + extend_questions = self.filter(tags__id = tag.id, deleted=False)[:50] + for item in extend_questions: + if item not in questions and len(questions) < 10: + questions.append(item) + + #print datetime.datetime.now() + return questions + +class Question(Content, DeletableContent): + title = models.CharField(max_length=300) + tags = models.ManyToManyField('Tag', related_name='questions') + 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) + followed_by = models.ManyToManyField(User, related_name='followed_questions') + + # Denormalised data + answer_count = models.PositiveIntegerField(default=0) + view_count = models.PositiveIntegerField(default=0) + favourite_count = models.PositiveIntegerField(default=0) + 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) + + favorited_by = models.ManyToManyField(User, through='FavoriteQuestion', related_name='favorite_questions') + + objects = QuestionManager() + + class Meta(Content.Meta): + db_table = u'question' + + def delete(self): + super(Question, self).delete() + try: + ping_google() + except Exception: + logging.debug('problem pinging google did you register you sitemap with google?') + + 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 tagname_meta_generator(self): + return u','.join([unicode(tag) for tag in self.tagname_list()]) + + def get_absolute_url(self): + return '%s%s' % (reverse('question', args=[self.id]), django_urlquote(slugify(self.title))) + + 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): + from answer import Answer + 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_last_update_info(self): + when, who = self.post_get_last_update_info() + + answers = self.answers.all() + if len(answers) > 0: + for a in answers: + a_when, a_who = a.post_get_last_update_info() + if a_when > when: + when = a_when + who = a_who + + return when, who + + 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 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' + return retval + else: + return None + + def __unicode__(self): + return self.title + + +class QuestionView(models.Model): + question = models.ForeignKey(Question, related_name='viewed') + who = models.ForeignKey(User, related_name='question_views') + when = models.DateTimeField() + + class Meta: + app_label = 'forum' + +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: + app_label = 'forum' + db_table = u'favorite_question' + def __unicode__(self): + return '[%s] favorited at %s' %(self.user, self.added_at) + +class QuestionRevision(ContentRevision): + """A revision of a Question.""" + question = models.ForeignKey(Question, related_name='revisions') + title = models.CharField(max_length=300) + tagnames = models.CharField(max_length=125) + + class Meta(ContentRevision.Meta): + db_table = u'question_revision' + ordering = ('-revision',) + + def get_question_title(self): + return self.question.title + + def get_absolute_url(self): + #print 'in QuestionRevision.get_absolute_url()' + return reverse('question_revisions', args=[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 AnonymousQuestion(AnonymousContent): + title = models.CharField(max_length=300) + tagnames = models.CharField(max_length=125) + + def publish(self,user): + added_at = datetime.datetime.now() + QuestionManager.create_new(title=self.title, author=user, added_at=added_at, + wiki=self.wiki, tagnames=self.tagnames, + summary=self.summary, text=self.text) + self.delete() + +from answer import Answer, AnswerManager diff --git a/forum/models/repute.py b/forum/models/repute.py index a47ce47d..495ba7e9 100755 --- a/forum/models/repute.py +++ b/forum/models/repute.py @@ -1,109 +1,109 @@ -from base import * - -from django.utils.translation import ugettext as _ - -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) - - awarded_to = models.ManyToManyField(User, through='Award', related_name='badges') - - class Meta: - app_label = 'forum' - 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 AwardManager(models.Manager): - def get_recent_awards(self): - awards = super(AwardManager, self).extra( - select={'badge_id': 'badge.id', 'badge_name':'badge.name', - 'badge_description': 'badge.description', 'badge_type': 'badge.type', - 'user_id': 'auth_user.id', 'user_name': 'auth_user.username' - }, - tables=['award', 'badge', 'auth_user'], - order_by=['-awarded_at'], - where=['auth_user.id=award.user_id AND badge_id=badge.id'], - ).values('badge_id', 'badge_name', 'badge_description', 'badge_type', 'user_id', 'user_name') - return awards - -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: - app_label = 'forum' - db_table = u'award' - -class ReputeManager(models.Manager): - def get_reputation_by_upvoted_today(self, user): - """ - For one user in one day, he can only earn rep till certain score (ep. +200) - by upvoted(also substracted from upvoted canceled). This is because we need - to prohibit gaming system by upvoting/cancel again and again. - """ - if user is not None: - today = datetime.date.today() - sums = self.filter(models.Q(reputation_type=1) | models.Q(reputation_type=-8), - user=user, reputed_at__range=(today, today + datetime.timedelta(1))). \ - agregate(models.Sum('positive'), models.Sum('negative')) - - return sums['positive__sum'] + sums['negative__sum'] - else: - return 0 - -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: - app_label = 'forum' - db_table = u'repute' +from base import * + +from django.utils.translation import ugettext as _ + +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) + + awarded_to = models.ManyToManyField(User, through='Award', related_name='badges') + + class Meta: + app_label = 'forum' + 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 AwardManager(models.Manager): + def get_recent_awards(self): + awards = super(AwardManager, self).extra( + select={'badge_id': 'badge.id', 'badge_name':'badge.name', + 'badge_description': 'badge.description', 'badge_type': 'badge.type', + 'user_id': 'auth_user.id', 'user_name': 'auth_user.username' + }, + tables=['award', 'badge', 'auth_user'], + order_by=['-awarded_at'], + where=['auth_user.id=award.user_id AND badge_id=badge.id'], + ).values('badge_id', 'badge_name', 'badge_description', 'badge_type', 'user_id', 'user_name') + return awards + +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: + app_label = 'forum' + db_table = u'award' + +class ReputeManager(models.Manager): + def get_reputation_by_upvoted_today(self, user): + """ + For one user in one day, he can only earn rep till certain score (ep. +200) + by upvoted(also substracted from upvoted canceled). This is because we need + to prohibit gaming system by upvoting/cancel again and again. + """ + if user is not None: + today = datetime.date.today() + sums = self.filter(models.Q(reputation_type=1) | models.Q(reputation_type=-8), + user=user, reputed_at__range=(today, today + datetime.timedelta(1))). \ + agregate(models.Sum('positive'), models.Sum('negative')) + + return sums['positive__sum'] + sums['negative__sum'] + else: + return 0 + +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: + app_label = 'forum' + db_table = u'repute' diff --git a/forum/models/tag.py b/forum/models/tag.py index 28b9e572..8d26d6f4 100755 --- a/forum/models/tag.py +++ b/forum/models/tag.py @@ -1,85 +1,85 @@ -from base import * - -from django.utils.translation import ugettext as _ - -class TagManager(models.Manager): - UPDATE_USED_COUNTS_QUERY = ( - 'UPDATE tag ' - 'SET used_count = (' - 'SELECT COUNT(*) FROM question_tags ' - 'INNER JOIN question ON question_id=question.id ' - 'WHERE tag_id = tag.id AND question.deleted=False' - ') ' - 'WHERE id IN (%s)') - - def get_valid_tags(self, page_size): - tags = self.all().filter(deleted=False).exclude(used_count=0).order_by("-id")[:page_size] - return tags - - def get_or_create_multiple(self, names, user): - """ - Fetches a list of Tags with the given names, creating any Tags - which don't exist when necesssary. - """ - tags = list(self.filter(name__in=names)) - #Set all these tag visible - for tag in tags: - if tag.deleted: - tag.deleted = False - tag.deleted_by = None - tag.deleted_at = None - tag.save() - - if len(tags) < len(names): - existing_names = set(tag.name for tag in tags) - new_names = [name for name in names if name not in existing_names] - tags.extend([self.create(name=name, created_by=user) - for name in new_names if self.filter(name=name).count() == 0 and len(name.strip()) > 0]) - - return tags - - def update_use_counts(self, tags): - """Updates the given Tags with their current use counts.""" - if not tags: - return - cursor = connection.cursor() - query = self.UPDATE_USED_COUNTS_QUERY % ','.join(['%s'] * len(tags)) - cursor.execute(query, [tag.id for tag in tags]) - transaction.commit_unless_managed() - - def get_tags_by_questions(self, questions): - question_ids = [] - for question in questions: - question_ids.append(question.id) - - question_ids_str = ','.join([str(id) for id in question_ids]) - related_tags = self.extra( - tables=['tag', 'question_tags'], - where=["tag.id = question_tags.tag_id AND question_tags.question_id IN (" + question_ids_str + ")"] - ).distinct() - - return related_tags - -class Tag(DeletableContent): - name = models.CharField(max_length=255, unique=True) - created_by = models.ForeignKey(User, related_name='created_tags') - # Denormalised data - used_count = models.PositiveIntegerField(default=0) - - objects = TagManager() - - class Meta(DeletableContent.Meta): - db_table = u'tag' - ordering = ('-used_count', 'name') - - def __unicode__(self): - return self.name - -class MarkedTag(models.Model): - TAG_MARK_REASONS = (('good',_('interesting')),('bad',_('ignored'))) - tag = models.ForeignKey('Tag', related_name='user_selections') - user = models.ForeignKey(User, related_name='tag_selections') - reason = models.CharField(max_length=16, choices=TAG_MARK_REASONS) - - class Meta: +from base import * + +from django.utils.translation import ugettext as _ + +class TagManager(models.Manager): + UPDATE_USED_COUNTS_QUERY = ( + 'UPDATE tag ' + 'SET used_count = (' + 'SELECT COUNT(*) FROM question_tags ' + 'INNER JOIN question ON question_id=question.id ' + 'WHERE tag_id = tag.id AND question.deleted=False' + ') ' + 'WHERE id IN (%s)') + + def get_valid_tags(self, page_size): + tags = self.all().filter(deleted=False).exclude(used_count=0).order_by("-id")[:page_size] + return tags + + def get_or_create_multiple(self, names, user): + """ + Fetches a list of Tags with the given names, creating any Tags + which don't exist when necesssary. + """ + tags = list(self.filter(name__in=names)) + #Set all these tag visible + for tag in tags: + if tag.deleted: + tag.deleted = False + tag.deleted_by = None + tag.deleted_at = None + tag.save() + + if len(tags) < len(names): + existing_names = set(tag.name for tag in tags) + new_names = [name for name in names if name not in existing_names] + tags.extend([self.create(name=name, created_by=user) + for name in new_names if self.filter(name=name).count() == 0 and len(name.strip()) > 0]) + + return tags + + def update_use_counts(self, tags): + """Updates the given Tags with their current use counts.""" + if not tags: + return + cursor = connection.cursor() + query = self.UPDATE_USED_COUNTS_QUERY % ','.join(['%s'] * len(tags)) + cursor.execute(query, [tag.id for tag in tags]) + transaction.commit_unless_managed() + + def get_tags_by_questions(self, questions): + question_ids = [] + for question in questions: + question_ids.append(question.id) + + question_ids_str = ','.join([str(id) for id in question_ids]) + related_tags = self.extra( + tables=['tag', 'question_tags'], + where=["tag.id = question_tags.tag_id AND question_tags.question_id IN (" + question_ids_str + ")"] + ).distinct() + + return related_tags + +class Tag(DeletableContent): + name = models.CharField(max_length=255, unique=True) + created_by = models.ForeignKey(User, related_name='created_tags') + # Denormalised data + used_count = models.PositiveIntegerField(default=0) + + objects = TagManager() + + class Meta(DeletableContent.Meta): + db_table = u'tag' + ordering = ('-used_count', 'name') + + def __unicode__(self): + return self.name + +class MarkedTag(models.Model): + TAG_MARK_REASONS = (('good',_('interesting')),('bad',_('ignored'))) + tag = models.ForeignKey('Tag', related_name='user_selections') + user = models.ForeignKey(User, related_name='tag_selections') + reason = models.CharField(max_length=16, choices=TAG_MARK_REASONS) + + class Meta: app_label = 'forum' \ No newline at end of file diff --git a/forum/models/user.py b/forum/models/user.py index 4e1a376d..ac6cbc0d 100755 --- a/forum/models/user.py +++ b/forum/models/user.py @@ -1,67 +1,67 @@ -from base import * - -from django.utils.translation import ugettext as _ - -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: - app_label = 'forum' - db_table = u'activity' - -class EmailFeedSetting(models.Model): - DELTA_TABLE = { - 'w':datetime.timedelta(7), - 'd':datetime.timedelta(1), - 'n':datetime.timedelta(-1), - } - FEED_TYPES = ( - ('q_all',_('Entire forum')), - ('q_ask',_('Questions that I asked')), - ('q_ans',_('Questions that I answered')), - ('q_sel',_('Individually selected questions')), - ) - UPDATE_FREQUENCY = ( - ('w',_('Weekly')), - ('d',_('Daily')), - ('n',_('No email')), - ) - subscriber = models.ForeignKey(User) - feed_type = models.CharField(max_length=16,choices=FEED_TYPES) - frequency = models.CharField(max_length=8,choices=UPDATE_FREQUENCY,default='n') - added_at = models.DateTimeField(auto_now_add=True) - reported_at = models.DateTimeField(null=True) - - def save(self,*args,**kwargs): - type = self.feed_type - subscriber = self.subscriber - similar = self.__class__.objects.filter(feed_type=type,subscriber=subscriber).exclude(pk=self.id) - if len(similar) > 0: - raise IntegrityError('email feed setting already exists') - super(EmailFeedSetting,self).save(*args,**kwargs) - - class Meta: - app_label = 'forum' - -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) - - class Meta: - app_label = 'forum' - - +from base import * + +from django.utils.translation import ugettext as _ + +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: + app_label = 'forum' + db_table = u'activity' + +class EmailFeedSetting(models.Model): + DELTA_TABLE = { + 'w':datetime.timedelta(7), + 'd':datetime.timedelta(1), + 'n':datetime.timedelta(-1), + } + FEED_TYPES = ( + ('q_all',_('Entire forum')), + ('q_ask',_('Questions that I asked')), + ('q_ans',_('Questions that I answered')), + ('q_sel',_('Individually selected questions')), + ) + UPDATE_FREQUENCY = ( + ('w',_('Weekly')), + ('d',_('Daily')), + ('n',_('No email')), + ) + subscriber = models.ForeignKey(User) + feed_type = models.CharField(max_length=16,choices=FEED_TYPES) + frequency = models.CharField(max_length=8,choices=UPDATE_FREQUENCY,default='n') + added_at = models.DateTimeField(auto_now_add=True) + reported_at = models.DateTimeField(null=True) + + def save(self,*args,**kwargs): + type = self.feed_type + subscriber = self.subscriber + similar = self.__class__.objects.filter(feed_type=type,subscriber=subscriber).exclude(pk=self.id) + if len(similar) > 0: + raise IntegrityError('email feed setting already exists') + super(EmailFeedSetting,self).save(*args,**kwargs) + + class Meta: + app_label = 'forum' + +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) + + class Meta: + app_label = 'forum' + + diff --git a/forum/modules.py b/forum/modules.py index 53cc619a..26c4d50a 100755 --- a/forum/modules.py +++ b/forum/modules.py @@ -1,54 +1,54 @@ -import os -import types - -MODULES_PACKAGE = 'forum_modules' - -MODULES_FOLDER = os.path.join(os.path.dirname(__file__), '../' + MODULES_PACKAGE) - -MODULE_LIST = [ - __import__('forum_modules.%s' % f, globals(), locals(), ['forum_modules']) - for f in os.listdir(MODULES_FOLDER) - if os.path.isdir(os.path.join(MODULES_FOLDER, f)) and - os.path.exists(os.path.join(MODULES_FOLDER, "%s/__init__.py" % f)) and - not os.path.exists(os.path.join(MODULES_FOLDER, "%s/DISABLED" % f)) -] - -def get_modules_script(script_name): - all = [] - - for m in MODULE_LIST: - try: - all.append(__import__('%s.%s' % (m.__name__, script_name), globals(), locals(), [m.__name__])) - except: - pass - - return all - -def get_modules_script_classes(script_name, base_class): - scripts = get_modules_script(script_name) - all_classes = {} - - for script in scripts: - all_classes.update(dict([ - (n, c) for (n, c) in [(n, getattr(script, n)) for n in dir(script)] - if isinstance(c, (type, types.ClassType)) and issubclass(c, base_class) - ])) - - return all_classes - -def get_all_handlers(name): - handler_files = get_modules_script('handlers') - - return [ - h for h in [ - getattr(f, name) for f in handler_files - if hasattr(f, name) - ] - - if callable(h) - ] - -def get_handler(name, default): - all = get_all_handlers(name) - print(len(all)) +import os +import types + +MODULES_PACKAGE = 'forum_modules' + +MODULES_FOLDER = os.path.join(os.path.dirname(__file__), '../' + MODULES_PACKAGE) + +MODULE_LIST = [ + __import__('forum_modules.%s' % f, globals(), locals(), ['forum_modules']) + for f in os.listdir(MODULES_FOLDER) + if os.path.isdir(os.path.join(MODULES_FOLDER, f)) and + os.path.exists(os.path.join(MODULES_FOLDER, "%s/__init__.py" % f)) and + not os.path.exists(os.path.join(MODULES_FOLDER, "%s/DISABLED" % f)) +] + +def get_modules_script(script_name): + all = [] + + for m in MODULE_LIST: + try: + all.append(__import__('%s.%s' % (m.__name__, script_name), globals(), locals(), [m.__name__])) + except: + pass + + return all + +def get_modules_script_classes(script_name, base_class): + scripts = get_modules_script(script_name) + all_classes = {} + + for script in scripts: + all_classes.update(dict([ + (n, c) for (n, c) in [(n, getattr(script, n)) for n in dir(script)] + if isinstance(c, (type, types.ClassType)) and issubclass(c, base_class) + ])) + + return all_classes + +def get_all_handlers(name): + handler_files = get_modules_script('handlers') + + return [ + h for h in [ + getattr(f, name) for f in handler_files + if hasattr(f, name) + ] + + if callable(h) + ] + +def get_handler(name, default): + all = get_all_handlers(name) + print(len(all)) return len(all) and all[0] or default \ No newline at end of file diff --git a/forum_modules/books/__init__.py b/forum_modules/books/__init__.py index bdd27b2b..a182c87c 100755 --- a/forum_modules/books/__init__.py +++ b/forum_modules/books/__init__.py @@ -1,3 +1,3 @@ -NAME = 'Osqa Books' -DESCRIPTION = "Allows discussion around books." -CAN_ENABLE = True +NAME = 'Osqa Books' +DESCRIPTION = "Allows discussion around books." +CAN_ENABLE = True diff --git a/forum_modules/books/models.py b/forum_modules/books/models.py index ecde34b3..a78c0e76 100755 --- a/forum_modules/books/models.py +++ b/forum_modules/books/models.py @@ -1,63 +1,63 @@ -from django.db import models -from django.contrib.auth.models import User -from forum.models import Question -from django.core.urlresolvers import reverse -from django.utils.http import urlquote as django_urlquote -from django.template.defaultfilters import slugify - -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 reverse('book', args=[django_urlquote(slugify(self.short_name))]) - - def __unicode__(self): - return self.title - - class Meta: - app_label = 'forum' - 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: - app_label = 'forum' - 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: - app_label = 'forum' +from django.db import models +from django.contrib.auth.models import User +from forum.models import Question +from django.core.urlresolvers import reverse +from django.utils.http import urlquote as django_urlquote +from django.template.defaultfilters import slugify + +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 reverse('book', args=[django_urlquote(slugify(self.short_name))]) + + def __unicode__(self): + return self.title + + class Meta: + app_label = 'forum' + 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: + app_label = 'forum' + 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: + app_label = 'forum' db_table = u'book_author_rss' \ No newline at end of file diff --git a/forum_modules/books/urls.py b/forum_modules/books/urls.py index 0e0432ba..bc0811e7 100755 --- a/forum_modules/books/urls.py +++ b/forum_modules/books/urls.py @@ -1,10 +1,10 @@ -from django.conf.urls.defaults import * -from django.utils.translation import ugettext as _ - -import views as app - -urlpatterns = patterns('', - url(r'^%s$' % _('books/'), app.books, name='books'), - url(r'^%s%s(?P[^/]+)/$' % (_('books/'), _('ask/')), app.ask_book, name='ask_book'), - url(r'^%s(?P[^/]+)/$' % _('books/'), app.book, name='book'), +from django.conf.urls.defaults import * +from django.utils.translation import ugettext as _ + +import views as app + +urlpatterns = patterns('', + url(r'^%s$' % _('books/'), app.books, name='books'), + url(r'^%s%s(?P[^/]+)/$' % (_('books/'), _('ask/')), app.ask_book, name='ask_book'), + url(r'^%s(?P[^/]+)/$' % _('books/'), app.book, name='book'), ) \ No newline at end of file diff --git a/forum_modules/books/views.py b/forum_modules/books/views.py index 31c82971..35e9f0fe 100755 --- a/forum_modules/books/views.py +++ b/forum_modules/books/views.py @@ -1,142 +1,142 @@ -from django.shortcuts import render_to_response, get_object_or_404 -from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden, Http404 -from django.template import RequestContext -from django.contrib.auth.decorators import login_required -from django.core.urlresolvers import reverse -from django.utils.html import * - -from models import * - -from forum.forms import AskForm -from forum.views.readers import _get_tags_cache_json -from forum.models import * -from forum.utils.html import sanitize_html - -def books(request): - return HttpResponseRedirect(reverse('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, - 'email_validation_faq_url': reverse('faq') + '#validate', +from django.shortcuts import render_to_response, get_object_or_404 +from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden, Http404 +from django.template import RequestContext +from django.contrib.auth.decorators import login_required +from django.core.urlresolvers import reverse +from django.utils.html import * + +from models import * + +from forum.forms import AskForm +from forum.views.readers import _get_tags_cache_json +from forum.models import * +from forum.utils.html import sanitize_html + +def books(request): + return HttpResponseRedirect(reverse('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, + 'email_validation_faq_url': reverse('faq') + '#validate', }, context_instance=RequestContext(request)) \ No newline at end of file diff --git a/forum_modules/pgfulltext/__init__.py b/forum_modules/pgfulltext/__init__.py index ec4892c7..8215e1a9 100755 --- a/forum_modules/pgfulltext/__init__.py +++ b/forum_modules/pgfulltext/__init__.py @@ -1,9 +1,9 @@ -NAME = 'Postgresql Full Text Search' -DESCRIPTION = "Enables PostgreSql full text search functionality." - -try: - import psycopg2 - CAN_ENABLE = True -except: - CAN_ENABLE = False +NAME = 'Postgresql Full Text Search' +DESCRIPTION = "Enables PostgreSql full text search functionality." + +try: + import psycopg2 + CAN_ENABLE = True +except: + CAN_ENABLE = False \ No newline at end of file diff --git a/forum_modules/pgfulltext/handlers.py b/forum_modules/pgfulltext/handlers.py index 17fb1762..f4a7a3b2 100755 --- a/forum_modules/pgfulltext/handlers.py +++ b/forum_modules/pgfulltext/handlers.py @@ -1,11 +1,11 @@ -from forum.models import Question - -def question_search(keywords, orderby): - return Question.objects.filter(deleted=False).extra( - select={ - 'ranking': "ts_rank_cd(tsv, plainto_tsquery(%s), 32)", - }, - where=["tsv @@ plainto_tsquery(%s)"], - params=[keywords], - select_params=[keywords] +from forum.models import Question + +def question_search(keywords, orderby): + return Question.objects.filter(deleted=False).extra( + select={ + 'ranking': "ts_rank_cd(tsv, plainto_tsquery(%s), 32)", + }, + where=["tsv @@ plainto_tsquery(%s)"], + params=[keywords], + select_params=[keywords] ).order_by(orderby, '-ranking') \ No newline at end of file diff --git a/forum_modules/pgfulltext/management.py b/forum_modules/pgfulltext/management.py index 89eb1395..487580ff 100755 --- a/forum_modules/pgfulltext/management.py +++ b/forum_modules/pgfulltext/management.py @@ -1,29 +1,29 @@ -import os - -from django.db import connection, transaction -from django.conf import settings - -import forum.models - -if settings.DATABASE_ENGINE in ('postgresql_psycopg2', 'postgresql', ): - from django.db.models.signals import post_syncdb - - def setup_pgfulltext(sender, **kwargs): - if sender == forum.models: - install_pg_fts() - - post_syncdb.connect(setup_pgfulltext) - -def install_pg_fts(): - f = open(os.path.join(os.path.dirname(__file__), 'pg_fts_install.sql'), 'r') - - try: - cursor = connection.cursor() - cursor.execute(f.read()) - transaction.commit_unless_managed() - except: - pass - finally: - cursor.close() - - f.close() +import os + +from django.db import connection, transaction +from django.conf import settings + +import forum.models + +if settings.DATABASE_ENGINE in ('postgresql_psycopg2', 'postgresql', ): + from django.db.models.signals import post_syncdb + + def setup_pgfulltext(sender, **kwargs): + if sender == forum.models: + install_pg_fts() + + post_syncdb.connect(setup_pgfulltext) + +def install_pg_fts(): + f = open(os.path.join(os.path.dirname(__file__), 'pg_fts_install.sql'), 'r') + + try: + cursor = connection.cursor() + cursor.execute(f.read()) + transaction.commit_unless_managed() + except: + pass + finally: + cursor.close() + + f.close() diff --git a/forum_modules/sphinxfulltext/dependencies.py b/forum_modules/sphinxfulltext/dependencies.py index 5ddb91d2..046ebfc5 100755 --- a/forum_modules/sphinxfulltext/dependencies.py +++ b/forum_modules/sphinxfulltext/dependencies.py @@ -1,2 +1,2 @@ -DJANGO_APPS = ('djangosphinx', ) - +DJANGO_APPS = ('djangosphinx', ) + diff --git a/forum_modules/sphinxfulltext/handlers.py b/forum_modules/sphinxfulltext/handlers.py index 665c9380..226acf72 100755 --- a/forum_modules/sphinxfulltext/handlers.py +++ b/forum_modules/sphinxfulltext/handlers.py @@ -1,4 +1,4 @@ -from forum.models import Question - -def question_search(keywords, orderby): +from forum.models import Question + +def question_search(keywords, orderby): return Question.search.query(keywords) \ No newline at end of file diff --git a/forum_modules/sphinxfulltext/models.py b/forum_modules/sphinxfulltext/models.py index 66b8ddf9..9db4aa86 100755 --- a/forum_modules/sphinxfulltext/models.py +++ b/forum_modules/sphinxfulltext/models.py @@ -1,10 +1,10 @@ -from forum.models import Question -from django.conf import settings -from djangosphinx.manager import SphinxSearch - - -Question.add_to_class('search', SphinxSearch( - index=' '.join(settings.SPHINX_SEARCH_INDICES), - mode='SPH_MATCH_ALL', - ) - ) +from forum.models import Question +from django.conf import settings +from djangosphinx.manager import SphinxSearch + + +Question.add_to_class('search', SphinxSearch( + index=' '.join(settings.SPHINX_SEARCH_INDICES), + mode='SPH_MATCH_ALL', + ) + ) diff --git a/forum_modules/sphinxfulltext/settings.py b/forum_modules/sphinxfulltext/settings.py index c98de7b3..7c2da124 100755 --- a/forum_modules/sphinxfulltext/settings.py +++ b/forum_modules/sphinxfulltext/settings.py @@ -1,5 +1,5 @@ -SPHINX_API_VERSION = 0x113 #refer to djangosphinx documentation -SPHINX_SEARCH_INDICES=('osqa',) #a tuple of index names remember about a comma after the -#last item, especially if you have just one :) -SPHINX_SERVER='localhost' -SPHINX_PORT=3312 +SPHINX_API_VERSION = 0x113 #refer to djangosphinx documentation +SPHINX_SEARCH_INDICES=('osqa',) #a tuple of index names remember about a comma after the +#last item, especially if you have just one :) +SPHINX_SERVER='localhost' +SPHINX_PORT=3312 diff --git a/settings_local.py.dist b/settings_local.py.dist index 3a71b389..52c54bbb 100755 --- a/settings_local.py.dist +++ b/settings_local.py.dist @@ -1,110 +1,110 @@ -# encoding:utf-8 -import os.path -from django.utils.translation import ugettext as _ - -def check_local_setting(name, value): - local_vars = locals() - if name in local_vars and local_vars[name] == value: - return True - else: - return False - -SITE_SRC_ROOT = os.path.dirname(__file__) -LOG_FILENAME = 'django.osqa.log' - -#for logging -import logging -logging.basicConfig( - filename=os.path.join(SITE_SRC_ROOT, 'log', LOG_FILENAME), - level=logging.DEBUG, - format='%(pathname)s TIME: %(asctime)s MSG: %(filename)s:%(funcName)s:%(lineno)d %(message)s', -) - -#ADMINS and MANAGERS -ADMINS = (('Forum Admin', 'forum@example.com'),) -MANAGERS = ADMINS - -#DEBUG SETTINGS -DEBUG = False -TEMPLATE_DEBUG = DEBUG -INTERNAL_IPS = ('127.0.0.1',) - -DATABASE_NAME = '' # Or path to database file if using sqlite3. -DATABASE_USER = '' # Not used with sqlite3. -DATABASE_PASSWORD = '' # Not used with sqlite3. -DATABASE_ENGINE = '' #mysql, etc -DATABASE_HOST = '' -DATABASE_PORT = '' - -#Moved from settings.py for better organization. (please check it up to clean up settings.py) - -#email server settings -SERVER_EMAIL = '' -DEFAULT_FROM_EMAIL = '' -EMAIL_HOST_USER = '' -EMAIL_HOST_PASSWORD = '' -EMAIL_SUBJECT_PREFIX = '[OSQA] ' -EMAIL_HOST='osqa.net' -EMAIL_PORT='25' -EMAIL_USE_TLS=False - -#LOCALIZATIONS -TIME_ZONE = 'America/New_York' - -########################### -# -# this will allow running your forum with url like http://site.com/forum -# -# FORUM_SCRIPT_ALIAS = 'forum/' -# -FORUM_SCRIPT_ALIAS = '' #no leading slash, default = '' empty string - - -#OTHER SETTINGS -APP_TITLE = u'OSQA: Open Source Q&A Forum' -APP_SHORT_NAME = u'OSQA' -APP_KEYWORDS = u'OSQA,CNPROG,forum,community' -APP_DESCRIPTION = u'Ask and answer questions.' -APP_INTRO = u'

    Ask and answer questions, make the world better!

    ' -APP_COPYRIGHT = 'Copyright OSQA, 2009. Some rights reserved under creative commons license.' -LOGIN_URL = '/%s%s%s' % (FORUM_SCRIPT_ALIAS,'account/','signin/') -GREETING_URL = LOGIN_URL #may be url of "faq" page or "about", etc - -USE_I18N = True -LANGUAGE_CODE = 'en' -EMAIL_VALIDATION = 'off' #string - on|off -MIN_USERNAME_LENGTH = 1 -EMAIL_UNIQUE = False -APP_URL = 'http://osqa.net' #used by email notif system and RSS -GOOGLE_SITEMAP_CODE = '' -GOOGLE_ANALYTICS_KEY = '' -BOOKS_ON = False -WIKI_ON = True -USE_EXTERNAL_LEGACY_LOGIN = False -EXTERNAL_LEGACY_LOGIN_HOST = 'login.osqa.net' -EXTERNAL_LEGACY_LOGIN_PORT = 80 -EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME = 'OSQA' -FEEDBACK_SITE_URL = None #None or url -EDITABLE_SCREEN_NAME = False #True or False - can user change screen name? - -DJANGO_VERSION = 1.1 -RESOURCE_REVISION=4 - -USE_SPHINX_SEARCH = False #if True all SPHINX_* settings are required -#also sphinx search engine and djangosphinxs app must be installed -#sample sphinx configuration file is /sphinx/sphinx.conf -SPHINX_API_VERSION = 0x113 #refer to djangosphinx documentation -SPHINX_SEARCH_INDICES=('osqa',) #a tuple of index names remember about a comma after the -#last item, especially if you have just one :) -SPHINX_SERVER='localhost' -SPHINX_PORT=3312 - -#please get these at recaptcha.net -RECAPTCHA_PRIVATE_KEY='...' -RECAPTCHA_PUBLIC_KEY='...' -OSQA_DEFAULT_SKIN = 'default' - -#Facebook settings -USE_FB_CONNECT=False -FB_API_KEY='' #your api key from facebook -FB_SECRET='' #your application secret +# encoding:utf-8 +import os.path +from django.utils.translation import ugettext as _ + +def check_local_setting(name, value): + local_vars = locals() + if name in local_vars and local_vars[name] == value: + return True + else: + return False + +SITE_SRC_ROOT = os.path.dirname(__file__) +LOG_FILENAME = 'django.osqa.log' + +#for logging +import logging +logging.basicConfig( + filename=os.path.join(SITE_SRC_ROOT, 'log', LOG_FILENAME), + level=logging.DEBUG, + format='%(pathname)s TIME: %(asctime)s MSG: %(filename)s:%(funcName)s:%(lineno)d %(message)s', +) + +#ADMINS and MANAGERS +ADMINS = (('Forum Admin', 'forum@example.com'),) +MANAGERS = ADMINS + +#DEBUG SETTINGS +DEBUG = False +TEMPLATE_DEBUG = DEBUG +INTERNAL_IPS = ('127.0.0.1',) + +DATABASE_NAME = '' # Or path to database file if using sqlite3. +DATABASE_USER = '' # Not used with sqlite3. +DATABASE_PASSWORD = '' # Not used with sqlite3. +DATABASE_ENGINE = '' #mysql, etc +DATABASE_HOST = '' +DATABASE_PORT = '' + +#Moved from settings.py for better organization. (please check it up to clean up settings.py) + +#email server settings +SERVER_EMAIL = '' +DEFAULT_FROM_EMAIL = '' +EMAIL_HOST_USER = '' +EMAIL_HOST_PASSWORD = '' +EMAIL_SUBJECT_PREFIX = '[OSQA] ' +EMAIL_HOST='osqa.net' +EMAIL_PORT='25' +EMAIL_USE_TLS=False + +#LOCALIZATIONS +TIME_ZONE = 'America/New_York' + +########################### +# +# this will allow running your forum with url like http://site.com/forum +# +# FORUM_SCRIPT_ALIAS = 'forum/' +# +FORUM_SCRIPT_ALIAS = '' #no leading slash, default = '' empty string + + +#OTHER SETTINGS +APP_TITLE = u'OSQA: Open Source Q&A Forum' +APP_SHORT_NAME = u'OSQA' +APP_KEYWORDS = u'OSQA,CNPROG,forum,community' +APP_DESCRIPTION = u'Ask and answer questions.' +APP_INTRO = u'

    Ask and answer questions, make the world better!

    ' +APP_COPYRIGHT = 'Copyright OSQA, 2009. Some rights reserved under creative commons license.' +LOGIN_URL = '/%s%s%s' % (FORUM_SCRIPT_ALIAS,'account/','signin/') +GREETING_URL = LOGIN_URL #may be url of "faq" page or "about", etc + +USE_I18N = True +LANGUAGE_CODE = 'en' +EMAIL_VALIDATION = 'off' #string - on|off +MIN_USERNAME_LENGTH = 1 +EMAIL_UNIQUE = False +APP_URL = 'http://osqa.net' #used by email notif system and RSS +GOOGLE_SITEMAP_CODE = '' +GOOGLE_ANALYTICS_KEY = '' +BOOKS_ON = False +WIKI_ON = True +USE_EXTERNAL_LEGACY_LOGIN = False +EXTERNAL_LEGACY_LOGIN_HOST = 'login.osqa.net' +EXTERNAL_LEGACY_LOGIN_PORT = 80 +EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME = 'OSQA' +FEEDBACK_SITE_URL = None #None or url +EDITABLE_SCREEN_NAME = False #True or False - can user change screen name? + +DJANGO_VERSION = 1.1 +RESOURCE_REVISION=4 + +USE_SPHINX_SEARCH = False #if True all SPHINX_* settings are required +#also sphinx search engine and djangosphinxs app must be installed +#sample sphinx configuration file is /sphinx/sphinx.conf +SPHINX_API_VERSION = 0x113 #refer to djangosphinx documentation +SPHINX_SEARCH_INDICES=('osqa',) #a tuple of index names remember about a comma after the +#last item, especially if you have just one :) +SPHINX_SERVER='localhost' +SPHINX_PORT=3312 + +#please get these at recaptcha.net +RECAPTCHA_PRIVATE_KEY='...' +RECAPTCHA_PUBLIC_KEY='...' +OSQA_DEFAULT_SKIN = 'default' + +#Facebook settings +USE_FB_CONNECT=False +FB_API_KEY='' #your api key from facebook +FB_SECRET='' #your application secret -- cgit v1.2.3-1-g7c22