diff options
author | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2010-05-23 18:31:11 -0400 |
---|---|---|
committer | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2010-05-23 18:31:11 -0400 |
commit | 2eaa3c6a104fd5e598fc25084f8c2ecc6493ecee (patch) | |
tree | 4e47f9045305474b4f215158478dbca8ae4d4abd | |
parent | 478178abdb4e1ca08b954cb35d900fe25f91790f (diff) | |
download | askbot-2eaa3c6a104fd5e598fc25084f8c2ecc6493ecee.tar.gz askbot-2eaa3c6a104fd5e598fc25084f8c2ecc6493ecee.tar.bz2 askbot-2eaa3c6a104fd5e598fc25084f8c2ecc6493ecee.zip |
merged mentions in to activity model
-rw-r--r-- | forum/const/__init__.py | 9 | ||||
-rw-r--r-- | forum/management/commands/multi_award_badges.py | 79 | ||||
-rw-r--r-- | forum/management/commands/once_award_badges.py | 99 | ||||
-rw-r--r-- | forum/management/commands/send_email_alerts.py | 17 | ||||
-rw-r--r-- | forum/migrations/0009_calculate_html_field_for_comments.py | 2 | ||||
-rw-r--r-- | forum/migrations/0011_merge_mentions_into_activity.py | 376 | ||||
-rw-r--r-- | forum/models/__init__.py | 41 | ||||
-rw-r--r-- | forum/models/answer.py | 6 | ||||
-rw-r--r-- | forum/models/base.py | 29 | ||||
-rw-r--r-- | forum/models/meta.py | 17 | ||||
-rw-r--r-- | forum/models/question.py | 9 | ||||
-rw-r--r-- | forum/models/user.py | 213 | ||||
-rw-r--r-- | forum/skins/default/templates/instant_notification.html | 46 | ||||
-rw-r--r-- | forum/skins/default/templates/questions.html | 6 | ||||
-rw-r--r-- | forum/utils/functions.py | 12 | ||||
-rw-r--r-- | forum/utils/markup.py | 43 | ||||
-rw-r--r-- | forum/views/users.py | 158 | ||||
-rw-r--r-- | locale/en/LC_MESSAGES/django.mo | bin | 23857 -> 24018 bytes | |||
-rw-r--r-- | locale/en/LC_MESSAGES/django.po | 215 |
19 files changed, 1008 insertions, 369 deletions
diff --git a/forum/const/__init__.py b/forum/const/__init__.py index 5419d70a..3e4dedef 100644 --- a/forum/const/__init__.py +++ b/forum/const/__init__.py @@ -119,7 +119,7 @@ TYPE_ACTIVITY = ( ) #response activity has receiving user not empty -RESPONSE_ACTIVITY_TYPES = ( +RESPONSE_ACTIVITY_TYPES_FOR_DISPLAY = ( TYPE_ACTIVITY_ANSWER, TYPE_ACTIVITY_COMMENT_QUESTION, TYPE_ACTIVITY_COMMENT_ANSWER, @@ -137,6 +137,13 @@ RESPONSE_ACTIVITY_TYPES = ( TYPE_ACTIVITY_FAVORITE, ) +RESPONSE_ACTIVITY_TYPES_FOR_EMAIL = ( + TYPE_ACTIVITY_COMMENT_QUESTION, + TYPE_ACTIVITY_COMMENT_ANSWER, + TYPE_ACTIVITY_UPDATE_ANSWER, + TYPE_ACTIVITY_UPDATE_QUESTION, +) + TYPE_RESPONSE = { 'QUESTION_ANSWERED' : _('question_answered'), 'QUESTION_COMMENTED': _('question_commented'), diff --git a/forum/management/commands/multi_award_badges.py b/forum/management/commands/multi_award_badges.py index 6b330cf9..8d8e0065 100644 --- a/forum/management/commands/multi_award_badges.py +++ b/forum/management/commands/multi_award_badges.py @@ -18,66 +18,9 @@ from django.db import connection from django.shortcuts import get_object_or_404 from django.contrib.contenttypes.models import ContentType -from forum.models import * -from forum.const import * +from forum.models import Badge, User, Award, Question, Answer +from forum import const from base_command import BaseCommand -""" -(1, '炼狱法师', 3, '炼狱法师', '删除自己有3个以上赞成票的帖子', 1, 0), -(2, '压力白领', 3, '压力白领', '删除自己有3个以上反对票的帖子', 1, 0), -(3, '优秀回答', 3, '优秀回答', '回答好评10次以上', 1, 0), -(4, '优秀问题', 3, '优秀问题', '问题好评10次以上', 1, 0), -(5, '评论家', 3, '评论家', '评论10次以上', 0, 0), -(6, '流行问题', 3, '流行问题', '问题的浏览量超过1000人次', 1, 0), -(7, '巡逻兵', 3, '巡逻兵', '第一次标记垃圾帖子', 0, 0), -(8, '清洁工', 3, '清洁工', '第一次撤销投票', 0, 0), -(9, '批评家', 3, '批评家', '第一次反对票', 0, 0), -(10, '小编', 3, '小编', '第一次编辑更新', 0, 0), -(11, '村长', 3, '村长', '第一次重新标签', 0, 0), -(12, '学者', 3, '学者', '第一次标记答案', 0, 0), -(13, '学生', 3, '学生', '第一次提问并且有一次以上赞成票', 0, 0), -(14, '支持者', 3, '支持者', '第一次赞成票', 0, 0), -(15, '教师', 3, '教师', '第一次回答问题并且得到一个以上赞成票', 0, 0), -(16, '自传作者', 3, '自传作者', '完整填写用户资料所有选项', 0, 0), -(17, '自学成才', 3, '自学成才', '回答自己的问题并且有3个以上赞成票', 1, 0), -(18, '最有价值回答', 1, '最有价值回答', '回答超过100次赞成票', 1, 0), -(19, '最有价值问题', 1, '最有价值问题', '问题超过100次赞成票', 1, 0), -(20, '万人迷', 1, '万人迷', '问题被100人以上收藏', 1, 0), -(21, '著名问题', 1, '著名问题', '问题的浏览量超过10000人次', 1, 0), -(22, 'alpha用户', 2, 'alpha用户', '内测期间的活跃用户', 0, 0), -(23, '极好回答', 2, '极好回答', '回答超过25次赞成票', 1, 0), -(24, '极好问题', 2, '极好问题', '问题超过25次赞成票', 1, 0), -(25, '受欢迎问题', 2, '受欢迎问题', '问题被25人以上收藏', 1, 0), -(26, '优秀市民', 2, '优秀市民', '投票300次以上', 0, 0), -(27, '编辑主任', 2, '编辑主任', '编辑了100个帖子', 0, 0), -(28, '通才', 2, '通才', '在多个标签领域活跃', 0, 0), -(29, '专家', 2, '专家', '在一个标签领域活跃出众', 0, 0), -(30, '老鸟', 2, '老鸟', '活跃超过一年的用户', 0, 0), -(31, '最受关注问题', 2, '最受关注问题', '问题的浏览量超过2500人次', 1, 0), -(32, '学问家', 2, '学问家', '第一次回答被投赞成票10次以上', 0, 0), -(33, 'beta用户', 2, 'beta用户', 'beta期间活跃参与', 0, 0), -(34, '导师', 2, '导师', '被指定为最佳答案并且赞成票40以上', 1, 0), -(35, '巫师', 2, '巫师', '在提问60天之后回答并且赞成票5次以上', 1, 0), -(36, '分类专家', 2, '分类专家', '创建的标签被50个以上问题使用', 1, 0); - - -TYPE_ACTIVITY_ASK_QUESTION=1 -TYPE_ACTIVITY_ANSWER=2 -TYPE_ACTIVITY_COMMENT_QUESTION=3 -TYPE_ACTIVITY_COMMENT_ANSWER=4 -TYPE_ACTIVITY_UPDATE_QUESTION=5 -TYPE_ACTIVITY_UPDATE_ANSWER=6 -TYPE_ACTIVITY_PRIZE=7 -TYPE_ACTIVITY_MARK_ANSWER=8 -TYPE_ACTIVITY_VOTE_UP=9 -TYPE_ACTIVITY_VOTE_DOWN=10 -TYPE_ACTIVITY_CANCEL_VOTE=11 -TYPE_ACTIVITY_DELETE_QUESTION=12 -TYPE_ACTIVITY_DELETE_ANSWER=13 -TYPE_ACTIVITY_MARK_OFFENSIVE=14 -TYPE_ACTIVITY_UPDATE_TAGS=15 -TYPE_ACTIVITY_FAVORITE=16 -TYPE_ACTIVITY_USER_FULL_UPDATED = 17 -""" class Command(BaseCommand): def handle_noargs(self, **options): @@ -114,7 +57,7 @@ class Command(BaseCommand): query = "SELECT act.id, act.user_id, act.object_id FROM activity act, question q WHERE act.object_id = q.id AND\ act.activity_type = %s AND\ q.vote_up_count >=3 AND \ - act.is_auditted = 0" % (TYPE_ACTIVITY_DELETE_QUESTION) + act.is_auditted = 0" % (const.TYPE_ACTIVITY_DELETE_QUESTION) self.__process_activities_badge(query, 1, Question) def delete_answer_be_voted_up_3(self): @@ -124,7 +67,7 @@ class Command(BaseCommand): query = "SELECT act.id, act.user_id, act.object_id FROM activity act, answer an WHERE act.object_id = an.id AND\ act.activity_type = %s AND\ an.vote_up_count >=3 AND \ - act.is_auditted = 0" % (TYPE_ACTIVITY_DELETE_ANSWER) + act.is_auditted = 0" % (const.TYPE_ACTIVITY_DELETE_ANSWER) self.__process_activities_badge(query, 1, Answer) def delete_question_be_vote_down_3(self): @@ -134,7 +77,7 @@ class Command(BaseCommand): query = "SELECT act.id, act.user_id, act.object_id FROM activity act, question q WHERE act.object_id = q.id AND\ act.activity_type = %s AND\ q.vote_down_count >=3 AND \ - act.is_auditted = 0" % (TYPE_ACTIVITY_DELETE_QUESTION) + act.is_auditted = 0" % (const.TYPE_ACTIVITY_DELETE_QUESTION) content_type = ContentType.objects.get_for_model(Question) self.__process_activities_badge(query, 2, Question) @@ -145,7 +88,7 @@ class Command(BaseCommand): query = "SELECT act.id, act.user_id, act.object_id FROM activity act, answer an WHERE act.object_id = an.id AND\ act.activity_type = %s AND\ an.vote_down_count >=3 AND \ - act.is_auditted = 0" % (TYPE_ACTIVITY_DELETE_ANSWER) + act.is_auditted = 0" % (const.TYPE_ACTIVITY_DELETE_ANSWER) self.__process_activities_badge(query, 2, Answer) def answer_be_voted_up_10(self): @@ -156,7 +99,7 @@ class Command(BaseCommand): activity act, answer a WHERE act.object_id = a.id AND\ act.activity_type = %s AND \ a.vote_up_count >= 10 AND\ - act.is_auditted = 0" % (TYPE_ACTIVITY_ANSWER) + act.is_auditted = 0" % (const.TYPE_ACTIVITY_ANSWER) self.__process_activities_badge(query, 3, Answer) def question_be_voted_up_10(self): @@ -167,7 +110,7 @@ class Command(BaseCommand): activity act, question q WHERE act.object_id = q.id AND\ act.activity_type = %s AND \ q.vote_up_count >= 10 AND\ - act.is_auditted = 0" % (TYPE_ACTIVITY_ASK_QUESTION) + act.is_auditted = 0" % (const.TYPE_ACTIVITY_ASK_QUESTION) self.__process_activities_badge(query, 4, Question) def question_view_1000(self): @@ -179,7 +122,8 @@ class Command(BaseCommand): act.object_id = q.id AND \ q.view_count >= 1000 AND\ act.object_id NOT IN \ - (SELECT object_id FROM award WHERE award.badge_id = %s)" % (TYPE_ACTIVITY_ASK_QUESTION, 6) + (SELECT object_id FROM award WHERE award.badge_id = %s)" \ + % (const.TYPE_ACTIVITY_ASK_QUESTION, 6) self.__process_activities_badge(query, 6, Question, False) def answer_self_question_be_voted_up_3(self): @@ -192,7 +136,8 @@ class Command(BaseCommand): an.vote_up_count >= 3 AND\ act.user_id = (SELECT user_id FROM question q WHERE q.id = an.question_id) AND\ act.object_id NOT IN \ - (SELECT object_id FROM award WHERE award.badge_id = %s)" % (TYPE_ACTIVITY_ANSWER, 17) + (SELECT object_id FROM award WHERE award.badge_id = %s)" \ + % (const.TYPE_ACTIVITY_ANSWER, 17) self.__process_activities_badge(query, 17, Question, False) def answer_be_voted_up_100(self): diff --git a/forum/management/commands/once_award_badges.py b/forum/management/commands/once_award_badges.py index 60457373..1d403da5 100644 --- a/forum/management/commands/once_award_badges.py +++ b/forum/management/commands/once_award_badges.py @@ -17,8 +17,8 @@ from django.db import connection from django.shortcuts import get_object_or_404 from django.contrib.contenttypes.models import ContentType -from forum.models import * -from forum.const import * +from forum.models import User, Activity, Badge, Award, Question, Answer +from forum import const from base_command import BaseCommand """ (1, '炼狱法师', 3, '炼狱法师', '删除自己有3个以上赞成票的帖子', 1, 0), @@ -79,15 +79,15 @@ TYPE_ACTIVITY_USER_FULL_UPDATED = 17 """ BADGE_AWARD_TYPE_FIRST = { - TYPE_ACTIVITY_MARK_OFFENSIVE : 7, - TYPE_ACTIVITY_CANCEL_VOTE: 8, - TYPE_ACTIVITY_VOTE_DOWN : 9, - TYPE_ACTIVITY_UPDATE_QUESTION : 10, - TYPE_ACTIVITY_UPDATE_ANSWER : 10, - TYPE_ACTIVITY_UPDATE_TAGS : 11, - TYPE_ACTIVITY_MARK_ANSWER : 12, - TYPE_ACTIVITY_VOTE_UP : 14, - TYPE_ACTIVITY_USER_FULL_UPDATED: 16 + const.TYPE_ACTIVITY_MARK_OFFENSIVE : 7, + const.TYPE_ACTIVITY_CANCEL_VOTE: 8, + const.TYPE_ACTIVITY_VOTE_DOWN : 9, + const.TYPE_ACTIVITY_UPDATE_QUESTION : 10, + const.TYPE_ACTIVITY_UPDATE_ANSWER : 10, + const.TYPE_ACTIVITY_UPDATE_TAGS : 11, + const.TYPE_ACTIVITY_MARK_ANSWER : 12, + const.TYPE_ACTIVITY_VOTE_UP : 14, + const.TYPE_ACTIVITY_USER_FULL_UPDATED: 16 } @@ -192,10 +192,12 @@ class Command(BaseCommand): """ For user asked question and got first upvote, we award him following badge: """ - query = "SELECT act.user_id, q.vote_up_count, act.object_id FROM " \ - "activity act, question q WHERE act.activity_type = %s AND " \ - "act.object_id = q.id AND " \ - "act.user_id NOT IN (SELECT distinct user_id FROM award WHERE badge_id = %s)" % (TYPE_ACTIVITY_ASK_QUESTION, 13) + query = "SELECT act.user_id, q.vote_up_count, act.object_id FROM " + "activity act, question q WHERE act.activity_type = %s AND " + "act.object_id = q.id AND " + "act.user_id NOT IN " + "(SELECT distinct user_id FROM award WHERE badge_id = %s)"\ + % (const.TYPE_ACTIVITY_ASK_QUESTION, 13) cursor = connection.cursor() try: cursor.execute(query) @@ -222,10 +224,12 @@ class Command(BaseCommand): (15, '教师', 3, '教师', '第一次回答问题并且得到一个以上赞成票', 0, 0), """ - query = "SELECT act.user_id, a.vote_up_count, act.object_id FROM " \ - "activity act, answer a WHERE act.activity_type = %s AND " \ - "act.object_id = a.id AND " \ - "act.user_id NOT IN (SELECT distinct user_id FROM award WHERE badge_id = %s)" % (TYPE_ACTIVITY_ANSWER, 15) + query = "SELECT act.user_id, a.vote_up_count, act.object_id FROM " + "activity act, answer a WHERE act.activity_type = %s AND " + "act.object_id = a.id AND " + "act.user_id NOT IN " + "(SELECT distinct user_id FROM award WHERE badge_id = %s)"\ + % (const.TYPE_ACTIVITY_ANSWER, 15) cursor = connection.cursor() try: cursor.execute(query) @@ -250,11 +254,13 @@ class Command(BaseCommand): """ (32, '学问家', 2, '学问家', '第一次回答被投赞成票10次以上', 0, 0) """ - query = "SELECT act.user_id, act.object_id FROM " \ - "activity act, answer a WHERE act.object_id = a.id AND " \ - "act.activity_type = %s AND " \ - "a.vote_up_count >= 10 AND " \ - "act.user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s)" % (TYPE_ACTIVITY_ANSWER, 32) + query = "SELECT act.user_id, act.object_id FROM " + "activity act, answer a WHERE act.object_id = a.id AND " + "act.activity_type = %s AND " + "a.vote_up_count >= 10 AND " + "act.user_id NOT IN " + "(SELECT user_id FROM award WHERE badge_id = %s)"\ + % (const.TYPE_ACTIVITY_ANSWER, 32) cursor = connection.cursor() try: cursor.execute(query) @@ -278,11 +284,16 @@ class Command(BaseCommand): """ (26, '优秀市民', 2, '优秀市民', '投票300次以上', 0, 0) """ - query = "SELECT count(*) vote_count, user_id FROM activity WHERE " \ - "activity_type = %s OR " \ - "activity_type = %s AND " \ - "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " \ - "GROUP BY user_id HAVING vote_count >= 300" % (TYPE_ACTIVITY_VOTE_UP, TYPE_ACTIVITY_VOTE_DOWN, 26) + query = "SELECT count(*) vote_count, user_id FROM activity WHERE " + "activity_type = %s OR " + "activity_type = %s AND " + "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " + "GROUP BY user_id HAVING vote_count >= 300"\ + % ( + const.TYPE_ACTIVITY_VOTE_UP, + const.TYPE_ACTIVITY_VOTE_DOWN, + 2 + ) self.__award_for_count_num(query, 26) @@ -290,11 +301,16 @@ class Command(BaseCommand): """ (27, '编辑主任', 2, '编辑主任', '编辑了100个帖子', 0, 0) """ - query = "SELECT count(*) vote_count, user_id FROM activity WHERE " \ - "activity_type = %s OR " \ - "activity_type = %s AND " \ - "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " \ - "GROUP BY user_id HAVING vote_count >= 100" % (TYPE_ACTIVITY_UPDATE_QUESTION, TYPE_ACTIVITY_UPDATE_ANSWER, 27) + query = "SELECT count(*) vote_count, user_id FROM activity WHERE " + "activity_type = %s OR " + "activity_type = %s AND " + "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " + "GROUP BY user_id HAVING vote_count >= 100"\ + % ( + const.TYPE_ACTIVITY_UPDATE_QUESTION, + const.TYPE_ACTIVITY_UPDATE_ANSWER, + 27 + ) self.__award_for_count_num(query, 27) @@ -302,11 +318,16 @@ class Command(BaseCommand): """ (5, '评论家', 3, '评论家', '评论10次以上', 0, 0), """ - query = "SELECT count(*) vote_count, user_id FROM activity WHERE " \ - "activity_type = %s OR " \ - "activity_type = %s AND " \ - "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " \ - "GROUP BY user_id HAVING vote_count >= 10" % (TYPE_ACTIVITY_COMMENT_QUESTION, TYPE_ACTIVITY_COMMENT_ANSWER, 5) + query = "SELECT count(*) vote_count, user_id FROM activity WHERE " + "activity_type = %s OR " + "activity_type = %s AND " + "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " + "GROUP BY user_id HAVING vote_count >= 10"\ + % ( + const.TYPE_ACTIVITY_COMMENT_QUESTION, + const.TYPE_ACTIVITY_COMMENT_ANSWER, + 5 + ) self.__award_for_count_num(query, 5) def __award_for_count_num(self, query, badge): diff --git a/forum/management/commands/send_email_alerts.py b/forum/management/commands/send_email_alerts.py index 5d23a8a4..75a2e7cb 100644 --- a/forum/management/commands/send_email_alerts.py +++ b/forum/management/commands/send_email_alerts.py @@ -240,12 +240,12 @@ class Command(NoArgsCommand): else: meta_data['comments'] = 1 - mentions = Mention.objects.filter( + mentions = Activity.objects.get_mentions( mentioned_at__gt = cutoff_time, mentioned_whom = user ) - q_mentions_id = [q.id for q in mentions.get_question_list()] + q_mentions_id = [q.id for q in mentions.get_all_origin_posts()] q_mentions_A = Q_set_A.filter(id__in, q_mentions_id) q_mentions_A.cutoff_time = cutoff_time @@ -453,12 +453,11 @@ class Command(NoArgsCommand): link = url_prefix + user.get_profile_url() + '?sort=email_subscriptions' text += _('go to %(link)s to change frequency of email updates or %(email)s administrator') \ % {'link':link, 'email':settings.ADMINS[0][1]} - msg = EmailMessage(subject, text, settings.DEFAULT_FROM_EMAIL, [user.email]) - msg.content_subtype = 'html' if DEBUG_THIS_COMMAND == False: + msg = EmailMessage(subject, text, settings.DEFAULT_FROM_EMAIL, [user.email]) + msg.content_subtype = 'html' msg.send() - #uncomment lines below to get copies of emails sent to others - #todo: maybe some debug setting would be appropriate here - #msg2 = EmailMessage(subject, text, settings.DEFAULT_FROM_EMAIL, ['your@email.com']) - #msg2.content_subtype = 'html' - #msg2.send() + else: + msg2 = EmailMessage(subject, text, settings.DEFAULT_FROM_EMAIL, ['your@email.com']) + msg2.content_subtype = 'html' + msg2.send() diff --git a/forum/migrations/0009_calculate_html_field_for_comments.py b/forum/migrations/0009_calculate_html_field_for_comments.py index f857fae4..2a8770d0 100644 --- a/forum/migrations/0009_calculate_html_field_for_comments.py +++ b/forum/migrations/0009_calculate_html_field_for_comments.py @@ -44,7 +44,7 @@ def get_absolute_url(user): 'slug': slugify(user.username) } ) - return '<a href="%s">@%s</a>' % (url, user.username) + return u'<a href="%s">@%s</a>' % (url, user.username) def get_comment_continuation(matching_author): if matching_author: diff --git a/forum/migrations/0011_merge_mentions_into_activity.py b/forum/migrations/0011_merge_mentions_into_activity.py new file mode 100644 index 00000000..e4385841 --- /dev/null +++ b/forum/migrations/0011_merge_mentions_into_activity.py @@ -0,0 +1,376 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models +from forum import const + +class Migration(DataMigration): + + def forwards(self, orm): + "Write your forwards methods here." + for m in orm['forum.Mention'].objects.all(): + a = orm['forum.Activity']() + a.activity_type = const.TYPE_ACTIVITY_MENTION + a.user = m.mentioned_by + a.active_at = m.mentioned_at + a.object_id = m.object_id + a.content_type = m.content_type + a.save() + a.receiving_users.add(m.mentioned_whom) + m.delete() + + def backwards(self, orm): + "Write your backwards methods here." + m_type = const.TYPE_ACTIVITY_MENTION + for a in orm['forum.Activity'].objects.filter(activity_type = m_type): + m = orm['forum.Mention']() + m.mentioned_by = a.user + m.mentioned_whom = a.receiving_users.all()[0] + m.mentioned_at = a.active_at + m.content_type = a.content_type + m.object_id = a.object_id + a.delete() + m.save() + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}), + 'hide_ignored_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'tag_filter_setting': ('django.db.models.fields.CharField', [], {'default': "'ignored'", 'max_length': '16'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'forum.activity': { + 'Meta': {'object_name': 'Activity', 'db_table': "u'activity'"}, + 'active_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'activity_type': ('django.db.models.fields.SmallIntegerField', [], {}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_auditted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'receiving_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'received_activity'", 'to': "orm['auth.User']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'forum.anonymousanswer': { + 'Meta': {'object_name': 'AnonymousAnswer'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'anonymous_answers'", 'to': "orm['forum.Question']"}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}) + }, + 'forum.anonymousquestion': { + 'Meta': {'object_name': 'AnonymousQuestion'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}) + }, + 'forum.answer': { + 'Meta': {'object_name': 'Answer', 'db_table': "u'answer'"}, + 'accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['auth.User']"}), + 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_answers'", 'null': 'True', 'to': "orm['auth.User']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_edited_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_edited_answers'", 'null': 'True', 'to': "orm['auth.User']"}), + 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_answers'", 'null': 'True', 'to': "orm['auth.User']"}), + 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['forum.Question']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'vote_down_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'vote_up_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'forum.answerrevision': { + 'Meta': {'object_name': 'AnswerRevision', 'db_table': "u'answer_revision'"}, + 'answer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Answer']"}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answerrevisions'", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {}) + }, + 'forum.authkeyuserassociation': { + 'Meta': {'object_name': 'AuthKeyUserAssociation'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['auth.User']"}) + }, + 'forum.award': { + 'Meta': {'object_name': 'Award', 'db_table': "u'award'"}, + 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['forum.Badge']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"}) + }, + 'forum.badge': { + 'Meta': {'unique_together': "(('name', 'type'),)", 'object_name': 'Badge', 'db_table': "u'badge'"}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['auth.User']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}) + }, + 'forum.book': { + 'Meta': {'object_name': 'Book', 'db_table': "u'book'"}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {}), + 'author': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'cover_img': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {}), + 'pages': ('django.db.models.fields.SmallIntegerField', [], {}), + 'price': ('django.db.models.fields.DecimalField', [], {'max_digits': '6', 'decimal_places': '2'}), + 'publication': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'published_at': ('django.db.models.fields.DateTimeField', [], {}), + 'questions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'book'", 'db_table': "'book_question'", 'to': "orm['forum.Question']"}), + 'short_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'forum.bookauthorinfo': { + 'Meta': {'object_name': 'BookAuthorInfo', 'db_table': "u'book_author_info'"}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {}), + 'blog_url': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Book']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'forum.bookauthorrss': { + 'Meta': {'object_name': 'BookAuthorRss', 'db_table': "u'book_author_rss'"}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {}), + 'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Book']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'rss_created_at': ('django.db.models.fields.DateTimeField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'forum.comment': { + 'Meta': {'object_name': 'Comment', 'db_table': "u'comment'"}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'comment': ('django.db.models.fields.CharField', [], {'max_length': '2048'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'html': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '2048'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'comments'", 'to': "orm['auth.User']"}) + }, + 'forum.emailfeedsetting': { + 'Meta': {'object_name': 'EmailFeedSetting'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"}) + }, + 'forum.favoritequestion': { + 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Question']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"}) + }, + 'forum.flaggeditem': { + 'Meta': {'unique_together': "(('content_type', 'object_id', 'user'),)", 'object_name': 'FlaggedItem', 'db_table': "u'flagged_item'"}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'flagged_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flaggeditems'", 'to': "orm['auth.User']"}) + }, + 'forum.markedtag': { + 'Meta': {'object_name': 'MarkedTag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"}) + }, + 'forum.mention': { + 'Meta': {'object_name': 'Mention', 'db_table': "u'mention'"}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mentioned_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'mentioned_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mentions_sent'", 'to': "orm['auth.User']"}), + 'mentioned_whom': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mentions_received'", 'to': "orm['auth.User']"}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}) + }, + 'forum.question': { + 'Meta': {'object_name': 'Question', 'db_table': "u'question'"}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'answer_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questions'", 'to': "orm['auth.User']"}), + 'close_reason': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'closed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'closed_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'closed_questions'", 'null': 'True', 'to': "orm['auth.User']"}), + 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_questions'", 'null': 'True', 'to': "orm['auth.User']"}), + 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'favorite_questions'", 'through': "'FavoriteQuestion'", 'to': "orm['auth.User']"}), + 'favourite_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'followed_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'followed_questions'", 'to': "orm['auth.User']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'last_active_in_questions'", 'to': "orm['auth.User']"}), + 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_edited_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_edited_questions'", 'null': 'True', 'to': "orm['auth.User']"}), + 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_questions'", 'null': 'True', 'to': "orm['auth.User']"}), + 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'questions'", 'to': "orm['forum.Tag']"}), + 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'vote_down_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'vote_up_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'forum.questionrevision': { + 'Meta': {'object_name': 'QuestionRevision', 'db_table': "u'question_revision'"}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questionrevisions'", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Question']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + 'forum.questionview': { + 'Meta': {'object_name': 'QuestionView'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['forum.Question']"}), + 'when': ('django.db.models.fields.DateTimeField', [], {}), + 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"}) + }, + 'forum.repute': { + 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Question']"}), + 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}), + 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'forum.tag': { + 'Meta': {'object_name': 'Tag', 'db_table': "u'tag'"}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'forum.validationhash': { + 'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'}, + 'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 5, 22, 21, 40, 30, 531379)'}), + 'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'forum.vote': { + 'Meta': {'unique_together': "(('content_type', 'object_id', 'user'),)", 'object_name': 'Vote', 'db_table': "u'vote'"}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['auth.User']"}), + 'vote': ('django.db.models.fields.SmallIntegerField', [], {}), + 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) + } + } + + complete_apps = ['forum'] diff --git a/forum/models/__init__.py b/forum/models/__init__.py index d4b9dcb4..1c20e74b 100644 --- a/forum/models/__init__.py +++ b/forum/models/__init__.py @@ -3,11 +3,12 @@ from answer import Answer, AnonymousAnswer, AnswerRevision from tag import Tag, MarkedTag from meta import Vote, Comment, FlaggedItem from user import Activity, ValidationHash, EmailFeedSetting -from user import Mention, AuthKeyUserAssociation +from user import AuthKeyUserAssociation from repute import Badge, Award, Repute from django.core.urlresolvers import reverse from forum.search.indexer import create_fulltext_indexes from django.db.models.signals import post_syncdb +from forum import const import logging import re @@ -250,7 +251,7 @@ def record_ask_event(instance, created, **kwargs): user=instance.author, active_at=instance.added_at, content_object=instance, - activity_type=TYPE_ACTIVITY_ASK_QUESTION + activity_type=const.TYPE_ACTIVITY_ASK_QUESTION ) activity.save() @@ -281,7 +282,7 @@ def record_answer_event(instance, created, **kwargs): user = instance.author, active_at = instance.added_at, content_object = instance, - activity_type = TYPE_ACTIVITY_ANSWER + activity_type = const.TYPE_ACTIVITY_ANSWER ) activity.save() receiving_users = instance.question.get_author_list( @@ -295,9 +296,9 @@ def record_answer_event(instance, created, **kwargs): def record_comment_event(instance, created, **kwargs): if created: if isinstance(instance.content_object, Question): - type = TYPE_ACTIVITY_COMMENT_QUESTION + activity_type = const.TYPE_ACTIVITY_COMMENT_QUESTION elif isinstance(instance.content_object, Answer): - type = TYPE_ACTIVITY_COMMENT_ANSWER + activity_type = const.TYPE_ACTIVITY_COMMENT_ANSWER else: logging.critical('recording comment for %s is not implemented' % type(instance.content_object)) @@ -305,7 +306,7 @@ def record_comment_event(instance, created, **kwargs): user = instance.user, active_at = instance.added_at, content_object = instance, - activity_type = type + activity_type = activity_type ) activity.save() @@ -322,7 +323,7 @@ def record_revision_question_event(instance, created, **kwargs): user=instance.author, active_at=instance.revised_at, content_object=instance, - activity_type=TYPE_ACTIVITY_UPDATE_QUESTION + activity_type=const.TYPE_ACTIVITY_UPDATE_QUESTION ) activity.save() receiving_users = set() @@ -342,7 +343,7 @@ def record_revision_answer_event(instance, created, **kwargs): user=instance.author, active_at=instance.revised_at, content_object=instance, - activity_type=TYPE_ACTIVITY_UPDATE_ANSWER + activity_type=const.TYPE_ACTIVITY_UPDATE_ANSWER ) activity.save() receiving_users = set() @@ -367,7 +368,7 @@ def record_award_event(instance, created, **kwargs): user=instance.user,#todo: change this to community user who gives the award active_at=instance.awarded_at, content_object=instance, - activity_type=TYPE_ACTIVITY_PRIZE + activity_type=const.TYPE_ACTIVITY_PRIZE ) activity.save() activity.receiving_users.add(instance.user) @@ -405,7 +406,7 @@ def record_answer_accepted(instance, created, **kwargs): user=instance.question.author, active_at=datetime.datetime.now(), content_object=instance, - activity_type=TYPE_ACTIVITY_MARK_ANSWER + activity_type=const.TYPE_ACTIVITY_MARK_ANSWER ) receiving_users = instance.get_author_list( exclude_list = [instance.question.author] @@ -428,9 +429,9 @@ def record_vote(instance, created, **kwargs): """ if created: if instance.vote == 1: - vote_type = TYPE_ACTIVITY_VOTE_UP + vote_type = const.TYPE_ACTIVITY_VOTE_UP else: - vote_type = TYPE_ACTIVITY_VOTE_DOWN + vote_type = const.TYPE_ACTIVITY_VOTE_DOWN activity = Activity( user=instance.user, @@ -449,7 +450,7 @@ def record_cancel_vote(instance, **kwargs): user=instance.user, active_at=datetime.datetime.now(), content_object=instance, - activity_type=TYPE_ACTIVITY_CANCEL_VOTE + activity_type=const.TYPE_ACTIVITY_CANCEL_VOTE ) #todo: same problem - cannot access receiving user here activity.save() @@ -459,9 +460,9 @@ def record_delete_question(instance, delete_by, **kwargs): when user deleted the question """ if instance.__class__ == "Question": - activity_type = TYPE_ACTIVITY_DELETE_QUESTION + activity_type = const.TYPE_ACTIVITY_DELETE_QUESTION else: - activity_type = TYPE_ACTIVITY_DELETE_ANSWER + activity_type = const.TYPE_ACTIVITY_DELETE_ANSWER activity = Activity( user=delete_by, @@ -477,7 +478,7 @@ def record_mark_offensive(instance, mark_by, **kwargs): user=mark_by, active_at=datetime.datetime.now(), content_object=instance, - activity_type=TYPE_ACTIVITY_MARK_OFFENSIVE + activity_type=const.TYPE_ACTIVITY_MARK_OFFENSIVE ) activity.save() receiving_users = instance.get_author_list( @@ -493,7 +494,7 @@ def record_update_tags(question, **kwargs): user=question.author, active_at=datetime.datetime.now(), content_object=question, - activity_type=TYPE_ACTIVITY_UPDATE_TAGS + activity_type=const.TYPE_ACTIVITY_UPDATE_TAGS ) activity.save() @@ -506,7 +507,7 @@ def record_favorite_question(instance, created, **kwargs): user=instance.user, active_at=datetime.datetime.now(), content_object=instance, - activity_type=TYPE_ACTIVITY_FAVORITE + activity_type=const.TYPE_ACTIVITY_FAVORITE ) activity.save() receiving_users = instance.question.get_author_list( @@ -519,7 +520,7 @@ def record_user_full_updated(instance, **kwargs): user=instance, active_at=datetime.datetime.now(), content_object=instance, - activity_type=TYPE_ACTIVITY_USER_FULL_UPDATED + activity_type=const.TYPE_ACTIVITY_USER_FULL_UPDATED ) activity.save() @@ -588,7 +589,6 @@ Activity = Activity EmailFeedSetting = EmailFeedSetting ValidationHash = ValidationHash AuthKeyUserAssociation = AuthKeyUserAssociation -Mention = Mention __all__ = [ 'Question', @@ -615,7 +615,6 @@ __all__ = [ 'EmailFeedSetting', 'ValidationHash', 'AuthKeyUserAssociation', - 'Mention', 'User', ] diff --git a/forum/models/answer.py b/forum/models/answer.py index 7f5c8ae8..1c33cad8 100644 --- a/forum/models/answer.py +++ b/forum/models/answer.py @@ -51,6 +51,12 @@ class AnswerManager(models.Manager): pass return answer + def get_author_list(self, **kwargs): + authors = set() + for answer in self: + authors.update(answer.get_author_list(**kwargs)) + return list(authors) + #GET_ANSWERS_FROM_USER_QUESTIONS = u'SELECT answer.* FROM answer INNER JOIN question ON answer.question_id = question.id WHERE question.author_id =%s AND answer.author_id <> %s' def get_answers_from_question(self, question, user=None): """ diff --git a/forum/models/base.py b/forum/models/base.py index 076d1cfa..068effbd 100644 --- a/forum/models/base.py +++ b/forum/models/base.py @@ -111,8 +111,8 @@ class Content(models.Model): last_edited_at = models.DateTimeField(null=True, blank=True) last_edited_by = models.ForeignKey(User, null=True, blank=True, related_name='last_edited_%(class)ss') - html = models.TextField(null=True) - text = models.TextField(null=True) #denormalized copy of latest revision + html = models.TextField(null=True)#html rendition of the latest revision + text = models.TextField(null=True)#denormalized copy of latest revision comments = generic.GenericRelation(Comment) votes = generic.GenericRelation(Vote) flagged_items = generic.GenericRelation(FlaggedItem) @@ -150,7 +150,10 @@ class Content(models.Model): self.save() def get_latest_revision(self): - return self.revisions.all()[0] + return self.revisions.all().order_by('-revised_at')[0] + + def get_latest_revision_number(self): + return self.get_latest_revision().revision def get_last_author(self): return self.last_edited_by @@ -162,12 +165,30 @@ class Content(models.Model): authors.update([c.user for c in self.comments.all()]) if recursive: if hasattr(self, 'answers'): - for a in self.answers.all(): + for a in self.answers.exclude(deleted = True): authors.update(a.get_author_list( include_comments = include_comments ) ) if exclude_list: authors -= set(exclude_list) return list(authors) + def passes_tag_filter_for_user(self, user): + tags = self.get_origin_post().tags.all() + + if self.tag_filter_setting == 'interesting': + #at least some of the tags must be marked interesting + return self.tag_selections.exists(tag__in = tags, reason = 'good') + + elif self.tag_filter_setting == 'ignored': + #at least one tag must be ignored + if self.tag_selections.exists(tag__in = tags, reason = 'bad'): + return False + else: + return True + + else: + raise Exception('unexpected User.tag_filter_setting %' % self.tag_filter_setting) + + def post_get_last_update_info(self):#todo: rename this subroutine when = self.added_at who = self.author diff --git a/forum/models/meta.py b/forum/models/meta.py index d792beca..8382fec7 100644 --- a/forum/models/meta.py +++ b/forum/models/meta.py @@ -99,13 +99,20 @@ class Comment(MetaContent, UserContent): logging.debug('problem pinging google did you register you sitemap with google?') def delete(self, **kwargs): - from forum.models.user import Mention - ctype = ContentType.objects.get_for_model(self) - Mention.objects.filter( - content_type = ctype, - object_id = self.id + #todo: not very good import in models of other models + #todo: potentially a circular import + from forum.models.user import Activity + Activity.objects.get_mentions( + mentioned_in = self ).delete() super(Comment,self).delete(**kwargs) + def get_absolute_url(self): + origin_post = self.get_origin_post() + return '%s#comment-%d' % (origin_post.get_absolute_url(), self.id) + + def get_latest_revision_number(self): + return 1 + def __unicode__(self): return self.comment diff --git a/forum/models/question.py b/forum/models/question.py index 58852495..c2fb6203 100644 --- a/forum/models/question.py +++ b/forum/models/question.py @@ -183,6 +183,15 @@ class QuestionManager(models.Manager): | Q(answers__in=answer_list) ).distinct().order_by('?') + def get_author_list(self, **kwargs): + #todo: - this is duplication - answer manager also has this method + #will be gone when models are consolidated + #note that method get_question_and_answer_contributors is similar in function + authors = set() + for question in self: + authors.update(question.get_author_list(**kwargs)) + return list(authors) + def update_tags(self, question, tagnames, user): """ Updates Tag associations for a question to match the given diff --git a/forum/models/user.py b/forum/models/user.py index 4e2b7f1a..2f4db6aa 100644 --- a/forum/models/user.py +++ b/forum/models/user.py @@ -2,70 +2,209 @@ from base import * from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic from django.contrib.auth.models import User +from forum.models.question import Question, QuestionRevision +from forum.models.answer import Answer, AnswerRevision +from forum.models.meta import Comment from hashlib import md5 import string from random import Random from forum import const +from forum.utils import functions import datetime +import logging from django.utils.translation import ugettext as _ +class ActivityManager(models.Manager): + def get_all_origin_posts(self): + #todo: redo this with query sets + origin_posts = set() + for m in self.all(): + post = m.content_object + if post and hasattr(post, 'get_origin_post'): + origin_posts.add(post.get_origin_post()) + else: + logging.debug( + 'method get_origin_post() not implemented for %s' \ + % unicode(post) + ) + return list(origin_posts) + + def create_new_mention( + self, + mentioned_by = None, + mentioned_whom = None, + mentioned_at = None, + mentioned_in = None, + reported = None + ): + + #todo: automate this using python inspect module + kwargs = dict() + + kwargs['activity_type'] = const.TYPE_ACTIVITY_MENTION + + if mentioned_at: + #todo: handle cases with rich lookups here like __lt + kwargs['active_at'] = mentioned_at + + if mentioned_by: + kwargs['user'] = mentioned_by + + if mentioned_in: + if functions.is_iterable(mentioned_in): + raise NotImplementedError('mentioned_in only works for single items') + else: + post_content_type = ContentType.objects.get_for_model(mentioned_in) + kwargs['content_type'] = post_content_type + kwargs['object_id'] = mentioned_in.id + + if reported == True: + kwargs['is_auditted'] = True + else: + kwargs['is_auditted'] = False + + + mention_activity = Activity(**kwargs) + mention_activity.save() + + if mentioned_whom: + if functions.is_iterable(mentioned_whom): + raise NotImplementedError('cannot yet mention multiple people at once') + else: + mention_activity.receiving_users.add(mentioned_whom) + + return mention_activity + + + def get_mentions( + self, + mentioned_by = None, + mentioned_whom = None, + mentioned_at = None, + mentioned_in = None, + reported = None + ): + + kwargs = dict() + + kwargs['activity_type'] = const.TYPE_ACTIVITY_MENTION + + if mentioned_at: + #todo: handle cases with rich lookups here like __lt + kwargs['active_at'] = mentioned_at + + if mentioned_by: + kwargs['user'] = mentioned_by + + if mentioned_whom: + if functions.is_iterable(mentioned_whom): + kwargs['receiving_users__in'] = mentioned_whom + else: + kwargs['receiving_users__in'] = (mentioned_whom,) + + if mentioned_in: + if functions.is_iterable(mentioned_in): + it = iter(mentioned_in) + raise NotImplementedError('mentioned_in only works for single items') + else: + post_content_type = ContentType.objects.get_for_model(mentioned_in) + kwargs['content_type'] = post_content_type + kwargs['object_id'] = mentioned_in.id + + if reported == True: + kwargs['is_auditted'] = True + else: + kwargs['is_auditted'] = False + + return self.filter(**kwargs) + + class Activity(models.Model): """ We keep some history data for user activities """ user = models.ForeignKey(User) receiving_users = models.ManyToManyField(User, related_name='received_activity') - activity_type = models.SmallIntegerField(choices=TYPE_ACTIVITY) + activity_type = models.SmallIntegerField(choices = const.TYPE_ACTIVITY) active_at = models.DateTimeField(default=datetime.datetime.now) content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id') is_auditted = models.BooleanField(default=False) + objects = ActivityManager() + def __unicode__(self): return u'[%s] was active at %s' % (self.user.username, self.active_at) + def get_response_type_content_object(self): + """ + This method will go when post models are + unified (todo:) + """ + cobj = self.content_object + if isinstance(cobj, Comment): + return cobj + elif isinstance(cobj, AnswerRevision): + return cobj.answer + elif isinstance(cobj, QuestionRevision): + return cobj.question + else: + raise NotImplementedError() + class Meta: app_label = 'forum' db_table = u'activity' -class MentionManager(models.Manager): - def get_question_list(self): - out = [] - for m in self.all(): - post = m.content_object - if isinstance(post, Question): - out.append(post) - elif isinstance(post, Answer): - out.append(post.question) - elif isinstance(post, Comment): - p = post.content_object - if isinstance(p, Question): - out.append(p) - elif isinstance(p, Answer): - out.append(p.question) - return out - -class Mention(models.Model): - """ - Table holding @mention type entries in the posts - todo: maybe merge this with Activity table - """ - mentioned_by = models.ForeignKey(User, related_name = 'mentions_sent') - mentioned_whom = models.ForeignKey(User, related_name = 'mentions_received') - mentioned_at = models.DateTimeField(default=datetime.datetime.now) - #have to use generic foreign key here to point to the context of the mention - content_type = models.ForeignKey(ContentType) - object_id = models.PositiveIntegerField() - content_object = generic.GenericForeignKey('content_type', 'object_id') - - objects = MentionManager() - - class Meta: - app_label = 'forum' - db_table = u'mention' +class EmailFeedSettingManager(models.Manager): + def exists_match_to_post_and_subscriber(self, post = None, subscriber = None, **kwargs): + """returns list of feeds matching the post + and subscriber + """ + feeds = self.filter(subscriber = subscriber, **kwargs) + + for feed in feeds: + + if feed.feed_type == 'm_and_c': + if isinstance(post, Comment): + return True + else: + post_content_type = ContentType.objects.get_for_model(post) + subscriber_mentions = Mention.objects.filter( + content_type = post_content_type, + object_id = post.id, + mentioned_whom = subscriber, + is_auditted = False + ) + if subscriber_mentions: + return True + else: + if feed.feed_type == 'q_all': + #'everything' category is tag filtered + if post.passes_tag_filter_for_user(subscriber): + return True + else: + + origin_post = post.get_origin_post() + + if feed.feed_type == 'q_ask': + if origin_post.author == subscriber: + return True + + elif feed.feed_type == 'q_ans': + #make sure that subscriber answered origin post + answers = origin_post.answers.exclude(deleted=True) + if subscriber in answers.get_author_list(): + return True + + elif feed.feed_type == 'q_sel': + #make sure that subscriber has selected this post + #individually + if subscriber in origin_post.followed_by.all(): + return True + return False class EmailFeedSetting(models.Model): DELTA_TABLE = { @@ -99,6 +238,8 @@ class EmailFeedSetting(models.Model): added_at = models.DateTimeField(auto_now_add=True) reported_at = models.DateTimeField(null=True) + objects = EmailFeedSettingManager() + #functions for rich comparison #PRECEDENCE = ('i','d','w','n')#the greater ones are first #def __eq__(self, other): diff --git a/forum/skins/default/templates/instant_notification.html b/forum/skins/default/templates/instant_notification.html new file mode 100644 index 00000000..a8218c12 --- /dev/null +++ b/forum/skins/default/templates/instant_notification.html @@ -0,0 +1,46 @@ +{% comment %} +Called from forum.models.__init__.maybe_send_instant_notifications() +Template paramaters: + +receiving_user - User +update_author - User +updated_post - Comment|Answer|Question +related_origin_post - origin post related to the update +update_url - absolute url (including http://... to updated post +update_type - question_comment|answer_comment|answer_update|question_update +revision_number - integer (first revision is 1) +has_mention - Boolean +{% endcomment %} +{% load i18n %} +{% load smart_if %} +{% blocktrans with receiving_user.get_best_name as user_name %}Dear {{user_name}},{% endblocktrans %} + +{% if has_mention %} +{% if update_type == 'question_comment' or update_type == 'answer_comment' %} +{% blocktrans with post_author.get_profile_link as author_link and related_origin_post.get_absolute_url as origin_post_url and related_origin_post.title as origin_post_title %} +{{author_link}} has left you a <a href="{{update_url|safe}}">comment</a> +related to question <a href="{{origin_post_url|safe}}">{{origin_post_title}}</a> +{% endblocktrans %} +{% endif %} +{% else %}{# updated post has no mention of user #} +{% if update_type == 'question_comment' or update_type == 'answer_comment' %} +{% blocktrans with post_author.get_profile_link as author_link and related_origin_post.get_absolute_url as origin_post_url and related_origin_post.title as origin_post_title %} +{{author_link}} has left a new <a href="{{update_url|safe}}">comment</a> +related to question <a href="{{origin_post_url|safe}}">{{origin_post_title}}</a> +{% endblocktrans %} +{% if update +{% endif %} +{% if update_type == 'question_comment' or update_type == 'answer_comment' %} +{% if has_mention %} +{% blocktrans %} + +{% endblocktrans %} +{% endif %} +{% endwith %} +{% endif %} +{% if update_type == 'answer_update' %} +{% endif %} +{% if update_type == 'question_update' %} +{% endif %} + +{% include "email_footer.txt" %} diff --git a/forum/skins/default/templates/questions.html b/forum/skins/default/templates/questions.html index f7863208..78ea8885 100644 --- a/forum/skins/default/templates/questions.html +++ b/forum/skins/default/templates/questions.html @@ -138,7 +138,11 @@ <div style="clear:both">
<p class="search-result-summary">
{% if author_name or search_tags or query %}
- {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num %} {{q_num}} question found{% plural %}{{q_num}} questions found{% endblocktrans %}
+ {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num %}
+ {{q_num}} question found
+ {% plural %}
+ {{q_num}} questions found
+ {% endblocktrans %}
{% else %}
{% blocktrans count questions_count as cnt with questions_count|intcomma as q_num %}{{q_num}} question{% plural %}{{q_num}} questions{% endblocktrans %}
{% endif %}
diff --git a/forum/utils/functions.py b/forum/utils/functions.py index 671ddc2c..da6a2cae 100644 --- a/forum/utils/functions.py +++ b/forum/utils/functions.py @@ -1,5 +1,11 @@ -def get_from_dict_or_object(object,key): +def get_from_dict_or_object(source, key): try: - return object[key] + return source[key] except: - return getattr(object,key) + return getattr(source,key) + +def is_iterable(thing): + if hasattr(thing, '__iter__'): + return True + else: + return isinstance(thing, basestring) diff --git a/forum/utils/markup.py b/forum/utils/markup.py index b921077a..bfe7b1c9 100644 --- a/forum/utils/markup.py +++ b/forum/utils/markup.py @@ -1,18 +1,18 @@ from forum import const #from forum.models import Comment, Question, Answer #from forum.models import QuestionRevision, AnswerRevision -from forum.models import Mention, User +from forum.models import Activity, User +#todo: don't like that this file deals with models directly def _make_mention(mentioned_whom, context_object = None): mentioned_by = context_object.get_last_author() if mentioned_whom: if mentioned_whom != mentioned_by: - m = Mention( + m = Activity.objects.create_new_mention( mentioned_by = mentioned_by, mentioned_whom = mentioned_whom, - content_object = context_object + mentioned_in = context_object ) - m.save() url = mentioned_whom.get_profile_url() username = mentioned_whom.username return '<a href="%s">@%s</a>' % (url, username) @@ -46,17 +46,30 @@ def mentionize(text, context_object = None): op = context_object.get_origin_post() authors = op.get_author_list( include_comments = True, recursive = True ) - extra_name_seed = '' - for c in text: - if c in const.TWITTER_STYLE_MENTION_TERMINATION_CHARS: - break - else: - extra_name_seed += c - if len(extra_name_seed) > 10: - break - - if len(extra_name_seed) > 0: - authors += list(User.objects.filter(username__startswith = extra_name_seed)) + text_copy = text + extra_name_seeds = set() + while '@' in text_copy: + pos = text_copy.index('@') + text_copy = text_copy[pos+1:]#chop off prefix + name_seed = '' + for c in text_copy: + if c in const.TWITTER_STYLE_MENTION_TERMINATION_CHARS: + extra_name_seeds.add(name_seed) + break + if len(name_seed) > 10: + extra_name_seeds.add(name_seed) + break + if c == '@': + extra_name_seeds.add(name_seed) + break + name_seed += c + + extra_authors = set() + for name_seed in extra_name_seeds: + if len(name_seed) > 0: + extra_authors.update(User.objects.filter(username__startswith = name_seed)) + + authors += list(extra_authors) output = '' while '@' in text: diff --git a/forum/views/users.py b/forum/views/users.py index 742f714c..afd78ac5 100644 --- a/forum/views/users.py +++ b/forum/views/users.py @@ -1,5 +1,4 @@ from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User from django.core.paginator import Paginator, EmptyPage, InvalidPage from django.template.defaultfilters import slugify from django.contrib.contenttypes.models import ContentType @@ -11,22 +10,22 @@ from django.utils.translation import ugettext as _ from django.utils.http import urlquote_plus from django.utils.html import strip_tags from django.core.urlresolvers import reverse -from forum.forms import *#incomplete list is EditUserForm, ModerateUserForm, TagFilterSelectionForm, from forum.utils.html import sanitize_html from forum import auth +from forum import forms import calendar from django.contrib.contenttypes.models import ContentType -from forum.models import user_updated -from forum.const import USERS_PAGE_SIZE +from forum import const from django.conf import settings from forum.conf import settings as forum_settings - -question_type = ContentType.objects.get_for_model(Question) -answer_type = ContentType.objects.get_for_model(Answer) -comment_type = ContentType.objects.get_for_model(Comment) -question_revision_type = ContentType.objects.get_for_model(QuestionRevision) -answer_revision_type = ContentType.objects.get_for_model(AnswerRevision) -repute_type = ContentType.objects.get_for_model(Repute) +from forum import models + +question_type = ContentType.objects.get_for_model(models.Question) +answer_type = ContentType.objects.get_for_model(models.Answer) +comment_type = ContentType.objects.get_for_model(models.Comment) +question_revision_type = ContentType.objects.get_for_model(models.QuestionRevision) +answer_revision_type = ContentType.objects.get_for_model(models.AnswerRevision) +repute_type = ContentType.objects.get_for_model(models.Repute) question_type_id = question_type.id answer_type_id = answer_type.id comment_type_id = comment_type.id @@ -45,18 +44,29 @@ def users(request): if suser == "": if sortby == "newest": - objects_list = Paginator(User.objects.all().order_by('-date_joined'), USERS_PAGE_SIZE) + order_by_parameter = '-date_joined' elif sortby == "last": - objects_list = Paginator(User.objects.all().order_by('date_joined'), USERS_PAGE_SIZE) + order_by_parameter = 'date_joined' elif sortby == "user": - objects_list = Paginator(User.objects.all().order_by('username'), USERS_PAGE_SIZE) - # default + order_by_parameter = 'username' else: - objects_list = Paginator(User.objects.all().order_by('-reputation'), USERS_PAGE_SIZE) + # default + order_by_parameter = '-reputation' + + objects_list = Paginator( + models.User.objects.all().order_by(order_by_parameter), + const.USERS_PAGE_SIZE + ) base_url = reverse('users') + '?sort=%s&' % sortby else: sortby = "reputation" - objects_list = Paginator(User.objects.extra(where=['username like %s'], params=['%' + suser + '%']).order_by('-reputation'), USERS_PAGE_SIZE) + objects_list = Paginator( + models.User.objects.extra( + where=['username like %s'], + params=['%' + suser + '%'] + ).order_by('-reputation'), + const.USERS_PAGE_SIZE + ) base_url = reverse('users') + '?name=%s&sort=%s&' % (suser, sortby) try: @@ -92,8 +102,8 @@ def moderate_user(request, id): if not request.is_ajax(): return HttpResponseForbidden(mimetype="application/json") - user = get_object_or_404(User, id=id) - form = ModerateUserForm(request.POST, instance=user) + user = get_object_or_404(models.User, id=id) + form = forms.ModerateUserForm(request.POST, instance=user) if form.is_valid(): form.save() @@ -114,11 +124,11 @@ def set_new_email(user, new_email, nomessage=False): @login_required def edit_user(request, id): - user = get_object_or_404(User, id=id) + user = get_object_or_404(models.User, id=id) if request.user != user: raise Http404 if request.method == "POST": - form = EditUserForm(user, request.POST) + form = forms.EditUserForm(user, request.POST) if form.is_valid(): new_email = sanitize_html(form.cleaned_data['email']) @@ -139,10 +149,10 @@ def edit_user(request, id): # send user updated singal if full fields have been updated if user.email and user.real_name and user.website and user.location and \ user.date_of_birth and user.about: - user_updated.send(sender=user.__class__, instance=user, updated_by=user) + models.user_updated.send(sender=user.__class__, instance=user, updated_by=user) return HttpResponseRedirect(user.get_profile_url()) else: - form = EditUserForm(user) + form = forms.EditUserForm(user) return render_to_response('user_edit.html', { 'active_tab': 'users', 'form' : form, @@ -150,8 +160,8 @@ def edit_user(request, id): }, context_instance=RequestContext(request)) def user_stats(request, user_id, user_view): - user = get_object_or_404(User, id=user_id) - questions = Question.objects.extra( + user = get_object_or_404(models.User, id=user_id) + questions = models.Question.objects.extra( select={ 'score' : 'question.score', 'favorited_myself' : 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s AND f.question_id = question.id', @@ -191,7 +201,7 @@ def user_stats(request, user_id, user_view): 'la_user_reputation')[:100] #this is meant for the questions answered by the user (or where answers were edited by him/her?) - answered_questions = Question.objects.extra( + answered_questions = models.Question.objects.extra( select={ 'vote_up_count' : 'answer.vote_up_count', 'vote_down_count' : 'answer.vote_down_count', @@ -216,20 +226,20 @@ def user_stats(request, user_id, user_view): 'vote_up_count', 'vote_down_count')[:100] - up_votes = Vote.objects.get_up_vote_count_from_user(user) - down_votes = Vote.objects.get_down_vote_count_from_user(user) - votes_today = Vote.objects.get_votes_count_today_from_user(user) + up_votes = models.Vote.objects.get_up_vote_count_from_user(user) + down_votes = models.Vote.objects.get_down_vote_count_from_user(user) + votes_today = models.Vote.objects.get_votes_count_today_from_user(user) votes_total = forum_settings.MAX_VOTES_PER_USER_PER_DAY question_id_set = set(map(lambda v: v['id'], list(questions))) \ | set(map(lambda v: v['id'], list(answered_questions))) - user_tags = Tag.objects.filter(questions__id__in = question_id_set) + user_tags = models.Tag.objects.filter(questions__id__in = question_id_set) try: from django.db.models import Count #todo - rewrite template to do the table joins within standard ORM - #awards = Award.objects.filter(user=user).order_by('-awarded_at') - awards = Award.objects.extra( + #awards = models.Award.objects.filter(user=user).order_by('-awarded_at') + awards = models.Award.objects.extra( select={'id': 'badge.id', 'name':'badge.name', 'description': 'badge.description', @@ -245,7 +255,7 @@ def user_stats(request, user_id, user_view): except ImportError: #todo: remove all old django stuff, e.g. with '.group_by = ' pattern - awards = Award.objects.extra( + awards = models.Award.objects.extra( select={'id': 'badge.id', 'count': 'count(badge_id)', 'name':'badge.name', @@ -266,7 +276,7 @@ def user_stats(request, user_id, user_view): user_tags.query.group_by = ['name'] if auth.can_moderate_users(request.user): - moderate_user_form = ModerateUserForm(instance=user) + moderate_user_form = forms.ModerateUserForm(instance=user) else: moderate_user_form = None @@ -290,9 +300,9 @@ def user_stats(request, user_id, user_view): }, context_instance=RequestContext(request)) def user_recent(request, user_id, user_view): - user = get_object_or_404(User, id=user_id) + user = get_object_or_404(models.User, id=user_id) def get_type_name(type_id): - for item in TYPE_ACTIVITY: + for item in const.TYPE_ACTIVITY: if type_id in item: return item[1] @@ -313,11 +323,11 @@ def user_recent(request, user_id, user_view): self.time = time self.type = get_type_name(type) self.type_id = type - self.badge = get_object_or_404(Badge, id=id) + self.badge = get_object_or_404(models.Badge, id=id) activities = [] # ask questions - questions = Activity.objects.extra( + questions = models.Activity.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'question.id', @@ -327,7 +337,7 @@ def user_recent(request, user_id, user_view): tables=['activity', 'question'], where=['activity.content_type_id = %s AND activity.object_id = ' + 'question.id AND question.deleted=False AND activity.user_id = %s AND activity.activity_type = %s'], - params=[question_type_id, user_id, TYPE_ACTIVITY_ASK_QUESTION], + params=[question_type_id, user_id, const.TYPE_ACTIVITY_ASK_QUESTION], order_by=['-activity.active_at'] ).values( 'title', @@ -341,7 +351,7 @@ def user_recent(request, user_id, user_view): activities.extend(questions) # answers - answers = Activity.objects.extra( + answers = models.Activity.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'question.id', @@ -353,7 +363,7 @@ def user_recent(request, user_id, user_view): where=['activity.content_type_id = %s AND activity.object_id = answer.id AND ' + 'answer.question_id=question.id AND answer.deleted=False AND activity.user_id=%s AND '+ 'activity.activity_type=%s AND question.deleted=False'], - params=[answer_type_id, user_id, TYPE_ACTIVITY_ANSWER], + params=[answer_type_id, user_id, const.TYPE_ACTIVITY_ANSWER], order_by=['-activity.active_at'] ).values( 'title', @@ -368,7 +378,7 @@ def user_recent(request, user_id, user_view): activities.extend(answers) # question comments - comments = Activity.objects.extra( + comments = models.Activity.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'comment.object_id', @@ -381,7 +391,7 @@ def user_recent(request, user_id, user_view): 'activity.user_id = comment.user_id AND comment.object_id=question.id AND '+ 'comment.content_type_id=%s AND activity.user_id = %s AND activity.activity_type=%s AND ' + 'question.deleted=False'], - params=[comment_type_id, question_type_id, user_id, TYPE_ACTIVITY_COMMENT_QUESTION], + params=[comment_type_id, question_type_id, user_id, const.TYPE_ACTIVITY_COMMENT_QUESTION], order_by=['-comment.added_at'] ).values( 'title', @@ -396,7 +406,7 @@ def user_recent(request, user_id, user_view): activities.extend(comments) # answer comments - comments = Activity.objects.extra( + comments = models.Activity.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'question.id', @@ -411,7 +421,7 @@ def user_recent(request, user_id, user_view): 'comment.content_type_id=%s AND question.id = answer.question_id AND '+ 'activity.user_id = %s AND activity.activity_type=%s AND '+ 'answer.deleted=False AND question.deleted=False'], - params=[comment_type_id, answer_type_id, user_id, TYPE_ACTIVITY_COMMENT_ANSWER], + params=[comment_type_id, answer_type_id, user_id, const.TYPE_ACTIVITY_COMMENT_ANSWER], order_by=['-comment.added_at'] ).values( 'title', @@ -427,7 +437,7 @@ def user_recent(request, user_id, user_view): activities.extend(comments) # question revisions - revisions = Activity.objects.extra( + revisions = models.Activity.objects.extra( select={ 'title' : 'question_revision.title', 'question_id' : 'question_revision.question_id', @@ -440,7 +450,7 @@ def user_recent(request, user_id, user_view): 'question_revision.id=question.id AND question.deleted=False AND '+ 'activity.user_id = question_revision.author_id AND activity.user_id = %s AND '+ 'activity.activity_type=%s'], - params=[question_revision_type_id, user_id, TYPE_ACTIVITY_UPDATE_QUESTION], + params=[question_revision_type_id, user_id, const.TYPE_ACTIVITY_UPDATE_QUESTION], order_by=['-activity.active_at'] ).values( 'title', @@ -456,7 +466,7 @@ def user_recent(request, user_id, user_view): activities.extend(revisions) # answer revisions - revisions = Activity.objects.extra( + revisions = models.Activity.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'question.id', @@ -472,7 +482,7 @@ def user_recent(request, user_id, user_view): 'answer_revision.answer_id=answer.id AND answer.question_id = question.id AND '+ 'question.deleted=False AND answer.deleted=False AND '+ 'activity.activity_type=%s'], - params=[answer_revision_type_id, user_id, TYPE_ACTIVITY_UPDATE_ANSWER], + params=[answer_revision_type_id, user_id, const.TYPE_ACTIVITY_UPDATE_ANSWER], order_by=['-activity.active_at'] ).values( 'title', @@ -489,7 +499,7 @@ def user_recent(request, user_id, user_view): activities.extend(revisions) # accepted answers - accept_answers = Activity.objects.extra( + accept_answers = models.Activity.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'question.id', @@ -501,7 +511,7 @@ def user_recent(request, user_id, user_view): 'activity.user_id = question.author_id AND activity.user_id = %s AND '+ 'answer.deleted=False AND question.deleted=False AND '+ 'answer.question_id=question.id AND activity.activity_type=%s'], - params=[answer_type_id, user_id, TYPE_ACTIVITY_MARK_ANSWER], + params=[answer_type_id, user_id, const.TYPE_ACTIVITY_MARK_ANSWER], order_by=['-activity.active_at'] ).values( 'title', @@ -514,7 +524,7 @@ def user_recent(request, user_id, user_view): q['question_id'])) for q in accept_answers] activities.extend(accept_answers) #award history - awards = Activity.objects.extra( + awards = models.Activity.objects.extra( select={ 'badge_id' : 'badge.id', 'awarded_at': 'award.awarded_at', @@ -523,7 +533,7 @@ def user_recent(request, user_id, user_view): tables=['activity', 'award', 'badge'], where=['activity.user_id = award.user_id AND activity.user_id = %s AND '+ 'award.badge_id=badge.id AND activity.object_id=award.id AND activity.activity_type=%s'], - params=[user_id, TYPE_ACTIVITY_PRIZE], + params=[user_id, const.TYPE_ACTIVITY_PRIZE], order_by=['-activity.active_at'] ).values( 'badge_id', @@ -562,10 +572,10 @@ def user_responses(request, user_id, user_view): def __unicode__(self): return u'%s %s' % (self.type, self.titlelink) - user = get_object_or_404(User, id=user_id) + user = get_object_or_404(models.User, id=user_id) responses = [] - answers = Answer.objects.extra( + answers = models.Answer.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'question.id', @@ -597,7 +607,7 @@ def user_responses(request, user_id, user_view): # question comments - comments = Comment.objects.extra( + comments = models.Comment.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'comment.object_id', @@ -626,7 +636,7 @@ def user_responses(request, user_id, user_view): responses.extend(comments) # answer comments - comments = Comment.objects.extra( + comments = models.Comment.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'question.id', @@ -658,7 +668,7 @@ def user_responses(request, user_id, user_view): responses.extend(comments) # answer has been accepted - answers = Answer.objects.extra( + answers = models.Answer.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'question.id', @@ -702,11 +712,11 @@ def user_responses(request, user_id, user_view): }, context_instance=RequestContext(request)) def user_votes(request, user_id, user_view): - user = get_object_or_404(User, id=user_id) + user = get_object_or_404(models.User, id=user_id) if not auth.can_view_user_votes(request.user, user): raise Http404 votes = [] - question_votes = Vote.objects.extra( + question_votes = models.Vote.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'question.id', @@ -730,7 +740,7 @@ def user_votes(request, user_id, user_view): if(len(question_votes) > 0): votes.extend(question_votes) - answer_votes = Vote.objects.extra( + answer_votes = models.Vote.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'question.id', @@ -765,10 +775,10 @@ def user_votes(request, user_id, user_view): }, context_instance=RequestContext(request)) def user_reputation(request, user_id, user_view): - user = get_object_or_404(User, id=user_id) + user = get_object_or_404(models.User, id=user_id) try: from django.db.models import Sum - reputation = Repute.objects.extra( + reputation = models.Repute.objects.extra( select={'question_id':'question_id', 'title': 'question.title'}, tables=['repute', 'question'], @@ -778,7 +788,7 @@ def user_reputation(request, user_id, user_view): ).values('question_id', 'title', 'reputed_at', 'reputation') reputation = reputation.annotate(positive=Sum("positive"), negative=Sum("negative")) except ImportError: - reputation = Repute.objects.extra( + reputation = models.Repute.objects.extra( select={'positive':'sum(positive)', 'negative':'sum(negative)', 'question_id':'question_id', 'title': 'question.title'}, tables=['repute', 'question'], @@ -789,7 +799,7 @@ def user_reputation(request, user_id, user_view): reputation.query.group_by = ['question_id'] rep_list = [] - for rep in Repute.objects.filter(user=user).order_by('reputed_at'): + for rep in models.Repute.objects.filter(user=user).order_by('reputed_at'): dic = '[%s,%s]' % (calendar.timegm(rep.reputed_at.timetuple()) * 1000, rep.reputation) rep_list.append(dic) reps = ','.join(rep_list) @@ -806,8 +816,8 @@ def user_reputation(request, user_id, user_view): }, context_instance=RequestContext(request)) def user_favorites(request, user_id, user_view): - user = get_object_or_404(User, id=user_id) - questions = Question.objects.extra( + user = get_object_or_404(models.User, id=user_id) + questions = models.Question.objects.extra( select={ 'score' : 'question.vote_up_count + question.vote_down_count', 'favorited_myself' : 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s '+ @@ -858,12 +868,12 @@ def user_favorites(request, user_id, user_view): @login_required def user_email_subscriptions(request, user_id, user_view): - user = get_object_or_404(User, id=user_id) + user = get_object_or_404(models.User, id=user_id) if request.user != user: raise Http404 if request.method == 'POST': - email_feeds_form = EditUserEmailFeedsForm(request.POST) - tag_filter_form = TagFilterSelectionForm(request.POST, instance=user) + email_feeds_form = forms.EditUserEmailFeedsForm(request.POST) + tag_filter_form = forms.TagFilterSelectionForm(request.POST, instance=user) if email_feeds_form.is_valid() and tag_filter_form.is_valid(): action_status = None @@ -876,14 +886,14 @@ def user_email_subscriptions(request, user_id, user_view): action_status = _('changes saved') elif 'stop_email' in request.POST: email_stopped = email_feeds_form.reset().save(user) - initial_values = EditUserEmailFeedsForm.NO_EMAIL_INITIAL - email_feeds_form = EditUserEmailFeedsForm(initial=initial_values) + initial_values = forms.EditUserEmailFeedsForm.NO_EMAIL_INITIAL + email_feeds_form = forms.EditUserEmailFeedsForm(initial=initial_values) if email_stopped: action_status = _('email updates canceled') else: - email_feeds_form = EditUserEmailFeedsForm() + email_feeds_form = forms.EditUserEmailFeedsForm() email_feeds_form.set_initial_values(user) - tag_filter_form = TagFilterSelectionForm(instance=user) + tag_filter_form = forms.TagFilterSelectionForm(instance=user) action_status = None return render_to_response(user_view.template_file,{ 'active_tab':'users', diff --git a/locale/en/LC_MESSAGES/django.mo b/locale/en/LC_MESSAGES/django.mo Binary files differindex 9788abbd..a21172b6 100644 --- a/locale/en/LC_MESSAGES/django.mo +++ b/locale/en/LC_MESSAGES/django.mo diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index ff46a2cb..44fc1454 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-05-17 21:17-0400\n" +"POT-Creation-Date: 2010-05-23 17:31-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Evgeny Fadeev <evgeny.fadeev@gmail.com>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -1225,60 +1225,60 @@ msgstr "" msgid "mentioned in the post" msgstr "" -#: forum/const/__init__.py:141 +#: forum/const/__init__.py:148 msgid "question_answered" msgstr "answered question" -#: forum/const/__init__.py:142 +#: forum/const/__init__.py:149 msgid "question_commented" msgstr "commented question" -#: forum/const/__init__.py:143 +#: forum/const/__init__.py:150 msgid "answer_commented" msgstr "" -#: forum/const/__init__.py:144 +#: forum/const/__init__.py:151 msgid "answer_accepted" msgstr "" -#: forum/const/__init__.py:148 +#: forum/const/__init__.py:155 msgid "[closed]" msgstr "" -#: forum/const/__init__.py:149 +#: forum/const/__init__.py:156 msgid "[deleted]" msgstr "" -#: forum/const/__init__.py:150 forum/views/readers.py:395 +#: forum/const/__init__.py:157 forum/views/readers.py:395 #: forum/views/readers.py:416 msgid "initial version" msgstr "" -#: forum/const/__init__.py:151 +#: forum/const/__init__.py:158 msgid "retagged" msgstr "" -#: forum/const/__init__.py:156 +#: forum/const/__init__.py:163 msgid "exclude ignored tags" msgstr "" -#: forum/const/__init__.py:157 +#: forum/const/__init__.py:164 msgid "allow only selected tags" msgstr "" -#: forum/const/__init__.py:161 +#: forum/const/__init__.py:168 msgid "instantly" msgstr "" -#: forum/const/__init__.py:162 +#: forum/const/__init__.py:169 msgid "daily" msgstr "" -#: forum/const/__init__.py:163 +#: forum/const/__init__.py:170 msgid "weekly" msgstr "" -#: forum/const/__init__.py:164 +#: forum/const/__init__.py:171 msgid "no email" msgstr "" @@ -1781,27 +1781,27 @@ msgstr "" msgid "Created a tag used by 50 questions" msgstr "" -#: forum/models/question.py:508 +#: forum/models/question.py:517 #, python-format msgid "%(author)s modified the question" msgstr "" -#: forum/models/question.py:512 +#: forum/models/question.py:521 #, python-format msgid "%(people)s posted %(new_answer_count)s new answers" msgstr "" -#: forum/models/question.py:517 +#: forum/models/question.py:526 #, python-format msgid "%(people)s commented the question" msgstr "" -#: forum/models/question.py:522 +#: forum/models/question.py:531 #, python-format msgid "%(people)s commented answers" msgstr "" -#: forum/models/question.py:524 +#: forum/models/question.py:533 #, python-format msgid "%(people)s commented an answer" msgstr "" @@ -1826,39 +1826,39 @@ msgstr "" msgid "ignored" msgstr "" -#: forum/models/user.py:78 +#: forum/models/user.py:217 msgid "Entire forum" msgstr "" -#: forum/models/user.py:79 +#: forum/models/user.py:218 msgid "Questions that I asked" msgstr "" -#: forum/models/user.py:80 +#: forum/models/user.py:219 msgid "Questions that I answered" msgstr "" -#: forum/models/user.py:81 +#: forum/models/user.py:220 msgid "Individually selected questions" msgstr "" -#: forum/models/user.py:82 +#: forum/models/user.py:221 msgid "Mentions and comment responses" msgstr "" -#: forum/models/user.py:85 +#: forum/models/user.py:224 msgid "Instantly" msgstr "" -#: forum/models/user.py:86 +#: forum/models/user.py:225 msgid "Daily" msgstr "" -#: forum/models/user.py:87 +#: forum/models/user.py:226 msgid "Weekly" msgstr "" -#: forum/models/user.py:88 +#: forum/models/user.py:227 msgid "No email" msgstr "" @@ -2708,6 +2708,35 @@ msgstr "" msgid "search" msgstr "" +#: forum/skins/default/templates/instant_notification.html:16 +#, python-format +msgid "Dear %(user_name)s," +msgstr "" + +#: forum/skins/default/templates/instant_notification.html:20 +#, python-format +msgid "" +"\n" +"%(author_link)s has left you a <a href=\"%(update_url|safe)s\">comment</a>\n" +"related to question <a href=\"%(origin_post_url|safe)s\">%(origin_post_title)" +"s</a>\n" +msgstr "" + +#: forum/skins/default/templates/instant_notification.html:27 +#, python-format +msgid "" +"\n" +"%(author_link)s has left a new <a href=\"%(update_url|safe)s\">comment</a>\n" +"related to question <a href=\"%(origin_post_url|safe)s\">%(origin_post_title)" +"s</a>\n" +msgstr "" + +#: forum/skins/default/templates/instant_notification.html:35 +msgid "" +"\n" +"\n" +msgstr "" + #: forum/skins/default/templates/logout.html:6 #: forum/skins/default/templates/logout.html:16 msgid "Logout" @@ -3034,7 +3063,7 @@ msgid "Question tags" msgstr "Tags" #: forum/skins/default/templates/question.html:467 -#: forum/skins/default/templates/questions.html:257 +#: forum/skins/default/templates/questions.html:261 #: forum/skins/default/templates/tag_selector.html:11 #: forum/skins/default/templates/tag_selector.html:28 #, python-format @@ -3243,102 +3272,112 @@ msgstr "" #: forum/skins/default/templates/questions.html:141 #, python-format -msgid " %(q_num)s question found" -msgid_plural "%(q_num)s questions found" -msgstr[0] "One question found" +msgid "" +"\n" +" %(q_num)s question found\n" +" " +msgid_plural "" +"\n" +" %(q_num)s questions found\n" +" " +msgstr[0] "" +"\n" +"<div class=\"questions-count\">%(q_num)s</div><p>question</p>" msgstr[1] "" +"\n" +"<div class=\"questions-count\">%(q_num)s</div><p>questions<p>" -#: forum/skins/default/templates/questions.html:143 +#: forum/skins/default/templates/questions.html:147 #, python-format msgid "%(q_num)s question" msgid_plural "%(q_num)s questions" msgstr[0] "" msgstr[1] "" -#: forum/skins/default/templates/questions.html:147 +#: forum/skins/default/templates/questions.html:151 #, python-format msgid "with %(author_name)s's contributions" msgstr "" -#: forum/skins/default/templates/questions.html:151 +#: forum/skins/default/templates/questions.html:155 msgid "tagged" msgstr "" -#: forum/skins/default/templates/questions.html:157 +#: forum/skins/default/templates/questions.html:161 msgid "Search tips:" msgstr "" -#: forum/skins/default/templates/questions.html:161 +#: forum/skins/default/templates/questions.html:165 msgid "reset author" msgstr "" -#: forum/skins/default/templates/questions.html:165 +#: forum/skins/default/templates/questions.html:169 msgid "reset tags" msgstr "" -#: forum/skins/default/templates/questions.html:169 #: forum/skins/default/templates/questions.html:173 +#: forum/skins/default/templates/questions.html:177 msgid "start over" msgstr "" -#: forum/skins/default/templates/questions.html:175 +#: forum/skins/default/templates/questions.html:179 msgid " - to expand, or dig in by adding more tags and revising the query." msgstr "" -#: forum/skins/default/templates/questions.html:178 +#: forum/skins/default/templates/questions.html:182 msgid "Search tip:" msgstr "" -#: forum/skins/default/templates/questions.html:178 +#: forum/skins/default/templates/questions.html:182 msgid "add tags and a query to focus your search" msgstr "" -#: forum/skins/default/templates/questions.html:190 +#: forum/skins/default/templates/questions.html:194 msgid "There are no unanswered questions here" msgstr "" -#: forum/skins/default/templates/questions.html:193 +#: forum/skins/default/templates/questions.html:197 msgid "No favorite questions here. " msgstr "" -#: forum/skins/default/templates/questions.html:194 +#: forum/skins/default/templates/questions.html:198 msgid "Please start (bookmark) some questions when you visit them" msgstr "" -#: forum/skins/default/templates/questions.html:199 +#: forum/skins/default/templates/questions.html:203 msgid "You can expand your search by " msgstr "" -#: forum/skins/default/templates/questions.html:203 +#: forum/skins/default/templates/questions.html:207 msgid "resetting author" msgstr "" -#: forum/skins/default/templates/questions.html:207 +#: forum/skins/default/templates/questions.html:211 msgid "resetting tags" msgstr "" -#: forum/skins/default/templates/questions.html:211 #: forum/skins/default/templates/questions.html:215 +#: forum/skins/default/templates/questions.html:219 msgid "starting over" msgstr "" -#: forum/skins/default/templates/questions.html:220 +#: forum/skins/default/templates/questions.html:224 msgid "Please always feel free to ask your question!" msgstr "" -#: forum/skins/default/templates/questions.html:224 +#: forum/skins/default/templates/questions.html:228 msgid "Did not find what you were looking for?" msgstr "" -#: forum/skins/default/templates/questions.html:225 +#: forum/skins/default/templates/questions.html:229 msgid "Please, post your question!" msgstr "" -#: forum/skins/default/templates/questions.html:241 +#: forum/skins/default/templates/questions.html:245 msgid "Contributors" msgstr "" -#: forum/skins/default/templates/questions.html:254 +#: forum/skins/default/templates/questions.html:258 msgid "Related tags" msgstr "Tags" @@ -3485,7 +3524,7 @@ msgid "change picture" msgstr "" #: forum/skins/default/templates/user_info.html:25 -#: forum/skins/default/templates/users.html:26 forum/views/users.py:938 +#: forum/skins/default/templates/users.html:26 forum/views/users.py:948 msgid "reputation" msgstr "karma" @@ -3619,19 +3658,19 @@ msgstr[1] "" msgid "User profile" msgstr "" -#: forum/skins/default/templates/user_tabs.html:7 forum/views/users.py:912 +#: forum/skins/default/templates/user_tabs.html:7 forum/views/users.py:922 msgid "overview" msgstr "" -#: forum/skins/default/templates/user_tabs.html:9 forum/views/users.py:920 +#: forum/skins/default/templates/user_tabs.html:9 forum/views/users.py:930 msgid "recent activity" msgstr "" -#: forum/skins/default/templates/user_tabs.html:12 forum/views/users.py:930 +#: forum/skins/default/templates/user_tabs.html:12 forum/views/users.py:940 msgid "comments and answers to others questions" msgstr "" -#: forum/skins/default/templates/user_tabs.html:13 forum/views/users.py:929 +#: forum/skins/default/templates/user_tabs.html:13 forum/views/users.py:939 msgid "responses" msgstr "" @@ -3643,11 +3682,11 @@ msgstr "Graph of user karma" msgid "reputation history" msgstr "karma history" -#: forum/skins/default/templates/user_tabs.html:20 forum/views/users.py:956 +#: forum/skins/default/templates/user_tabs.html:20 forum/views/users.py:966 msgid "user vote record" msgstr "" -#: forum/skins/default/templates/user_tabs.html:20 forum/views/users.py:955 +#: forum/skins/default/templates/user_tabs.html:20 forum/views/users.py:965 msgid "casted votes" msgstr "votes" @@ -3659,11 +3698,11 @@ msgstr "" msgid "favorites" msgstr "" -#: forum/skins/default/templates/user_tabs.html:27 forum/views/users.py:965 +#: forum/skins/default/templates/user_tabs.html:27 forum/views/users.py:975 msgid "email subscription settings" msgstr "" -#: forum/skins/default/templates/user_tabs.html:28 forum/views/users.py:964 +#: forum/skins/default/templates/user_tabs.html:28 forum/views/users.py:974 msgid "email subscriptions" msgstr "subscriptions" @@ -4378,59 +4417,59 @@ msgstr "" msgid "We look forward to hearing your feedback! Please, give it next time :)" msgstr "" -#: forum/views/users.py:872 forum/views/users.py:876 +#: forum/views/users.py:882 forum/views/users.py:886 msgid "changes saved" msgstr "" -#: forum/views/users.py:882 +#: forum/views/users.py:892 msgid "email updates canceled" msgstr "" -#: forum/views/users.py:913 +#: forum/views/users.py:923 msgid "user profile" msgstr "" -#: forum/views/users.py:914 +#: forum/views/users.py:924 msgid "user profile overview" msgstr "" -#: forum/views/users.py:921 +#: forum/views/users.py:931 msgid "recent user activity" msgstr "" -#: forum/views/users.py:922 +#: forum/views/users.py:932 msgid "profile - recent activity" msgstr "" -#: forum/views/users.py:931 +#: forum/views/users.py:941 msgid "profile - responses" msgstr "" -#: forum/views/users.py:939 +#: forum/views/users.py:949 msgid "user reputation in the community" msgstr "user karma" -#: forum/views/users.py:940 +#: forum/views/users.py:950 msgid "profile - user reputation" msgstr "Profile - User's Karma" -#: forum/views/users.py:946 +#: forum/views/users.py:956 msgid "favorite questions" msgstr "" -#: forum/views/users.py:947 +#: forum/views/users.py:957 msgid "users favorite questions" msgstr "" -#: forum/views/users.py:948 +#: forum/views/users.py:958 msgid "profile - favorite questions" msgstr "" -#: forum/views/users.py:957 +#: forum/views/users.py:967 msgid "profile - votes" msgstr "" -#: forum/views/users.py:966 +#: forum/views/users.py:976 msgid "profile - email subscriptions" msgstr "" @@ -4605,6 +4644,11 @@ msgstr "" msgid "Uncollapse all" msgstr "" +#~ msgid "%(q_num)s question found" +#~ msgid_plural "%(q_num)s questions found" +#~ msgstr[0] "One question found" +#~ msgstr[1] "" + #~ msgid "unanswered questions" #~ msgstr "unanswered" @@ -4715,18 +4759,3 @@ msgstr "" #~ "\n" #~ "<div class=\"questions-count\">%(q_num)s</div><p>questions without an " #~ "accepted answer</p>" - -#~ msgid "" -#~ "\n" -#~ " have total %(q_num)s questions\n" -#~ " " -#~ msgid_plural "" -#~ "\n" -#~ " have total %(q_num)s questions\n" -#~ " " -#~ msgstr[0] "" -#~ "\n" -#~ "<div class=\"questions-count\">%(q_num)s</div><p>question</p>" -#~ msgstr[1] "" -#~ "\n" -#~ "<div class=\"questions-count\">%(q_num)s</div><p>questions<p>" |