summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--forum/const/__init__.py9
-rw-r--r--forum/management/commands/multi_award_badges.py79
-rw-r--r--forum/management/commands/once_award_badges.py99
-rw-r--r--forum/management/commands/send_email_alerts.py17
-rw-r--r--forum/migrations/0009_calculate_html_field_for_comments.py2
-rw-r--r--forum/migrations/0011_merge_mentions_into_activity.py376
-rw-r--r--forum/models/__init__.py41
-rw-r--r--forum/models/answer.py6
-rw-r--r--forum/models/base.py29
-rw-r--r--forum/models/meta.py17
-rw-r--r--forum/models/question.py9
-rw-r--r--forum/models/user.py213
-rw-r--r--forum/skins/default/templates/instant_notification.html46
-rw-r--r--forum/skins/default/templates/questions.html6
-rw-r--r--forum/utils/functions.py12
-rw-r--r--forum/utils/markup.py43
-rw-r--r--forum/views/users.py158
-rw-r--r--locale/en/LC_MESSAGES/django.mobin23857 -> 24018 bytes
-rw-r--r--locale/en/LC_MESSAGES/django.po215
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
index 9788abbd..a21172b6 100644
--- a/locale/en/LC_MESSAGES/django.mo
+++ b/locale/en/LC_MESSAGES/django.mo
Binary files differ
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>"