]> git.openstreetmap.org Git - osqa.git/blob - forum/views/users.py
Initial commit
[osqa.git] / forum / views / users.py
1 from django.contrib.auth.decorators import login_required
2 from django.contrib.auth.models import User
3 from django.core.paginator import Paginator, EmptyPage, InvalidPage
4 from django.template.defaultfilters import slugify
5 from django.contrib.contenttypes.models import ContentType
6 from django.core.urlresolvers import reverse
7 from django.shortcuts import render_to_response, get_object_or_404
8 from django.template import RequestContext
9 from django.http import HttpResponse, HttpResponseForbidden, HttpResponseRedirect, Http404
10 from django.utils.translation import ugettext as _
11 from django.utils.http import urlquote_plus
12 from django.utils.html import strip_tags
13 from django.core.urlresolvers import reverse
14 from forum.forms import *#incomplete list is EditUserForm, ModerateUserForm, TagFilterSelectionForm,
15 from forum.utils.html import sanitize_html
16 from forum import auth
17 import calendar
18 from django.contrib.contenttypes.models import ContentType
19
20 question_type = ContentType.objects.get_for_model(Question)
21 answer_type = ContentType.objects.get_for_model(Answer)
22 comment_type = ContentType.objects.get_for_model(Comment)
23 question_revision_type = ContentType.objects.get_for_model(QuestionRevision)
24 answer_revision_type = ContentType.objects.get_for_model(AnswerRevision)
25 repute_type = ContentType.objects.get_for_model(Repute)
26 question_type_id = question_type.id
27 answer_type_id = answer_type.id
28 comment_type_id = comment_type.id
29 question_revision_type_id = question_revision_type.id
30 answer_revision_type_id = answer_revision_type.id
31 repute_type_id = repute_type.id
32
33 USERS_PAGE_SIZE = 35# refactor - move to some constants file
34
35 def users(request):
36     is_paginated = True
37     sortby = request.GET.get('sort', 'reputation')
38     suser = request.REQUEST.get('q',  "")
39     try:
40         page = int(request.GET.get('page', '1'))
41     except ValueError:
42         page = 1
43
44     if suser == "":
45         if sortby == "newest":
46             objects_list = Paginator(User.objects.all().order_by('-date_joined'), USERS_PAGE_SIZE)
47         elif sortby == "last":
48             objects_list = Paginator(User.objects.all().order_by('date_joined'), USERS_PAGE_SIZE)
49         elif sortby == "user":
50             objects_list = Paginator(User.objects.all().order_by('username'), USERS_PAGE_SIZE)
51         # default
52         else:
53             objects_list = Paginator(User.objects.all().order_by('-reputation'), USERS_PAGE_SIZE)
54         base_url = reverse('users') + '?sort=%s&' % sortby
55     else:
56         sortby = "reputation"
57         objects_list = Paginator(User.objects.extra(where=['username like %s'], params=['%' + suser + '%']).order_by('-reputation'), USERS_PAGE_SIZE)
58         base_url = reverse('users') + '?name=%s&sort=%s&' % (suser, sortby)
59
60     try:
61         users = objects_list.page(page)
62     except (EmptyPage, InvalidPage):
63         users = objects_list.page(objects_list.num_pages)
64
65     return render_to_response('users.html', {
66                                 "users" : users,
67                                 "suser" : suser,
68                                 "keywords" : suser,
69                                 "tab_id" : sortby,
70                                 "context" : {
71                                     'is_paginated' : is_paginated,
72                                     'pages': objects_list.num_pages,
73                                     'page': page,
74                                     'has_previous': users.has_previous(),
75                                     'has_next': users.has_next(),
76                                     'previous': users.previous_page_number(),
77                                     'next': users.next_page_number(),
78                                     'base_url' : base_url
79                                 }
80
81                                 }, context_instance=RequestContext(request))
82
83 @login_required
84 def moderate_user(request, id):
85     """ajax handler of user moderation
86     """
87     if not auth.can_moderate_users(request.user) or request.method != 'POST':
88         raise Http404
89     if not request.is_ajax():
90         return HttpResponseForbidden(mimetype="application/json")
91
92     user = get_object_or_404(User, id=id)
93     form = ModerateUserForm(request.POST, instance=user)
94
95     if form.is_valid():
96         form.save()
97         logging.debug('data saved')
98         response = HttpResponse(simplejson.dumps(''), mimetype="application/json")
99     else:
100         response = HttpResponseForbidden(mimetype="application/json")
101     return response
102
103 def set_new_email(user, new_email, nomessage=False):
104     if new_email != user.email:
105         user.email = new_email
106         user.email_isvalid = False
107         user.save()
108         #if settings.EMAIL_VALIDATION == 'on':
109         #    send_new_email_key(user,nomessage=nomessage)    
110
111 @login_required
112 def edit_user(request, id):
113     user = get_object_or_404(User, id=id)
114     if request.user != user:
115         raise Http404
116     if request.method == "POST":
117         form = EditUserForm(user, request.POST)
118         if form.is_valid():
119             new_email = sanitize_html(form.cleaned_data['email'])
120
121             set_new_email(user, new_email)
122
123             #user.username = sanitize_html(form.cleaned_data['username'])
124             user.real_name = sanitize_html(form.cleaned_data['realname'])
125             user.website = sanitize_html(form.cleaned_data['website'])
126             user.location = sanitize_html(form.cleaned_data['city'])
127             user.date_of_birth = sanitize_html(form.cleaned_data['birthday'])
128             if len(user.date_of_birth) == 0:
129                 user.date_of_birth = '1900-01-01'
130             user.about = sanitize_html(form.cleaned_data['about'])
131
132             user.save()
133             # send user updated singal if full fields have been updated
134             if user.email and user.real_name and user.website and user.location and \
135                 user.date_of_birth and user.about:
136                 user_updated.send(sender=user.__class__, instance=user, updated_by=user)
137             return HttpResponseRedirect(user.get_profile_url())
138     else:
139         form = EditUserForm(user)
140     return render_to_response('user_edit.html', {
141                                                 'form' : form,
142                                                 'gravatar_faq_url' : reverse('faq') + '#gravatar',
143                                     }, context_instance=RequestContext(request))
144
145 def user_stats(request, user_id, user_view):
146     user = get_object_or_404(User, id=user_id)
147     questions = Question.objects.extra(
148         select={
149             'vote_count' : 'question.score',
150             'favorited_myself' : 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s AND f.question_id = question.id',
151             'la_user_id' : 'auth_user.id',
152             'la_username' : 'auth_user.username',
153             'la_user_gold' : 'auth_user.gold',
154             'la_user_silver' : 'auth_user.silver',
155             'la_user_bronze' : 'auth_user.bronze',
156             'la_user_reputation' : 'auth_user.reputation'
157             },
158         select_params=[user_id],
159         tables=['question', 'auth_user'],
160         where=['question.deleted=False AND question.author_id=%s AND question.last_activity_by_id = auth_user.id'],
161         params=[user_id],
162         order_by=['-vote_count', '-last_activity_at']
163     ).values('vote_count',
164              'favorited_myself',
165              'id',
166              'title',
167              'author_id',
168              'added_at',
169              'answer_accepted',
170              'answer_count',
171              'comment_count',
172              'view_count',
173              'favourite_count',
174              'summary',
175              'tagnames',
176              'vote_up_count',
177              'vote_down_count',
178              'last_activity_at',
179              'la_user_id',
180              'la_username',
181              'la_user_gold',
182              'la_user_silver',
183              'la_user_bronze',
184              'la_user_reputation')[:100]
185
186     answered_questions = Question.objects.extra(
187         select={
188             'vote_up_count' : 'answer.vote_up_count',
189             'vote_down_count' : 'answer.vote_down_count',
190             'answer_id' : 'answer.id',
191             'accepted' : 'answer.accepted',
192             'vote_count' : 'answer.score',
193             'comment_count' : 'answer.comment_count'
194             },
195         tables=['question', 'answer'],
196         where=['answer.deleted=False AND question.deleted=False AND answer.author_id=%s AND answer.question_id=question.id'],
197         params=[user_id],
198         order_by=['-vote_count', '-answer_id'],
199         select_params=[user_id]
200     ).distinct().values('comment_count',
201                         'id',
202                         'answer_id',
203                         'title',
204                         'author_id',
205                         'accepted',
206                         'vote_count',
207                         'answer_count',
208                         'vote_up_count',
209                         'vote_down_count')[:100]
210
211     up_votes = Vote.objects.get_up_vote_count_from_user(user)
212     down_votes = Vote.objects.get_down_vote_count_from_user(user)
213     votes_today = Vote.objects.get_votes_count_today_from_user(user)
214     votes_total = auth.VOTE_RULES['scope_votes_per_user_per_day']
215
216     question_id_set = set(map(lambda v: v['id'], list(questions))) \
217                         | set(map(lambda v: v['id'], list(answered_questions)))
218
219     user_tags = Tag.objects.filter(questions__id__in = question_id_set)
220     try:
221         from django.db.models import Count
222         awards = Award.objects.extra(
223                                         select={'id': 'badge.id', 
224                                                 'name':'badge.name', 
225                                                 'description': 'badge.description', 
226                                                 'type': 'badge.type'},
227                                         tables=['award', 'badge'],
228                                         order_by=['-awarded_at'],
229                                         where=['user_id=%s AND badge_id=badge.id'],
230                                         params=[user.id]
231                                     ).values('id', 'name', 'description', 'type')
232         total_awards = awards.count()
233         awards = awards.annotate(count = Count('badge__id'))
234         user_tags = user_tags.annotate(user_tag_usage_count=Count('name'))
235
236     except ImportError:
237         awards = Award.objects.extra(
238                                         select={'id': 'badge.id', 
239                                                 'count': 'count(badge_id)', 
240                                                 'name':'badge.name', 
241                                                 'description': 'badge.description', 
242                                                 'type': 'badge.type'},
243                                         tables=['award', 'badge'],
244                                         order_by=['-awarded_at'],
245                                         where=['user_id=%s AND badge_id=badge.id'],
246                                         params=[user.id]
247                                     ).values('id', 'count', 'name', 'description', 'type')
248         total_awards = awards.count()
249         awards.query.group_by = ['badge_id']
250
251         user_tags = user_tags.extra(
252             select={'user_tag_usage_count': 'COUNT(1)',},
253             order_by=['-user_tag_usage_count'],
254         )
255         user_tags.query.group_by = ['name']
256
257     if auth.can_moderate_users(request.user):
258         moderate_user_form = ModerateUserForm(instance=user)
259     else:
260         moderate_user_form = None
261
262     return render_to_response(user_view.template_file,{
263                                 'moderate_user_form': moderate_user_form,
264                                 "tab_name" : user_view.id,
265                                 "tab_description" : user_view.tab_description,
266                                 "page_title" : user_view.page_title,
267                                 "view_user" : user,
268                                 "questions" : questions,
269                                 "answered_questions" : answered_questions,
270                                 "up_votes" : up_votes,
271                                 "down_votes" : down_votes,
272                                 "total_votes": up_votes + down_votes,
273                                 "votes_today_left": votes_total-votes_today,
274                                 "votes_total_per_day": votes_total,
275                                 "user_tags" : user_tags[:50],
276                                 "awards": awards,
277                                 "total_awards" : total_awards,
278                             }, context_instance=RequestContext(request))
279
280 def user_recent(request, user_id, user_view):
281     user = get_object_or_404(User, id=user_id)
282     def get_type_name(type_id):
283         for item in TYPE_ACTIVITY:
284             if type_id in item:
285                 return item[1]
286
287     class Event:
288         def __init__(self, time, type, title, summary, answer_id, question_id):
289             self.time = time
290             self.type = get_type_name(type)
291             self.type_id = type
292             self.title = title
293             self.summary = summary
294             slug_title = slugify(title)
295             self.title_link = reverse('question', kwargs={'id':question_id}) + u'%s' % slug_title
296             if int(answer_id) > 0:
297                 self.title_link += '#%s' % answer_id
298
299     class AwardEvent:
300         def __init__(self, time, type, id):
301             self.time = time
302             self.type = get_type_name(type)
303             self.type_id = type
304             self.badge = get_object_or_404(Badge, id=id)
305
306     activities = []
307     # ask questions
308     questions = Activity.objects.extra(
309         select={
310             'title' : 'question.title',
311             'question_id' : 'question.id',
312             'active_at' : 'activity.active_at',
313             'activity_type' : 'activity.activity_type'
314             },
315         tables=['activity', 'question'],
316         where=['activity.content_type_id = %s AND activity.object_id = ' +
317             'question.id AND question.deleted=False AND activity.user_id = %s AND activity.activity_type = %s'],
318         params=[question_type_id, user_id, TYPE_ACTIVITY_ASK_QUESTION],
319         order_by=['-activity.active_at']
320     ).values(
321             'title',
322             'question_id',
323             'active_at',
324             'activity_type'
325             )
326     if len(questions) > 0:
327         questions = [(Event(q['active_at'], q['activity_type'], q['title'], '', '0', \
328                       q['question_id'])) for q in questions]
329         activities.extend(questions)
330
331     # answers
332     answers = Activity.objects.extra(
333         select={
334             'title' : 'question.title',
335             'question_id' : 'question.id',
336             'answer_id' : 'answer.id',
337             'active_at' : 'activity.active_at',
338             'activity_type' : 'activity.activity_type'
339             },
340         tables=['activity', 'answer', 'question'],
341         where=['activity.content_type_id = %s AND activity.object_id = answer.id AND ' + 
342             'answer.question_id=question.id AND answer.deleted=False AND activity.user_id=%s AND '+ 
343             'activity.activity_type=%s AND question.deleted=False'],
344         params=[answer_type_id, user_id, TYPE_ACTIVITY_ANSWER],
345         order_by=['-activity.active_at']
346     ).values(
347             'title',
348             'question_id',
349             'answer_id',
350             'active_at',
351             'activity_type'
352             )
353     if len(answers) > 0:
354         answers = [(Event(q['active_at'], q['activity_type'], q['title'], '', q['answer_id'], \
355                     q['question_id'])) for q in answers]
356         activities.extend(answers)
357
358     # question comments
359     comments = Activity.objects.extra(
360         select={
361             'title' : 'question.title',
362             'question_id' : 'comment.object_id',
363             'added_at' : 'comment.added_at',
364             'activity_type' : 'activity.activity_type'
365             },
366         tables=['activity', 'question', 'comment'],
367
368         where=['activity.content_type_id = %s AND activity.object_id = comment.id AND '+
369             'activity.user_id = comment.user_id AND comment.object_id=question.id AND '+
370             'comment.content_type_id=%s AND activity.user_id = %s AND activity.activity_type=%s AND ' +
371             'question.deleted=False'],
372         params=[comment_type_id, question_type_id, user_id, TYPE_ACTIVITY_COMMENT_QUESTION],
373         order_by=['-comment.added_at']
374     ).values(
375             'title',
376             'question_id',
377             'added_at',
378             'activity_type'
379             )
380
381     if len(comments) > 0:
382         comments = [(Event(q['added_at'], q['activity_type'], q['title'], '', '0', \
383                      q['question_id'])) for q in comments]
384         activities.extend(comments)
385
386     # answer comments
387     comments = Activity.objects.extra(
388         select={
389             'title' : 'question.title',
390             'question_id' : 'question.id',
391             'answer_id' : 'answer.id',
392             'added_at' : 'comment.added_at',
393             'activity_type' : 'activity.activity_type'
394             },
395         tables=['activity', 'question', 'answer', 'comment'],
396
397         where=['activity.content_type_id = %s AND activity.object_id = comment.id AND '+
398             'activity.user_id = comment.user_id AND comment.object_id=answer.id AND '+
399             'comment.content_type_id=%s AND question.id = answer.question_id AND '+
400             'activity.user_id = %s AND activity.activity_type=%s AND '+
401             'answer.deleted=False AND question.deleted=False'],
402         params=[comment_type_id, answer_type_id, user_id, TYPE_ACTIVITY_COMMENT_ANSWER],
403         order_by=['-comment.added_at']
404     ).values(
405             'title',
406             'question_id',
407             'answer_id',
408             'added_at',
409             'activity_type'
410             )
411
412     if len(comments) > 0:
413         comments = [(Event(q['added_at'], q['activity_type'], q['title'], '', q['answer_id'], \
414                      q['question_id'])) for q in comments]
415         activities.extend(comments)
416
417     # question revisions
418     revisions = Activity.objects.extra(
419         select={
420             'title' : 'question_revision.title',
421             'question_id' : 'question_revision.question_id',
422             'added_at' : 'activity.active_at',
423             'activity_type' : 'activity.activity_type',
424             'summary' : 'question_revision.summary'
425             },
426         tables=['activity', 'question_revision', 'question'],
427         where=['activity.content_type_id = %s AND activity.object_id = question_revision.id AND '+
428             'question_revision.id=question.id AND question.deleted=False AND '+
429             'activity.user_id = question_revision.author_id AND activity.user_id = %s AND '+
430             'activity.activity_type=%s'],
431         params=[question_revision_type_id, user_id, TYPE_ACTIVITY_UPDATE_QUESTION],
432         order_by=['-activity.active_at']
433     ).values(
434             'title',
435             'question_id',
436             'added_at',
437             'activity_type',
438             'summary'
439             )
440
441     if len(revisions) > 0:
442         revisions = [(Event(q['added_at'], q['activity_type'], q['title'], q['summary'], '0', \
443                       q['question_id'])) for q in revisions]
444         activities.extend(revisions)
445
446     # answer revisions
447     revisions = Activity.objects.extra(
448         select={
449             'title' : 'question.title',
450             'question_id' : 'question.id',
451             'answer_id' : 'answer.id',
452             'added_at' : 'activity.active_at',
453             'activity_type' : 'activity.activity_type',
454             'summary' : 'answer_revision.summary'
455             },
456         tables=['activity', 'answer_revision', 'question', 'answer'],
457
458         where=['activity.content_type_id = %s AND activity.object_id = answer_revision.id AND '+
459             'activity.user_id = answer_revision.author_id AND activity.user_id = %s AND '+
460             'answer_revision.answer_id=answer.id AND answer.question_id = question.id AND '+
461             'question.deleted=False AND answer.deleted=False AND '+
462             'activity.activity_type=%s'],
463         params=[answer_revision_type_id, user_id, TYPE_ACTIVITY_UPDATE_ANSWER],
464         order_by=['-activity.active_at']
465     ).values(
466             'title',
467             'question_id',
468             'added_at',
469             'answer_id',
470             'activity_type',
471             'summary'
472             )
473
474     if len(revisions) > 0:
475         revisions = [(Event(q['added_at'], q['activity_type'], q['title'], q['summary'], \
476                       q['answer_id'], q['question_id'])) for q in revisions]
477         activities.extend(revisions)
478
479     # accepted answers
480     accept_answers = Activity.objects.extra(
481         select={
482             'title' : 'question.title',
483             'question_id' : 'question.id',
484             'added_at' : 'activity.active_at',
485             'activity_type' : 'activity.activity_type',
486             },
487         tables=['activity', 'answer', 'question'],
488         where=['activity.content_type_id = %s AND activity.object_id = answer.id AND '+
489             'activity.user_id = question.author_id AND activity.user_id = %s AND '+
490             'answer.deleted=False AND question.deleted=False AND '+
491             'answer.question_id=question.id AND activity.activity_type=%s'],
492         params=[answer_type_id, user_id, TYPE_ACTIVITY_MARK_ANSWER],
493         order_by=['-activity.active_at']
494     ).values(
495             'title',
496             'question_id',
497             'added_at',
498             'activity_type',
499             )
500     if len(accept_answers) > 0:
501         accept_answers = [(Event(q['added_at'], q['activity_type'], q['title'], '', '0', \
502             q['question_id'])) for q in accept_answers]
503         activities.extend(accept_answers)
504     #award history
505     awards = Activity.objects.extra(
506         select={
507             'badge_id' : 'badge.id',
508             'awarded_at': 'award.awarded_at',
509             'activity_type' : 'activity.activity_type'
510             },
511         tables=['activity', 'award', 'badge'],
512         where=['activity.user_id = award.user_id AND activity.user_id = %s AND '+
513             'award.badge_id=badge.id AND activity.object_id=award.id AND activity.activity_type=%s'],
514         params=[user_id, TYPE_ACTIVITY_PRIZE],
515         order_by=['-activity.active_at']
516     ).values(
517             'badge_id',
518             'awarded_at',
519             'activity_type'
520             )
521     if len(awards) > 0:
522         awards = [(AwardEvent(q['awarded_at'], q['activity_type'], q['badge_id'])) for q in awards]
523         activities.extend(awards)
524
525     activities.sort(lambda x,y: cmp(y.time, x.time))
526
527     return render_to_response(user_view.template_file,{
528                                     "tab_name" : user_view.id,
529                                     "tab_description" : user_view.tab_description,
530                                     "page_title" : user_view.page_title,
531                                     "view_user" : user,
532                                     "activities" : activities[:user_view.data_size]
533                                 }, context_instance=RequestContext(request))
534
535 def user_responses(request, user_id, user_view):
536     """
537     We list answers for question, comments, and answer accepted by others for this user.
538     """
539     class Response:
540         def __init__(self, type, title, question_id, answer_id, time, username, user_id, content):
541             self.type = type
542             self.title = title
543             self.titlelink = reverse('question', args=[question_id]) + u'%s#%s' % (slugify(title), answer_id)
544             self.time = time
545             self.userlink = reverse('users') + u'%s/%s/' % (user_id, username)
546             self.username = username
547             self.content = u'%s ...' % strip_tags(content)[:300]
548
549         def __unicode__(self):
550             return u'%s %s' % (self.type, self.titlelink)
551
552     user = get_object_or_404(User, id=user_id)
553     responses = []
554     answers = Answer.objects.extra(
555                                     select={
556                                         'title' : 'question.title',
557                                         'question_id' : 'question.id',
558                                         'answer_id' : 'answer.id',
559                                         'added_at' : 'answer.added_at',
560                                         'html' : 'answer.html',
561                                         'username' : 'auth_user.username',
562                                         'user_id' : 'auth_user.id'
563                                         },
564                                     select_params=[user_id],
565                                     tables=['answer', 'question', 'auth_user'],
566                                     where=['answer.question_id = question.id AND answer.deleted=False AND question.deleted=False AND '+
567                                         'question.author_id = %s AND answer.author_id <> %s AND answer.author_id=auth_user.id'],
568                                     params=[user_id, user_id],
569                                     order_by=['-answer.id']
570                                 ).values(
571                                         'title',
572                                         'question_id',
573                                         'answer_id',
574                                         'added_at',
575                                         'html',
576                                         'username',
577                                         'user_id'
578                                         )
579     if len(answers) > 0:
580         answers = [(Response(TYPE_RESPONSE['QUESTION_ANSWERED'], a['title'], a['question_id'],
581         a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers]
582         responses.extend(answers)
583
584
585     # question comments
586     comments = Comment.objects.extra(
587                                 select={
588                                     'title' : 'question.title',
589                                     'question_id' : 'comment.object_id',
590                                     'added_at' : 'comment.added_at',
591                                     'comment' : 'comment.comment',
592                                     'username' : 'auth_user.username',
593                                     'user_id' : 'auth_user.id'
594                                     },
595                                 tables=['question', 'auth_user', 'comment'],
596                                 where=['question.deleted=False AND question.author_id = %s AND comment.object_id=question.id AND '+
597                                     'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id'],
598                                 params=[user_id, question_type_id, user_id],
599                                 order_by=['-comment.added_at']
600                             ).values(
601                                     'title',
602                                     'question_id',
603                                     'added_at',
604                                     'comment',
605                                     'username',
606                                     'user_id'
607                                     )
608
609     if len(comments) > 0:
610         comments = [(Response(TYPE_RESPONSE['QUESTION_COMMENTED'], c['title'], c['question_id'],
611             '', c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments]
612         responses.extend(comments)
613
614     # answer comments
615     comments = Comment.objects.extra(
616         select={
617             'title' : 'question.title',
618             'question_id' : 'question.id',
619             'answer_id' : 'answer.id',
620             'added_at' : 'comment.added_at',
621             'comment' : 'comment.comment',
622             'username' : 'auth_user.username',
623             'user_id' : 'auth_user.id'
624             },
625         tables=['answer', 'auth_user', 'comment', 'question'],
626         where=['answer.deleted=False AND answer.author_id = %s AND comment.object_id=answer.id AND '+
627             'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id '+
628             'AND question.id = answer.question_id'],
629         params=[user_id, answer_type_id, user_id],
630         order_by=['-comment.added_at']
631     ).values(
632             'title',
633             'question_id',
634             'answer_id',
635             'added_at',
636             'comment',
637             'username',
638             'user_id'
639             )
640
641     if len(comments) > 0:
642         comments = [(Response(TYPE_RESPONSE['ANSWER_COMMENTED'], c['title'], c['question_id'],
643         c['answer_id'], c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments]
644         responses.extend(comments)
645
646     # answer has been accepted
647     answers = Answer.objects.extra(
648         select={
649             'title' : 'question.title',
650             'question_id' : 'question.id',
651             'answer_id' : 'answer.id',
652             'added_at' : 'answer.accepted_at',
653             'html' : 'answer.html',
654             'username' : 'auth_user.username',
655             'user_id' : 'auth_user.id'
656             },
657         select_params=[user_id],
658         tables=['answer', 'question', 'auth_user'],
659         where=['answer.question_id = question.id AND answer.deleted=False AND question.deleted=False AND '+
660             'answer.author_id = %s AND answer.accepted=True AND question.author_id=auth_user.id'],
661         params=[user_id],
662         order_by=['-answer.id']
663     ).values(
664             'title',
665             'question_id',
666             'answer_id',
667             'added_at',
668             'html',
669             'username',
670             'user_id'
671             )
672     if len(answers) > 0:
673         answers = [(Response(TYPE_RESPONSE['ANSWER_ACCEPTED'], a['title'], a['question_id'],
674             a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers]
675         responses.extend(answers)
676
677     # sort posts by time
678     responses.sort(lambda x,y: cmp(y.time, x.time))
679
680     return render_to_response(user_view.template_file,{
681         "tab_name" : user_view.id,
682         "tab_description" : user_view.tab_description,
683         "page_title" : user_view.page_title,
684         "view_user" : user,
685         "responses" : responses[:user_view.data_size],
686
687     }, context_instance=RequestContext(request))
688
689 def user_votes(request, user_id, user_view):
690     user = get_object_or_404(User, id=user_id)
691     if not auth.can_view_user_votes(request.user, user):
692         raise Http404
693     votes = []
694     question_votes = Vote.objects.extra(
695         select={
696             'title' : 'question.title',
697             'question_id' : 'question.id',
698             'answer_id' : 0,
699             'voted_at' : 'vote.voted_at',
700             'vote' : 'vote',
701             },
702         select_params=[user_id],
703         tables=['vote', 'question', 'auth_user'],
704         where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = question.id '+
705             'AND vote.user_id=auth_user.id'],
706         params=[question_type_id, user_id],
707         order_by=['-vote.id']
708     ).values(
709             'title',
710             'question_id',
711             'answer_id',
712             'voted_at',
713             'vote',
714             )
715     if(len(question_votes) > 0):
716         votes.extend(question_votes)
717
718     answer_votes = Vote.objects.extra(
719         select={
720             'title' : 'question.title',
721             'question_id' : 'question.id',
722             'answer_id' : 'answer.id',
723             'voted_at' : 'vote.voted_at',
724             'vote' : 'vote',
725             },
726         select_params=[user_id],
727         tables=['vote', 'answer', 'question', 'auth_user'],
728         where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = answer.id '+
729             'AND answer.question_id = question.id AND vote.user_id=auth_user.id'],
730         params=[answer_type_id, user_id],
731         order_by=['-vote.id']
732     ).values(
733             'title',
734             'question_id',
735             'answer_id',
736             'voted_at',
737             'vote',
738             )
739     if(len(answer_votes) > 0):
740         votes.extend(answer_votes)
741     votes.sort(lambda x,y: cmp(y['voted_at'], x['voted_at']))
742     return render_to_response(user_view.template_file,{
743         "tab_name" : user_view.id,
744         "tab_description" : user_view.tab_description,
745         "page_title" : user_view.page_title,
746         "view_user" : user,
747         "votes" : votes[:user_view.data_size]
748
749     }, context_instance=RequestContext(request))
750
751 def user_reputation(request, user_id, user_view):
752     user = get_object_or_404(User, id=user_id)
753     try:
754         from django.db.models import Sum
755         reputation = Repute.objects.extra(
756                                           select={'question_id':'question_id',
757                                           'title': 'question.title'},
758                                           tables=['repute', 'question'],
759                                           order_by=['-reputed_at'],
760                                           where=['user_id=%s AND question_id=question.id'],
761                                           params=[user.id]
762                                           ).values('question_id', 'title', 'reputed_at', 'reputation')
763         reputation = reputation.annotate(positive=Sum("positive"), negative=Sum("negative"))
764     except ImportError:
765         reputation = Repute.objects.extra(
766                                           select={'positive':'sum(positive)', 'negative':'sum(negative)', 'question_id':'question_id',
767                                           'title': 'question.title'},
768                                           tables=['repute', 'question'],
769                                           order_by=['-reputed_at'],
770                                           where=['user_id=%s AND question_id=question.id'],
771                                           params=[user.id]
772                                           ).values('positive', 'negative', 'question_id', 'title', 'reputed_at', 'reputation')
773         reputation.query.group_by = ['question_id']
774
775     rep_list = []
776     for rep in Repute.objects.filter(user=user).order_by('reputed_at'):
777         dic = '[%s,%s]' % (calendar.timegm(rep.reputed_at.timetuple()) * 1000, rep.reputation)
778         rep_list.append(dic)
779     reps = ','.join(rep_list)
780     reps = '[%s]' % reps
781
782     return render_to_response(user_view.template_file, {
783                               "tab_name": user_view.id,
784                               "tab_description": user_view.tab_description,
785                               "page_title": user_view.page_title,
786                               "view_user": user,
787                               "reputation": reputation,
788                               "reps": reps
789                               }, context_instance=RequestContext(request))
790
791 def user_favorites(request, user_id, user_view):
792     user = get_object_or_404(User, id=user_id)
793     questions = Question.objects.extra(
794         select={
795             'vote_count' : 'question.vote_up_count + question.vote_down_count',
796             'favorited_myself' : 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s '+
797                 'AND f.question_id = question.id',
798             'la_user_id' : 'auth_user.id',
799             'la_username' : 'auth_user.username',
800             'la_user_gold' : 'auth_user.gold',
801             'la_user_silver' : 'auth_user.silver',
802             'la_user_bronze' : 'auth_user.bronze',
803             'la_user_reputation' : 'auth_user.reputation'
804             },
805         select_params=[user_id],
806         tables=['question', 'auth_user', 'favorite_question'],
807         where=['question.deleted=True AND question.last_activity_by_id = auth_user.id '+
808             'AND favorite_question.question_id = question.id AND favorite_question.user_id = %s'],
809         params=[user_id],
810         order_by=['-vote_count', '-question.id']
811     ).values('vote_count',
812              'favorited_myself',
813              'id',
814              'title',
815              'author_id',
816              'added_at',
817              'answer_accepted',
818              'answer_count',
819              'comment_count',
820              'view_count',
821              'favourite_count',
822              'summary',
823              'tagnames',
824              'vote_up_count',
825              'vote_down_count',
826              'last_activity_at',
827              'la_user_id',
828              'la_username',
829              'la_user_gold',
830              'la_user_silver',
831              'la_user_bronze',
832              'la_user_reputation')
833     return render_to_response(user_view.template_file,{
834         "tab_name" : user_view.id,
835         "tab_description" : user_view.tab_description,
836         "page_title" : user_view.page_title,
837         "questions" : questions[:user_view.data_size],
838         "view_user" : user
839     }, context_instance=RequestContext(request))
840
841 def user_email_subscriptions(request, user_id, user_view):
842     user = get_object_or_404(User, id=user_id)
843     if request.method == 'POST':
844         email_feeds_form = EditUserEmailFeedsForm(request.POST)
845         tag_filter_form = TagFilterSelectionForm(request.POST, instance=user)
846         if email_feeds_form.is_valid() and tag_filter_form.is_valid():
847
848             action_status = None
849             tag_filter_saved = tag_filter_form.save()
850             if tag_filter_saved:
851                 action_status = _('changes saved')
852             if 'save' in request.POST:
853                 feeds_saved = email_feeds_form.save(user)
854                 if feeds_saved:
855                     action_status = _('changes saved')
856             elif 'stop_email' in request.POST:
857                 email_stopped = email_feeds_form.reset().save(user)
858                 initial_values = EditUserEmailFeedsForm.NO_EMAIL_INITIAL
859                 email_feeds_form = EditUserEmailFeedsForm(initial=initial_values)
860                 if email_stopped:
861                     action_status = _('email updates canceled')
862     else:
863         email_feeds_form = EditUserEmailFeedsForm()
864         email_feeds_form.set_initial_values(user)
865         tag_filter_form = TagFilterSelectionForm(instance=user)
866         action_status = None
867     return render_to_response(user_view.template_file,{
868         'tab_name':user_view.id,
869         'tab_description':user_view.tab_description,
870         'page_title':user_view.page_title,
871         'view_user':user,
872         'email_feeds_form':email_feeds_form,
873         'tag_filter_selection_form':tag_filter_form,
874         'action_status':action_status,
875     }, context_instance=RequestContext(request))
876
877 class UserView:
878     def __init__(self, id, tab_title, tab_description, page_title, view_func, template_file, data_size=0):
879         self.id = id
880         self.tab_title = tab_title
881         self.tab_description = tab_description
882         self.page_title = page_title
883         self.view_func = view_func 
884         self.template_file = template_file
885         self.data_size = data_size
886         
887 USER_TEMPLATE_VIEWS = (
888     UserView(
889         id = 'stats',
890         tab_title = _('overview'),
891         tab_description = _('user profile'),
892         page_title = _('user profile overview'),
893         view_func = user_stats,
894         template_file = 'user_stats.html'
895     ),
896     UserView(
897         id = 'recent',
898         tab_title = _('recent activity'),
899         tab_description = _('recent user activity'),
900         page_title = _('profile - recent activity'),
901         view_func = user_recent,
902         template_file = 'user_recent.html',
903         data_size = 50
904     ),
905     UserView(
906         id = 'responses',
907         tab_title = _('responses'),
908         tab_description = _('comments and answers to others questions'),
909         page_title = _('profile - responses'),
910         view_func = user_responses,
911         template_file = 'user_responses.html',
912         data_size = 50
913     ),
914     UserView(
915         id = 'reputation',
916         tab_title = _('reputation'),
917         tab_description = _('user reputation in the community'),
918         page_title = _('profile - user reputation'),
919         view_func = user_reputation,
920         template_file = 'user_reputation.html'
921     ),
922     UserView(
923         id = 'favorites',
924         tab_title = _('favorite questions'),
925         tab_description = _('users favorite questions'),
926         page_title = _('profile - favorite questions'),
927         view_func = user_favorites,
928         template_file = 'user_favorites.html',
929         data_size = 50
930     ),
931     UserView(
932         id = 'votes',
933         tab_title = _('casted votes'),
934         tab_description = _('user vote record'),
935         page_title = _('profile - votes'),
936         view_func = user_votes,
937         template_file = 'user_votes.html',
938         data_size = 50
939     ),
940     UserView(
941         id = 'email_subscriptions',
942         tab_title = _('email subscriptions'),
943         tab_description = _('email subscription settings'),
944         page_title = _('profile - email subscriptions'),
945         view_func = user_email_subscriptions,
946         template_file = 'user_email_subscriptions.html'
947     )
948 )
949
950 def user(request, id):
951     sort = request.GET.get('sort', 'stats')
952     user_view = dict((v.id, v) for v in USER_TEMPLATE_VIEWS).get(sort, USER_TEMPLATE_VIEWS[0])
953     from forum.views import users
954     func = user_view.view_func
955     return func(request, id, user_view)
956
957
958 @login_required
959 def changepw(request):
960     """
961     change password view.
962
963     url : /changepw/
964     template: authopenid/changepw.html
965     """
966     logging.debug('')
967     user_ = request.user
968
969     if not user_.has_usable_password():
970         raise Http404
971
972     if request.POST:
973         form = ChangePasswordForm(request.POST, user=user_)
974         if form.is_valid():
975             user_.set_password(form.cleaned_data['password1'])
976             user_.save()
977             msg = _("Password changed.")
978             redirect = "%s?msg=%s" % (
979                     reverse('user_account_settings'),
980                     urlquote_plus(msg))
981             return HttpResponseRedirect(redirect)
982     else:
983         form = ChangePasswordForm(user=user_)
984
985     return render_to_response('changepw.html', {'form': form },
986                                 context_instance=RequestContext(request))
987
988 @login_required
989 def account_settings(request):
990     """
991     index pages to changes some basic account settings :
992      - change password
993      - change email
994      - associate a new openid
995      - delete account
996
997     url : /
998
999     template : authopenid/settings.html
1000     """
1001     logging.debug('')
1002     msg = request.GET.get('msg', '')
1003     is_openid = False
1004
1005     return render_to_response('account_settings.html', {
1006         'msg': msg,
1007         'is_openid': is_openid
1008         }, context_instance=RequestContext(request))
1009