3 #-------------------------------------------------------------------------------
4 # Name: Award badges command
5 # Purpose: This is a command file croning in background process regularly to
6 # query database and award badges for user's special acitivities.
8 # Author: Mike, Sailing
11 # Copyright: (c) Mike 2009
13 #-------------------------------------------------------------------------------
15 from datetime import datetime, date
16 from django.db import connection
17 from django.shortcuts import get_object_or_404
18 from django.contrib.contenttypes.models import ContentType
20 from forum.models import *
21 from forum.const import *
22 from base_command import BaseCommand
24 (1, '炼狱法师', 3, '炼狱法师', '删除自己有3个以上赞成票的帖子', 1, 0),
25 (2, '压力白领', 3, '压力白领', '删除自己有3个以上反对票的帖子', 1, 0),
26 (3, '优秀回答', 3, '优秀回答', '回答好评10次以上', 1, 0),
27 (4, '优秀问题', 3, '优秀问题', '问题好评10次以上', 1, 0),
28 (5, '评论家', 3, '评论家', '评论10次以上', 0, 0),
29 (6, '流行问题', 3, '流行问题', '问题的浏览量超过1000人次', 1, 0),
30 (7, '巡逻兵', 3, '巡逻兵', '第一次标记垃圾帖子', 0, 0),
31 (8, '清洁工', 3, '清洁工', '第一次撤销投票', 0, 0),
32 (9, '批评家', 3, '批评家', '第一次反对票', 0, 0),
33 (10, '小编', 3, '小编', '第一次编辑更新', 0, 0),
34 (11, '村长', 3, '村长', '第一次重新标签', 0, 0),
35 (12, '学者', 3, '学者', '第一次标记答案', 0, 0),
36 (13, '学生', 3, '学生', '第一次提问并且有一次以上赞成票', 0, 0),
37 (14, '支持者', 3, '支持者', '第一次赞成票', 0, 0),
38 (15, '教师', 3, '教师', '第一次回答问题并且得到一个以上赞成票', 0, 0),
39 (16, '自传作者', 3, '自传作者', '完整填写用户资料所有选项', 0, 0),
40 (17, '自学成才', 3, '自学成才', '回答自己的问题并且有3个以上赞成票', 1, 0),
41 (18, '最有价值回答', 1, '最有价值回答', '回答超过100次赞成票', 1, 0),
42 (19, '最有价值问题', 1, '最有价值问题', '问题超过100次赞成票', 1, 0),
43 (20, '万人迷', 1, '万人迷', '问题被100人以上收藏', 1, 0),
44 (21, '著名问题', 1, '著名问题', '问题的浏览量超过10000人次', 1, 0),
45 (22, 'alpha用户', 2, 'alpha用户', '内测期间的活跃用户', 0, 0),
46 (23, '极好回答', 2, '极好回答', '回答超过25次赞成票', 1, 0),
47 (24, '极好问题', 2, '极好问题', '问题超过25次赞成票', 1, 0),
48 (25, '受欢迎问题', 2, '受欢迎问题', '问题被25人以上收藏', 1, 0),
49 (26, '优秀市民', 2, '优秀市民', '投票300次以上', 0, 0),
50 (27, '编辑主任', 2, '编辑主任', '编辑了100个帖子', 0, 0),
51 (28, '通才', 2, '通才', '在多个标签领域活跃', 0, 0),
52 (29, '专家', 2, '专家', '在一个标签领域活跃出众', 0, 0),
53 (30, '老鸟', 2, '老鸟', '活跃超过一年的用户', 0, 0),
54 (31, '最受关注问题', 2, '最受关注问题', '问题的浏览量超过2500人次', 1, 0),
55 (32, '学问家', 2, '学问家', '第一次回答被投赞成票10次以上', 0, 0),
56 (33, 'beta用户', 2, 'beta用户', 'beta期间活跃参与', 0, 0),
57 (34, '导师', 2, '导师', '被指定为最佳答案并且赞成票40以上', 1, 0),
58 (35, '巫师', 2, '巫师', '在提问60天之后回答并且赞成票5次以上', 1, 0),
59 (36, '分类专家', 2, '分类专家', '创建的标签被50个以上问题使用', 1, 0);
62 TYPE_ACTIVITY_ASK_QUESTION=1
63 TYPE_ACTIVITY_ANSWER=2
64 TYPE_ACTIVITY_COMMENT_QUESTION=3
65 TYPE_ACTIVITY_COMMENT_ANSWER=4
66 TYPE_ACTIVITY_UPDATE_QUESTION=5
67 TYPE_ACTIVITY_UPDATE_ANSWER=6
69 TYPE_ACTIVITY_MARK_ANSWER=8
70 TYPE_ACTIVITY_VOTE_UP=9
71 TYPE_ACTIVITY_VOTE_DOWN=10
72 TYPE_ACTIVITY_CANCEL_VOTE=11
73 TYPE_ACTIVITY_DELETE_QUESTION=12
74 TYPE_ACTIVITY_DELETE_ANSWER=13
75 TYPE_ACTIVITY_MARK_OFFENSIVE=14
76 TYPE_ACTIVITY_UPDATE_TAGS=15
77 TYPE_ACTIVITY_FAVORITE=16
78 TYPE_ACTIVITY_USER_FULL_UPDATED = 17
81 BADGE_AWARD_TYPE_FIRST = {
82 TYPE_ACTIVITY_MARK_OFFENSIVE : 7,
83 TYPE_ACTIVITY_CANCEL_VOTE: 8,
84 TYPE_ACTIVITY_VOTE_DOWN : 9,
85 TYPE_ACTIVITY_UPDATE_QUESTION : 10,
86 TYPE_ACTIVITY_UPDATE_ANSWER : 10,
87 TYPE_ACTIVITY_UPDATE_TAGS : 11,
88 TYPE_ACTIVITY_MARK_ANSWER : 12,
89 TYPE_ACTIVITY_VOTE_UP : 14,
90 TYPE_ACTIVITY_USER_FULL_UPDATED: 16
94 class Command(BaseCommand):
95 def handle_noargs(self, **options):
100 self.first_type_award()
101 self.first_ask_be_voted()
102 self.first_answer_be_voted()
103 self.first_answer_be_voted_10()
104 self.vote_count_300()
105 self.edit_count_100()
106 self.comment_count_10()
112 def alpha_user(self):
114 Before Jan 25, 2009(Chinese New Year Eve and enter into Beta for CNProg), every registered user
115 will be awarded the "Alpha" badge if he has any activities.
117 alpha_end_date = date(2009, 1, 25)
118 if date.today() < alpha_end_date:
119 badge = get_object_or_404(Badge, id=22)
120 for user in User.objects.all():
121 award = Award.objects.filter(user=user, badge=badge)
122 if award and not badge.multiple:
124 activities = Activity.objects.filter(user=user)
125 if len(activities) > 0:
126 new_award = Award(user=user, badge=badge)
131 Before Feb 25, 2009, every registered user
132 will be awarded the "Beta" badge if he has any activities.
134 beta_end_date = date(2009, 2, 25)
135 if date.today() < beta_end_date:
136 badge = get_object_or_404(Badge, id=33)
137 for user in User.objects.all():
138 award = Award.objects.filter(user=user, badge=badge)
139 if award and not badge.multiple:
141 activities = Activity.objects.filter(user=user)
142 if len(activities) > 0:
143 new_award = Award(user=user, badge=badge)
146 def first_type_award(self):
148 This will award below badges for users first behaviors:
150 (7, '巡逻兵', 3, '巡逻兵', '第一次标记垃圾帖子', 0, 0),
151 (8, '清洁工', 3, '清洁工', '第一次撤销投票', 0, 0),
152 (9, '批评家', 3, '批评家', '第一次反对票', 0, 0),
153 (10, '小编', 3, '小编', '第一次编辑更新', 0, 0),
154 (11, '村长', 3, '村长', '第一次重新标签', 0, 0),
155 (12, '学者', 3, '学者', '第一次标记答案', 0, 0),
156 (14, '支持者', 3, '支持者', '第一次赞成票', 0, 0),
157 (16, '自传作者', 3, '自传作者', '完整填写用户资料所有选项', 0, 0),
159 activity_types = ','.join('%s' % item for item in BADGE_AWARD_TYPE_FIRST.keys())
160 # ORDER BY user_id, activity_type
161 query = "SELECT id, user_id, activity_type, content_type_id, object_id FROM activity WHERE is_auditted = 0 AND activity_type IN (%s) ORDER BY user_id, activity_type" % activity_types
163 cursor = connection.cursor()
165 cursor.execute(query)
166 rows = cursor.fetchall()
167 # collect activity_id in current process
170 last_activity_type = 0
172 activity_ids.append(row[0])
174 activity_type = row[2]
175 content_type_id = row[3]
178 # if the user and activity are same as the last, continue
179 if user_id == last_user_id and activity_type == last_activity_type:
182 user = get_object_or_404(User, id=user_id)
183 badge = get_object_or_404(Badge, id=BADGE_AWARD_TYPE_FIRST[activity_type])
184 content_type = get_object_or_404(ContentType, id=content_type_id)
186 count = Award.objects.filter(user=user, badge=badge).count()
187 if count and not badge.multiple:
191 award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
194 # set the current user_id and activity_type to last
195 last_user_id = user_id
196 last_activity_type = activity_type
198 # update processed rows to auditted
199 self.update_activities_auditted(cursor, activity_ids)
203 def first_ask_be_voted(self):
205 For user asked question and got first upvote, we award him following badge:
207 (13, '学生', 3, '学生', '第一次提问并且有一次以上赞成票', 0, 0),
209 query = "SELECT act.user_id, q.vote_up_count, act.object_id FROM " \
210 "activity act, question q WHERE act.activity_type = %s AND " \
211 "act.object_id = q.id AND " \
212 "act.user_id NOT IN (SELECT distinct user_id FROM award WHERE badge_id = %s)" % (TYPE_ACTIVITY_ASK_QUESTION, 13)
213 cursor = connection.cursor()
215 cursor.execute(query)
216 rows = cursor.fetchall()
218 badge = get_object_or_404(Badge, id=13)
219 content_type = ContentType.objects.get_for_model(Question)
223 vote_up_count = row[1]
225 if vote_up_count > 0 and user_id not in awarded_users:
226 user = get_object_or_404(User, id=user_id)
227 award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
229 awarded_users.append(user_id)
233 def first_answer_be_voted(self):
235 When user answerd questions and got first upvote, we award him following badge:
237 (15, '教师', 3, '教师', '第一次回答问题并且得到一个以上赞成票', 0, 0),
239 query = "SELECT act.user_id, a.vote_up_count, act.object_id FROM " \
240 "activity act, answer a WHERE act.activity_type = %s AND " \
241 "act.object_id = a.id AND " \
242 "act.user_id NOT IN (SELECT distinct user_id FROM award WHERE badge_id = %s)" % (TYPE_ACTIVITY_ANSWER, 15)
243 cursor = connection.cursor()
245 cursor.execute(query)
246 rows = cursor.fetchall()
249 badge = get_object_or_404(Badge, id=15)
250 content_type = ContentType.objects.get_for_model(Answer)
253 vote_up_count = row[1]
255 if vote_up_count > 0 and user_id not in awarded_users:
256 user = get_object_or_404(User, id=user_id)
257 award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
259 awarded_users.append(user_id)
263 def first_answer_be_voted_10(self):
265 (32, '学问家', 2, '学问家', '第一次回答被投赞成票10次以上', 0, 0)
267 query = "SELECT act.user_id, act.object_id FROM " \
268 "activity act, answer a WHERE act.object_id = a.id AND " \
269 "act.activity_type = %s AND " \
270 "a.vote_up_count >= 10 AND " \
271 "act.user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s)" % (TYPE_ACTIVITY_ANSWER, 32)
272 cursor = connection.cursor()
274 cursor.execute(query)
275 rows = cursor.fetchall()
278 badge = get_object_or_404(Badge, id=32)
279 content_type = ContentType.objects.get_for_model(Answer)
282 if user_id not in awarded_users:
283 user = get_object_or_404(User, id=user_id)
285 award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
287 awarded_users.append(user_id)
291 def vote_count_300(self):
293 (26, '优秀市民', 2, '优秀市民', '投票300次以上', 0, 0)
295 query = "SELECT count(*) vote_count, user_id FROM activity WHERE " \
296 "activity_type = %s OR " \
297 "activity_type = %s AND " \
298 "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " \
299 "GROUP BY user_id HAVING vote_count >= 300" % (TYPE_ACTIVITY_VOTE_UP, TYPE_ACTIVITY_VOTE_DOWN, 26)
301 self.__award_for_count_num(query, 26)
303 def edit_count_100(self):
305 (27, '编辑主任', 2, '编辑主任', '编辑了100个帖子', 0, 0)
307 query = "SELECT count(*) vote_count, user_id FROM activity WHERE " \
308 "activity_type = %s OR " \
309 "activity_type = %s AND " \
310 "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " \
311 "GROUP BY user_id HAVING vote_count >= 100" % (TYPE_ACTIVITY_UPDATE_QUESTION, TYPE_ACTIVITY_UPDATE_ANSWER, 27)
313 self.__award_for_count_num(query, 27)
315 def comment_count_10(self):
317 (5, '评论家', 3, '评论家', '评论10次以上', 0, 0),
319 query = "SELECT count(*) vote_count, user_id FROM activity WHERE " \
320 "activity_type = %s OR " \
321 "activity_type = %s AND " \
322 "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " \
323 "GROUP BY user_id HAVING vote_count >= 10" % (TYPE_ACTIVITY_COMMENT_QUESTION, TYPE_ACTIVITY_COMMENT_ANSWER, 5)
324 self.__award_for_count_num(query, 5)
326 def __award_for_count_num(self, query, badge):
327 cursor = connection.cursor()
329 cursor.execute(query)
330 rows = cursor.fetchall()
333 badge = get_object_or_404(Badge, id=badge)
338 if user_id not in awarded_users:
339 user = get_object_or_404(User, id=user_id)
340 award = Award(user=user, badge=badge)
342 awarded_users.append(user_id)
349 if __name__ == '__main__':