summaryrefslogtreecommitdiffstats
path: root/forum
diff options
context:
space:
mode:
authorAdolfo Fitoria <fitoria@fitoria-laptop.(none)>2009-12-15 16:57:37 -0600
committerAdolfo Fitoria <fitoria@fitoria-laptop.(none)>2009-12-15 16:57:37 -0600
commit9d1fb9890b97beb55461ca34f9757bc685461130 (patch)
tree1f8f0552ba6f4ca092aaa5a5347f0ad07433f1de /forum
parentebb0f636ae8f7db4e7a2e7470e449af3d96b15c0 (diff)
parent82d35490db90878f013523c4d1a5ec3af2df8b23 (diff)
downloadaskbot-9d1fb9890b97beb55461ca34f9757bc685461130.tar.gz
askbot-9d1fb9890b97beb55461ca34f9757bc685461130.tar.bz2
askbot-9d1fb9890b97beb55461ca34f9757bc685461130.zip
Merge branch 'master' of git://github.com/evgenyfadeev/CNPROG into evgenyfadeev/master
Conflicts: INSTALL LICENSE TODO cnprog.wsgi context.py development.log forum/feed.py forum/forms.py forum/management/commands/send_email_alerts.py forum/managers.py forum/models.py forum/templatetags/extra_filters.py forum/templatetags/extra_tags.py forum/urls.py forum/views.py locale/en/LC_MESSAGES/django.mo locale/en/LC_MESSAGES/django.po middleware/__init__.py middleware/anon_user.py settings.py settings_local.py.dist templates/about.html templates/authopenid/complete.html templates/authopenid/external_legacy_login_info.html templates/base.html templates/base_content.html templates/content/js/com.cnprog.admin.js templates/content/js/com.cnprog.i18n.js templates/content/js/com.cnprog.post.js templates/content/js/com.cnprog.utils.js templates/content/js/wmd/wmd.js templates/content/style/style.css templates/question.html templates/questions.html templates/unanswered.html templates/user_email_subscriptions.html templates/user_reputation.html templates/user_stats.html templates/user_votes.html
Diffstat (limited to 'forum')
-rw-r--r--forum/const.py5
-rw-r--r--forum/feed.py4
-rw-r--r--forum/forms.py27
-rw-r--r--forum/management/commands/send_email_alerts.py150
-rw-r--r--forum/managers.py11
-rw-r--r--forum/models.py93
-rw-r--r--forum/sitemap.py11
-rw-r--r--forum/templatetags/extra_filters.py11
-rw-r--r--forum/templatetags/extra_tags.py9
-rw-r--r--forum/urls.py21
-rw-r--r--forum/views.py575
11 files changed, 698 insertions, 219 deletions
diff --git a/forum/const.py b/forum/const.py
index 9b9230c0..76fd4a24 100644
--- a/forum/const.py
+++ b/forum/const.py
@@ -49,6 +49,7 @@ TYPE_ACTIVITY_MARK_OFFENSIVE=14
TYPE_ACTIVITY_UPDATE_TAGS=15
TYPE_ACTIVITY_FAVORITE=16
TYPE_ACTIVITY_USER_FULL_UPDATED = 17
+TYPE_ACTIVITY_QUESTION_EMAIL_UPDATE_SENT = 18
#TYPE_ACTIVITY_EDIT_QUESTION=17
#TYPE_ACTIVITY_EDIT_ANSWER=18
@@ -70,6 +71,7 @@ TYPE_ACTIVITY = (
(TYPE_ACTIVITY_UPDATE_TAGS, _('updated tags')),
(TYPE_ACTIVITY_FAVORITE, _('selected favorite')),
(TYPE_ACTIVITY_USER_FULL_UPDATED, _('completed user profile')),
+ (TYPE_ACTIVITY_QUESTION_EMAIL_UPDATE_SENT, _('email update sent to user')),
)
TYPE_RESPONSE = {
@@ -85,3 +87,6 @@ CONST = {
'default_version' : _('initial version'),
'retagged' : _('retagged'),
}
+
+#how to filter questions by tags in email digests?
+TAG_EMAIL_FILTER_CHOICES = (('ignored', _('exclude ignored tags')),('interesting',_('allow only selected tags')))
diff --git a/forum/feed.py b/forum/feed.py
index 373f8a87..ad1d5cbd 100644
--- a/forum/feed.py
+++ b/forum/feed.py
@@ -16,7 +16,7 @@ from models import Question
import settings
class RssLastestQuestionsFeed(Feed):
title = settings.APP_TITLE + _(' - ')+ _('latest questions')
- link = settings.APP_URL + '/' + _('questions/')
+ link = settings.APP_URL + '/' + _('question/')
description = settings.APP_DESCRIPTION
#ttl = 10
copyright = settings.APP_COPYRIGHT
@@ -34,7 +34,7 @@ class RssLastestQuestionsFeed(Feed):
return item.added_at
def items(self, item):
- return Question.objects.filter(deleted=False).order_by('-added_at')[:30]
+ return Question.objects.filter(deleted=False).order_by('-last_activity_at')[:30]
def main():
pass
diff --git a/forum/forms.py b/forum/forms.py
index 5b181d48..ad2c5bac 100644
--- a/forum/forms.py
+++ b/forum/forms.py
@@ -4,7 +4,7 @@ from django import forms
from models import *
from const import *
from django.utils.translation import ugettext as _
-from django_authopenid.forms import NextUrlField
+from django_authopenid.forms import NextUrlField, UserNameField
import settings
class TitleField(forms.CharField):
@@ -195,6 +195,7 @@ class EditAnswerForm(forms.Form):
class EditUserForm(forms.Form):
email = forms.EmailField(label=u'Email', help_text=_('this email does not have to be linked to gravatar'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+ username = UserNameField(label=_('Screen name'))
realname = forms.CharField(label=_('Real name'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
website = forms.URLField(label=_('Website'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
city = forms.CharField(label=_('Location'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
@@ -203,6 +204,7 @@ class EditUserForm(forms.Form):
def __init__(self, user, *args, **kwargs):
super(EditUserForm, self).__init__(*args, **kwargs)
+ self.fields['username'].initial = user.username
self.fields['email'].initial = user.email
self.fields['realname'].initial = user.real_name
self.fields['website'].initial = user.website
@@ -230,6 +232,23 @@ class EditUserForm(forms.Form):
raise forms.ValidationError(_('this email has already been registered, please use another one'))
return self.cleaned_data['email']
+class TagFilterSelectionForm(forms.ModelForm):
+ tag_filter_setting = forms.ChoiceField(choices=TAG_EMAIL_FILTER_CHOICES, #imported from forum/const.py
+ initial='ignored',
+ label=_('Choose email tag filter'),
+ widget=forms.RadioSelect)
+ class Meta:
+ model = User
+ fields = ('tag_filter_setting',)
+
+ def save(self):
+ before = self.instance.tag_filter_setting
+ super(TagFilterSelectionForm, self).save()
+ after = self.instance.tag_filter_setting #User.objects.get(pk=self.instance.id).tag_filter_setting
+ if before != after:
+ return True
+ return False
+
class EditUserEmailFeedsForm(forms.Form):
WN = (('w',_('weekly')),('n',_('no email')))
DWN = (('d',_('daily')),('w',_('weekly')),('n',_('no email')))
@@ -245,9 +264,6 @@ class EditUserEmailFeedsForm(forms.Form):
'answered_by_me':'n',
'individually_selected':'n',
}
- all_questions = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Entire forum'),)
asked_by_me = forms.ChoiceField(choices=DWN,initial='w',
widget=forms.RadioSelect,
label=_('Asked by me'))
@@ -257,6 +273,9 @@ class EditUserEmailFeedsForm(forms.Form):
individually_selected = forms.ChoiceField(choices=DWN,initial='w',
widget=forms.RadioSelect,
label=_('Individually selected'))
+ all_questions = forms.ChoiceField(choices=DWN,initial='w',
+ widget=forms.RadioSelect,
+ label=_('Entire forum (tag filtered)'),)
def set_initial_values(self,user=None):
KEY_MAP = dict([(v,k) for k,v in self.FORM_TO_MODEL_MAP.iteritems()])
diff --git a/forum/management/commands/send_email_alerts.py b/forum/management/commands/send_email_alerts.py
index a25e4343..283d5683 100644
--- a/forum/management/commands/send_email_alerts.py
+++ b/forum/management/commands/send_email_alerts.py
@@ -2,11 +2,20 @@ from django.core.management.base import NoArgsCommand
from django.db import connection
from django.db.models import Q, F
from forum.models import *
+<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
+=======
+from forum import const
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
from django.core.mail import EmailMessage
from django.utils.translation import ugettext as _
from django.utils.translation import ungettext
import datetime
import settings
+<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
+=======
+import logging
+from utils.odict import OrderedDict
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
class Command(NoArgsCommand):
def handle_noargs(self,**options):
@@ -18,10 +27,17 @@ class Command(NoArgsCommand):
connection.close()
def get_updated_questions_for_user(self,user):
+<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
q_sel = []
q_ask = []
q_ans = []
q_all = []
+=======
+ q_sel = None
+ q_ask = None
+ q_ans = None
+ q_all = None
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
now = datetime.datetime.now()
Q_set1 = Question.objects.exclude(
last_activity_by=user,
@@ -35,16 +51,28 @@ class Command(NoArgsCommand):
).exclude(
closed=True
)
+<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
+=======
+
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
user_feeds = EmailFeedSetting.objects.filter(subscriber=user).exclude(frequency='n')
for feed in user_feeds:
cutoff_time = now - EmailFeedSetting.DELTA_TABLE[feed.frequency]
if feed.reported_at == None or feed.reported_at <= cutoff_time:
+<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
Q_set = Q_set1.exclude(last_activity_at__gt=cutoff_time)
+=======
+ Q_set = Q_set1.exclude(last_activity_at__gt=cutoff_time)#report these excluded later
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
feed.reported_at = now
feed.save()#may not actually report anything, depending on filters below
if feed.feed_type == 'q_sel':
q_sel = Q_set.filter(followed_by=user)
+<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
q_sel.cutoff_time = cutoff_time
+=======
+ q_sel.cutoff_time = cutoff_time #store cutoff time per query set
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
elif feed.feed_type == 'q_ask':
q_ask = Q_set.filter(author=user)
q_ask.cutoff_time = cutoff_time
@@ -52,6 +80,7 @@ class Command(NoArgsCommand):
q_ans = Q_set.filter(answers__author=user)
q_ans.cutoff_time = cutoff_time
elif feed.feed_type == 'q_all':
+<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
q_all = Q_set
q_all.cutoff_time = cutoff_time
#build list in this order
@@ -97,14 +126,108 @@ class Command(NoArgsCommand):
return out
def __act_count(self,string,number,output):
+=======
+ if user.tag_filter_setting == 'ignored':
+ ignored_tags = Tag.objects.filter(user_selections___reason='bad',user_selections__user=user)
+ q_all = Q_set.exclude( tags__in=ignored_tags )
+ else:
+ selected_tags = Tag.objects.filter(user_selections___reason='good',user_selections__user=user)
+ q_all = Q_set.filter( tags__in=selected_tags )
+ q_all.cutoff_time = cutoff_time
+ #build list in this order
+ q_list = OrderedDict()
+ def extend_question_list(src, dst):
+ """src is a query set with questions
+ or an empty list
+ dst - is an ordered dictionary
+ """
+ if src is None:
+ return #will not do anything if subscription of this type is not used
+ cutoff_time = src.cutoff_time
+ for q in src:
+ if q in dst:
+ if cutoff_time < dst[q]['cutoff_time']:
+ dst[q]['cutoff_time'] = cutoff_time
+ else:
+ #initialise a questions metadata dictionary to use for email reporting
+ dst[q] = {'cutoff_time':cutoff_time}
+
+ extend_question_list(q_sel, q_list)
+ extend_question_list(q_ask, q_list)
+ extend_question_list(q_ans, q_list)
+ extend_question_list(q_all, q_list)
+
+ ctype = ContentType.objects.get_for_model(Question)
+ EMAIL_UPDATE_ACTIVITY = const.TYPE_ACTIVITY_QUESTION_EMAIL_UPDATE_SENT
+ for q, meta_data in q_list.items():
+ #todo use Activity, but first start keeping more Activity records
+ #act = Activity.objects.filter(content_type=ctype, object_id=q.id)
+ #because currently activity is not fully recorded to through
+ #revision records to see what kind modifications were done on
+ #the questions and answers
+ try:
+ update_info = Activity.objects.get(content_type=ctype,
+ object_id=q.id,
+ activity_type=EMAIL_UPDATE_ACTIVITY)
+ emailed_at = update_info.active_at
+ except Activity.DoesNotExist:
+ update_info = Activity(user=user, content_object=q, activity_type=EMAIL_UPDATE_ACTIVITY)
+ emailed_at = datetime.datetime(1970,1,1)#long time ago
+ except Activity.MultipleObjectsReturned:
+ raise Exception('server error - multiple question email activities found per user-question pair')
+
+ q_rev = QuestionRevision.objects.filter(question=q,\
+ revised_at__lt=cutoff_time,\
+ revised_at__gt=emailed_at)
+ q_rev = q_rev.exclude(author=user)
+ meta_data['q_rev'] = len(q_rev)
+ if len(q_rev) > 0 and q.added_at == q_rev[0].revised_at:
+ meta_data['q_rev'] = 0
+ meta_data['new_q'] = True
+ else:
+ meta_data['new_q'] = False
+
+ new_ans = Answer.objects.filter(question=q,\
+ added_at__lt=cutoff_time,\
+ added_at__gt=emailed_at)
+ new_ans = new_ans.exclude(author=user)
+ meta_data['new_ans'] = len(new_ans)
+ ans_rev = AnswerRevision.objects.filter(answer__question=q,\
+ revised_at__lt=cutoff_time,\
+ revised_at__gt=emailed_at)
+ ans_rev = ans_rev.exclude(author=user)
+ meta_data['ans_rev'] = len(ans_rev)
+ if len(q_rev) == 0 and len(new_ans) == 0 and len(ans_rev) == 0:
+ meta_data['nothing_new'] = True
+ else:
+ meta_data['nothing_new'] = False
+ update_info.active_at = now
+ update_info.save() #save question email update activity
+ return q_list
+
+ def __action_count(self,string,number,output):
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
if number > 0:
output.append(_(string) % {'num':number})
def send_email_alerts(self):
+<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
for user in User.objects.all():
q_list = self.get_updated_questions_for_user(user)
num_q = len(q_list)
+=======
+ #todo: move this to template
+ for user in User.objects.all():
+ q_list = self.get_updated_questions_for_user(user)
+ num_q = 0
+ num_moot = 0
+ for meta_data in q_list.values():
+ if meta_data['nothing_new'] == False:
+ num_q += 1
+ else:
+ num_moot += 1
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
if num_q > 0:
url_prefix = settings.APP_URL
subject = _('email update message subject')
@@ -113,6 +236,7 @@ class Command(NoArgsCommand):
% {'num':num_q, 'name':user.username}
text += '<ul>'
+<<<<<<< HEAD:forum/management/commands/send_email_alerts.py
for q, act in q_list.items():
act_list = []
if act['new_q']:
@@ -124,6 +248,32 @@ class Command(NoArgsCommand):
text += '<li><a href="%s?sort=latest">%s</a> <font color="#777777">(%s)</font></li>' \
% (url_prefix + q.get_absolute_url(), q.title, act_token)
text += '</ul>'
+=======
+ for q, meta_data in q_list.items():
+ act_list = []
+ if meta_data['nothing_new']:
+ continue
+ else:
+ if meta_data['new_q']:
+ act_list.append(_('new question'))
+ self.__action_count('%(num)d rev', meta_data['q_rev'],act_list)
+ self.__action_count('%(num)d ans', meta_data['new_ans'],act_list)
+ self.__action_count('%(num)d ans rev',meta_data['ans_rev'],act_list)
+ act_token = ', '.join(act_list)
+ text += '<li><a href="%s?sort=latest">%s</a> <font color="#777777">(%s)</font></li>' \
+ % (url_prefix + q.get_absolute_url(), q.title, act_token)
+ text += '</ul>'
+ if num_moot > 0:
+ text += '<p></p>'
+ text += ungettext('There is also one question which was recently '\
+ +'updated but you might not have seen its latest version.',
+ 'There are also %(num)d more questions which were recently updated '\
+ +'but you might not have seen their latest version.',num_moot) \
+ % {'num':num_moot,}
+ text += _('Perhaps you could look up previously sent forum reminders in your mailbox.')
+ text += '</p>'
+
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/management/commands/send_email_alerts.py
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]}
diff --git a/forum/managers.py b/forum/managers.py
index 795d382e..06fae761 100644
--- a/forum/managers.py
+++ b/forum/managers.py
@@ -7,6 +7,7 @@ from forum.models import *
from urllib import quote, unquote
class QuestionManager(models.Manager):
+<<<<<<< HEAD:forum/managers.py
def get_translation_questions(self, orderby, page_size):
questions = self.filter(deleted=False, author__id__in=[28,29]).order_by(orderby)[:page_size]
return questions
@@ -26,6 +27,8 @@ class QuestionManager(models.Manager):
def get_questions(self, orderby):
questions = self.filter(deleted=False).order_by(orderby)
return questions
+=======
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/managers.py
def update_tags(self, question, tagnames, user):
"""
@@ -92,12 +95,20 @@ class QuestionManager(models.Manager):
Questions with the individual tags will be added to list if above questions are not full.
"""
#print datetime.datetime.now()
+<<<<<<< HEAD:forum/managers.py
from forum.models import Question
questions = list(Question.objects.filter(tagnames = question.tagnames, deleted=False).all())
tags_list = question.tags.all()
for tag in tags_list:
extend_questions = Question.objects.filter(tags__id = tag.id, deleted=False)[:50]
+=======
+ questions = list(self.filter(tagnames = question.tagnames, deleted=False).all())
+
+ tags_list = question.tags.all()
+ for tag in tags_list:
+ extend_questions = self.filter(tags__id = tag.id, deleted=False)[:50]
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/managers.py
for item in extend_questions:
if item not in questions and len(questions) < 10:
questions.append(item)
diff --git a/forum/models.py b/forum/models.py
index 39058bea..f1876588 100644
--- a/forum/models.py
+++ b/forum/models.py
@@ -3,6 +3,10 @@ import datetime
import hashlib
from urllib import quote_plus, urlencode
from django.db import models, IntegrityError
+<<<<<<< HEAD:forum/models.py
+=======
+from django.utils.http import urlquote as django_urlquote
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/models.py
from django.utils.html import strip_tags
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
@@ -12,8 +16,18 @@ from django.template.defaultfilters import slugify
from django.db.models.signals import post_delete, post_save, pre_save
from django.utils.translation import ugettext as _
from django.utils.safestring import mark_safe
+<<<<<<< HEAD:forum/models.py
import django.dispatch
import settings
+=======
+from django.contrib.sitemaps import ping_google
+import django.dispatch
+import settings
+import logging
+
+if settings.USE_SPHINX_SEARCH == True:
+ from djangosphinx.models import SphinxSearch
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/models.py
from forum.managers import *
from const import *
@@ -96,6 +110,17 @@ class Comment(models.Model):
class Meta:
ordering = ('-added_at',)
db_table = u'comment'
+<<<<<<< HEAD:forum/models.py
+=======
+
+ def save(self,**kwargs):
+ super(Comment,self).save(**kwargs)
+ try:
+ ping_google()
+ except Exception:
+ logging.debug('problem pinging google did you register you sitemap with google?')
+
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/models.py
def __unicode__(self):
return self.comment
@@ -184,8 +209,27 @@ class Question(models.Model):
votes = generic.GenericRelation(Vote)
flagged_items = generic.GenericRelation(FlaggedItem)
+<<<<<<< HEAD:forum/models.py
objects = QuestionManager()
+=======
+ if settings.USE_SPHINX_SEARCH == True:
+ search = SphinxSearch(
+ index=' '.join(settings.SPHINX_SEARCH_INDICES),
+ mode='SPH_MATCH_ALL',
+ )
+ logging.debug('have sphinx search')
+
+ objects = QuestionManager()
+
+ def delete(self):
+ super(Question, self).delete()
+ try:
+ ping_google()
+ except Exception:
+ logging.debug('problem pinging google did you register you sitemap with google?')
+
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/models.py
def save(self, **kwargs):
"""
Overridden to manually manage addition of tags when the object
@@ -196,6 +240,13 @@ class Question(models.Model):
"""
initial_addition = (self.id is None)
super(Question, self).save(**kwargs)
+<<<<<<< HEAD:forum/models.py
+=======
+ try:
+ ping_google()
+ except Exception:
+ logging.debug('problem pinging google did you register you sitemap with google?')
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/models.py
if initial_addition:
tags = Tag.objects.get_or_create_multiple(self.tagname_list(),
self.author)
@@ -210,7 +261,11 @@ class Question(models.Model):
return u','.join([unicode(tag) for tag in self.tagname_list()])
def get_absolute_url(self):
+<<<<<<< HEAD:forum/models.py
return '%s%s' % (reverse('question', args=[self.id]), slugify(self.title))
+=======
+ return '%s%s' % (reverse('question', args=[self.id]), django_urlquote(slugify(self.title)))
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/models.py
def has_favorite_by_user(self, user):
if not user.is_authenticated():
@@ -332,6 +387,15 @@ class FavoriteQuestion(models.Model):
def __unicode__(self):
return '[%s] favorited at %s' %(self.user, self.added_at)
+<<<<<<< HEAD:forum/models.py
+=======
+class MarkedTag(models.Model):
+ TAG_MARK_REASONS = (('good',_('interesting')),('bad',_('ignored')))
+ tag = models.ForeignKey(Tag, related_name='user_selections')
+ user = models.ForeignKey(User, related_name='tag_selections')
+ reason = models.CharField(max_length=16, choices=TAG_MARK_REASONS)
+
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/models.py
class QuestionRevision(models.Model):
"""A revision of a Question."""
question = models.ForeignKey(Question, related_name='revisions')
@@ -435,6 +499,16 @@ class Answer(models.Model):
get_comments = get_object_comments
get_last_update_info = post_get_last_update_info
+<<<<<<< HEAD:forum/models.py
+=======
+ def save(self,**kwargs):
+ super(Answer,self).save(**kwargs)
+ try:
+ ping_google()
+ except Exception:
+ logging.debug('problem pinging google did you register you sitemap with google?')
+
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/models.py
def get_user_vote(self, user):
votes = self.votes.filter(user=user)
if votes.count() > 0:
@@ -449,7 +523,11 @@ class Answer(models.Model):
return self.question.title
def get_absolute_url(self):
+<<<<<<< HEAD:forum/models.py
return '%s%s#%s' % (reverse('question', args=[self.question.id]), slugify(self.question.title), self.id)
+=======
+ return '%s%s#%s' % (reverse('question', args=[self.question.id]), django_urlquote(slugify(self.question.title)), self.id)
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/models.py
class Meta:
db_table = u'answer'
@@ -590,7 +668,11 @@ class Book(models.Model):
questions = models.ManyToManyField(Question, related_name='book', db_table='book_question')
def get_absolute_url(self):
+<<<<<<< HEAD:forum/models.py
return reverse('book', args=[self.short_name])
+=======
+ return reverse('book', args=[django_urlquote(slugify(self.short_name))])
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/models.py
def __unicode__(self):
return self.title
@@ -680,6 +762,17 @@ User.add_to_class('date_of_birth', models.DateField(null=True, blank=True))
User.add_to_class('about', models.TextField(blank=True))
User.add_to_class('is_username_taken',classmethod(user_is_username_taken))
User.add_to_class('get_q_sel_email_feed_frequency',user_get_q_sel_email_feed_frequency)
+<<<<<<< HEAD:forum/models.py
+=======
+User.add_to_class('hide_ignored_questions', models.BooleanField(default=False))
+User.add_to_class('tag_filter_setting',
+ models.CharField(
+ max_length=16,
+ choices=TAG_EMAIL_FILTER_CHOICES,
+ default='ignored'
+ )
+ )
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/models.py
# custom signal
tags_updated = django.dispatch.Signal(providing_args=["question"])
diff --git a/forum/sitemap.py b/forum/sitemap.py
new file mode 100644
index 00000000..dc97a009
--- /dev/null
+++ b/forum/sitemap.py
@@ -0,0 +1,11 @@
+from django.contrib.sitemaps import Sitemap
+from forum.models import Question
+
+class QuestionsSitemap(Sitemap):
+ changefreq = 'daily'
+ priority = 0.5
+ def items(self):
+ return Question.objects.exclude(deleted=True)
+
+ def lastmod(self, obj):
+ return obj.last_activity_at
diff --git a/forum/templatetags/extra_filters.py b/forum/templatetags/extra_filters.py
index 3644fdc3..865cd33d 100644
--- a/forum/templatetags/extra_filters.py
+++ b/forum/templatetags/extra_filters.py
@@ -1,4 +1,8 @@
from django import template
+<<<<<<< HEAD:forum/templatetags/extra_filters.py
+=======
+from django.core import serializers
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/templatetags/extra_filters.py
from forum import auth
import logging
@@ -91,3 +95,10 @@ def cnprog_intword(number):
return number
except:
return number
+<<<<<<< HEAD:forum/templatetags/extra_filters.py
+=======
+
+@register.filter
+def json_serialize(object):
+ return serializers.serialize('json',object)
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/templatetags/extra_filters.py
diff --git a/forum/templatetags/extra_tags.py b/forum/templatetags/extra_tags.py
index 8bd0e128..8ed79d3c 100644
--- a/forum/templatetags/extra_tags.py
+++ b/forum/templatetags/extra_tags.py
@@ -251,7 +251,11 @@ def diff_date(date, limen=2):
return _('2 days ago')
elif days == 1:
return _('yesterday')
+<<<<<<< HEAD:forum/templatetags/extra_tags.py
elif minutes > 60:
+=======
+ elif minutes >= 60:
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/templatetags/extra_tags.py
return ungettext('%(hr)d hour ago','%(hr)d hours ago',hours) % {'hr':hours}
else:
return ungettext('%(min)d min ago','%(min)d mins ago',minutes) % {'min':minutes}
@@ -332,8 +336,13 @@ class BlockResourceNode(template.Node):
for item in self.items:
bit = item.render(context)
out += bit
+<<<<<<< HEAD:forum/templatetags/extra_tags.py
out = out.replace(' ','')
return os.path.normpath(out) + '?v=%d' % settings.RESOURCE_REVISION
+=======
+ out = os.path.normpath(out) + '?v=%d' % settings.RESOURCE_REVISION
+ return out.replace(' ','')
+>>>>>>> 82d35490db90878f013523c4d1a5ec3af2df8b23:forum/templatetags/extra_tags.py
@register.tag(name='blockresource')
def blockresource(parser,token):
diff --git a/forum/urls.py b/forum/urls.py
index a08fe716..62e70161 100644
--- a/forum/urls.py
+++ b/forum/urls.py
@@ -3,16 +3,21 @@ from django.conf.urls.defaults import *
from django.contrib import admin
from forum import views as app
from forum.feed import RssLastestQuestionsFeed
+from forum.sitemap import QuestionsSitemap
from django.utils.translation import ugettext as _
admin.autodiscover()
feeds = {
'rss': RssLastestQuestionsFeed
}
+sitemaps = {
+ 'questions': QuestionsSitemap
+}
APP_PATH = os.path.dirname(os.path.dirname(__file__))
urlpatterns = patterns('',
url(r'^$', app.index, name='index'),
+ url(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}),
(r'^favicon\.ico$', 'django.views.generic.simple.redirect_to', {'url': '/content/images/favicon.ico'}),
(r'^favicon\.gif$', 'django.views.generic.simple.redirect_to', {'url': '/content/images/favicon.gif'}),
(r'^content/(?P<path>.*)$', 'django.views.static.serve',
@@ -39,6 +44,7 @@ urlpatterns = patterns('',
url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('vote/')), app.vote, name='vote'),
url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('revisions/')), app.question_revisions, name='question_revisions'),
url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('comments/')), app.question_comments, name='question_comments'),
+ url(r'^%s$' % _('command/'), app.ajax_command, name='call_ajax'),
url(r'^%s(?P<object_id>\d+)/%s(?P<comment_id>\d+)/%s$' % (_('questions/'), _('comments/'),_('delete/')), \
app.delete_comment, kwargs={'commented_object_type':'question'},\
@@ -50,7 +56,20 @@ urlpatterns = patterns('',
#place general question item in the end of other operations
url(r'^%s(?P<id>\d+)//*' % _('question/'), app.question, name='question'),
url(r'^%s$' % _('tags/'), app.tags, name='tags'),
- url(r'^%s(?P<tag>[^/]+)/$' % _('tags/'), app.tag),
+ url(r'^%s(?P<tag>[^/]+)/$' % _('tags/'), app.tag, name='tag_questions'),
+
+ url(r'^%s%s(?P<tag>[^/]+)/$' % (_('mark-tag/'),_('interesting/')), app.mark_tag, \
+ kwargs={'reason':'good','action':'add'}, \
+ name='mark_interesting_tag'),
+
+ url(r'^%s%s(?P<tag>[^/]+)/$' % (_('mark-tag/'),_('ignored/')), app.mark_tag, \
+ kwargs={'reason':'bad','action':'add'}, \
+ name='mark_ignored_tag'),
+
+ url(r'^%s(?P<tag>[^/]+)/$' % _('unmark-tag/'), app.mark_tag, \
+ kwargs={'action':'remove'}, \
+ name='mark_ignored_tag'),
+
url(r'^%s$' % _('users/'),app.users, name='users'),
url(r'^%s(?P<id>\d+)/$' % _('moderate-user/'), app.moderate_user, name='moderate_user'),
url(r'^%s(?P<id>\d+)/%s$' % (_('users/'), _('edit/')), app.edit_user, name='edit_user'),
diff --git a/forum/views.py b/forum/views.py
index e4ccaa16..296745f9 100644
--- a/forum/views.py
+++ b/forum/views.py
@@ -15,12 +15,15 @@ from django.utils import simplejson
from django.core import serializers
from django.core.mail import mail_admins
from django.db import transaction
+from django.db.models import Count, Q
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext as _
+from django.utils.datastructures import SortedDict
from django.template.defaultfilters import slugify
from django.core.exceptions import PermissionDenied
from utils.html import sanitize_html
+from utils.decorators import ajax_method, ajax_login_required
from markdown2 import Markdown
#from lxml.html.diff import htmldiff
from forum.diff import textDiff as htmldiff
@@ -50,7 +53,7 @@ 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)
+repute_type = ContentType.objects.get_for_model(Repute)
question_type_id = question_type.id
answer_type_id = answer_type.id
comment_type_id = comment_type.id
@@ -61,7 +64,7 @@ def _get_tags_cache_json():
tags = Tag.objects.filter(deleted=False).all()
tags_list = []
for tag in tags:
- dic = {'n': tag.name, 'c': tag.used_count }
+ dic = {'n': tag.name, 'c': tag.used_count}
tags_list.append(dic)
tags = simplejson.dumps(tags_list)
return tags
@@ -87,14 +90,26 @@ def index(request):
}
view_id, orderby = _get_and_remember_questions_sort_method(request, view_dic, 'latest')
- questions = Question.objects.get_questions_by_pagesize(orderby, INDEX_PAGE_SIZE)
+ page_size = request.session.get('pagesize', QUESTIONS_PAGE_SIZE)
+ questions = Question.objects.exclude(deleted=True).order_by(orderby)[:page_size]
# RISK - inner join queries
questions = questions.select_related()
tags = Tag.objects.get_valid_tags(INDEX_TAGS_SIZE)
awards = Award.objects.get_recent_awards()
+ (interesting_tag_names, ignored_tag_names) = (None, None)
+ if request.user.is_authenticated():
+ pt = MarkedTag.objects.filter(user=request.user)
+ interesting_tag_names = pt.filter(reason='good').values_list('tag__name', flat=True)
+ ignored_tag_names = pt.filter(reason='bad').values_list('tag__name', flat=True)
+
+ tags_autocomplete = _get_tags_cache_json()
+
return render_to_response('index.html', {
+ 'interesting_tag_names': interesting_tag_names,
+ 'tags_autocomplete': tags_autocomplete,
+ 'ignored_tag_names': ignored_tag_names,
"questions" : questions,
"tab_id" : view_id,
"tags" : tags,
@@ -145,7 +160,7 @@ def questions(request, tagname=None, unanswered=False):
List of Questions, Tagged questions, and Unanswered questions.
"""
# template file
- # "questions.html" or "unanswered.html"
+ # "questions.html" or maybe index.html in the future
template_file = "questions.html"
# Set flag to False by default. If it is equal to True, then need to be saved.
pagesize_changed = False
@@ -160,18 +175,61 @@ def questions(request, tagname=None, unanswered=False):
view_id, orderby = _get_and_remember_questions_sort_method(request,view_dic,'latest')
# check if request is from tagged questions
+ qs = Question.objects.exclude(deleted=True)
+
if tagname is not None:
- objects = Question.objects.get_questions_by_tag(tagname, orderby)
- elif unanswered:
- #check if request is from unanswered questions
- template_file = "unanswered.html"
- objects = Question.objects.get_unanswered_questions(orderby)
- else:
- objects = Question.objects.get_questions(orderby)
+ qs = qs.filter(tags__name = unquote(tagname))
- # RISK - inner join queries
- objects = objects.select_related(depth=1);
- objects_list = Paginator(objects, pagesize)
+ if unanswered:
+ qs = qs.exclude(answer_accepted=True)
+
+ author_name = None
+ #user contributed questions & answers
+ if 'user' in request.GET:
+ try:
+ author_name = request.GET['user']
+ u = User.objects.get(username=author_name)
+ qs = qs.filter(Q(author=u) | Q(answers__author=u))
+ except User.DoesNotExist:
+ author_name = None
+
+ if request.user.is_authenticated():
+ uid_str = str(request.user.id)
+ qs = qs.extra(
+ select = SortedDict([
+ (
+ 'interesting_score',
+ 'SELECT COUNT(1) FROM forum_markedtag, question_tags '
+ + 'WHERE forum_markedtag.user_id = %s '
+ + 'AND forum_markedtag.tag_id = question_tags.tag_id '
+ + 'AND forum_markedtag.reason = "good" '
+ + 'AND question_tags.question_id = question.id'
+ ),
+ ]),
+ select_params = (uid_str,),
+ )
+ if request.user.hide_ignored_questions:
+ ignored_tags = Tag.objects.filter(user_selections__reason='bad',
+ user_selections__user = request.user)
+ qs = qs.exclude(tags__in=ignored_tags)
+ else:
+ qs = qs.extra(
+ select = SortedDict([
+ (
+ 'ignored_score',
+ 'SELECT COUNT(1) FROM forum_markedtag, question_tags '
+ + 'WHERE forum_markedtag.user_id = %s '
+ + 'AND forum_markedtag.tag_id = question_tags.tag_id '
+ + 'AND forum_markedtag.reason = "bad" '
+ + 'AND question_tags.question_id = question.id'
+ )
+ ]),
+ select_params = (uid_str, )
+ )
+
+ qs = qs.select_related(depth=1).order_by(orderby)
+
+ objects_list = Paginator(qs, pagesize)
questions = objects_list.page(page)
# Get related tags from this page objects
@@ -179,13 +237,26 @@ def questions(request, tagname=None, unanswered=False):
related_tags = Tag.objects.get_tags_by_questions(questions.object_list)
else:
related_tags = None
+ tags_autocomplete = _get_tags_cache_json()
+
+ # get the list of interesting and ignored tags
+ (interesting_tag_names, ignored_tag_names) = (None, None)
+ if request.user.is_authenticated():
+ pt = MarkedTag.objects.filter(user=request.user)
+ interesting_tag_names = pt.filter(reason='good').values_list('tag__name', flat=True)
+ ignored_tag_names = pt.filter(reason='bad').values_list('tag__name', flat=True)
+
return render_to_response(template_file, {
"questions" : questions,
+ "author_name" : author_name,
"tab_id" : view_id,
"questions_count" : objects_list.count,
"tags" : related_tags,
+ "tags_autocomplete" : tags_autocomplete,
"searchtag" : tagname,
"is_unanswered" : unanswered,
+ "interesting_tag_names": interesting_tag_names,
+ 'ignored_tag_names': ignored_tag_names,
"context" : {
'is_paginated' : True,
'pages': objects_list.num_pages,
@@ -546,7 +617,6 @@ def _retag_question(request, question):
'tags' : _get_tags_cache_json(),
}, context_instance=RequestContext(request))
-
def _edit_question(request, question):
latest_revision = question.get_latest_revision()
revision_form = None
@@ -641,8 +711,8 @@ def edit_answer(request, id):
if revision_form.is_valid():
# Replace with those from the selected revision
form = EditAnswerForm(answer,
- AnswerRevision.objects.get(answer=answer,
- revision=revision_form.cleaned_data['revision']))
+ AnswerRevision.objects.get(answer=answer,
+ revision=revision_form.cleaned_data['revision']))
else:
form = EditAnswerForm(answer, latest_revision, request.POST)
else:
@@ -659,11 +729,11 @@ def edit_answer(request, id):
Answer.objects.filter(id=answer.id).update(**updated_fields)
revision = AnswerRevision(
- answer = answer,
- author = request.user,
- revised_at = edited_at,
- text = form.cleaned_data['text']
- )
+ answer=answer,
+ author=request.user,
+ revised_at=edited_at,
+ text=form.cleaned_data['text']
+ )
if form.cleaned_data['summary']:
revision.summary = form.cleaned_data['summary']
@@ -680,14 +750,15 @@ def edit_answer(request, id):
revision_form = RevisionForm(answer, latest_revision)
form = EditAnswerForm(answer, latest_revision)
return render_to_response('answer_edit.html', {
- 'answer': answer,
- 'revision_form': revision_form,
- 'form' : form,
- }, context_instance=RequestContext(request))
+ 'answer': answer,
+ 'revision_form': revision_form,
+ 'form': form,
+ }, context_instance=RequestContext(request))
QUESTION_REVISION_TEMPLATE = ('<h1>%(title)s</h1>\n'
- '<div class="text">%(html)s</div>\n'
- '<div class="tags">%(tags)s</div>')
+ '<div class="text">%(html)s</div>\n'
+ '<div class="tags">%(tags)s</div>')
+
def question_revisions(request, id):
post = get_object_or_404(Question, id=id)
revisions = list(post.revisions.all())
@@ -706,13 +777,13 @@ def question_revisions(request, id):
'title': revisions[0].title,
'html': sanitize_html(markdowner.convert(revisions[0].text)),
'tags': ' '.join(['<a class="post-tag">%s</a>' % tag
- for tag in revisions[0].tagnames.split(' ')]),
+ for tag in revisions[0].tagnames.split(' ')]),
}
revisions[i].summary = _('initial version')
return render_to_response('revisions_question.html', {
- 'post': post,
- 'revisions': revisions,
- }, context_instance=RequestContext(request))
+ 'post': post,
+ 'revisions': revisions,
+ }, context_instance=RequestContext(request))
ANSWER_REVISION_TEMPLATE = ('<div class="text">%(html)s</div>')
def answer_revisions(request, id):
@@ -729,9 +800,9 @@ def answer_revisions(request, id):
revisions[i].diff = revisions[i].text
revisions[i].summary = _('initial version')
return render_to_response('revisions_answer.html', {
- 'post': post,
- 'revisions': revisions,
- }, context_instance=RequestContext(request))
+ 'post': post,
+ 'revisions': revisions,
+ }, context_instance=RequestContext(request))
def answer(request, id):
question = get_object_or_404(Question, id=id)
@@ -744,25 +815,25 @@ def answer(request, id):
if request.user.is_authenticated():
create_new_answer(
- question=question,
- author=request.user,
- added_at=update_time,
- wiki=wiki,
- text=text,
- email_notify=form.cleaned_data['email_notify']
- )
+ question=question,
+ author=request.user,
+ added_at=update_time,
+ wiki=wiki,
+ text=text,
+ email_notify=form.cleaned_data['email_notify']
+ )
else:
request.session.flush()
html = sanitize_html(markdowner.convert(text))
summary = strip_tags(html)[:120]
anon = AnonymousAnswer(
- question = question,
- wiki = wiki,
- text = text,
- summary = summary,
- session_key = request.session.session_key,
- ip_addr = request.META['REMOTE_ADDR'],
- )
+ question=question,
+ wiki=wiki,
+ text=text,
+ summary=summary,
+ session_key=request.session.session_key,
+ ip_addr=request.META['REMOTE_ADDR'],
+ )
anon.save()
return HttpResponseRedirect(reverse('user_signin_new_answer'))
@@ -793,22 +864,21 @@ def tags(request):
tags = objects_list.page(objects_list.num_pages)
return render_to_response('tags.html', {
- "tags" : tags,
- "stag" : stag,
- "tab_id" : sortby,
- "keywords" : stag,
- "context" : {
- 'is_paginated' : is_paginated,
- 'pages': objects_list.num_pages,
- 'page': page,
- 'has_previous': tags.has_previous(),
- 'has_next': tags.has_next(),
- 'previous': tags.previous_page_number(),
- 'next': tags.next_page_number(),
- 'base_url' : reverse('tags') + '?sort=%s&' % sortby
- }
-
- }, context_instance=RequestContext(request))
+ "tags" : tags,
+ "stag" : stag,
+ "tab_id" : sortby,
+ "keywords" : stag,
+ "context" : {
+ 'is_paginated' : is_paginated,
+ 'pages': objects_list.num_pages,
+ 'page': page,
+ 'has_previous': tags.has_previous(),
+ 'has_next': tags.has_next(),
+ 'previous': tags.previous_page_number(),
+ 'next': tags.next_page_number(),
+ 'base_url' : reverse('tags') + '?sort=%s&' % sortby
+ }
+ }, context_instance=RequestContext(request))
def tag(request, tag):
return questions(request, tagname=tag)
@@ -1042,6 +1112,42 @@ def vote(request, id):
data = simplejson.dumps(response_data)
return HttpResponse(data, mimetype="application/json")
+@ajax_login_required
+def mark_tag(request, tag=None, **kwargs):
+ action = kwargs['action']
+ ts = MarkedTag.objects.filter(user=request.user, tag__name=tag)
+ if action == 'remove':
+ logging.debug('deleting tag %s' % tag)
+ ts.delete()
+ else:
+ reason = kwargs['reason']
+ if len(ts) == 0:
+ try:
+ t = Tag.objects.get(name=tag)
+ mt = MarkedTag(user=request.user, reason=reason, tag=t)
+ mt.save()
+ except:
+ pass
+ else:
+ ts.update(reason=reason)
+ return HttpResponse(simplejson.dumps(''), mimetype="application/json")
+
+@ajax_login_required
+def ajax_toggle_ignored_questions(request):
+ if request.user.hide_ignored_questions:
+ new_hide_setting = False
+ else:
+ new_hide_setting = True
+ request.user.hide_ignored_questions = new_hide_setting
+ request.user.save()
+
+@ajax_method
+def ajax_command(request):
+ if 'command' not in request.POST:
+ return HttpResponseForbidden(mimetype="application/json")
+ if request.POST['command'] == 'toggle-ignored-questions':
+ return ajax_toggle_ignored_questions(request)
+
def users(request):
is_paginated = True
sortby = request.GET.get('sort', 'reputation')
@@ -1073,22 +1179,22 @@ def users(request):
users = objects_list.page(objects_list.num_pages)
return render_to_response('users.html', {
- "users" : users,
- "suser" : suser,
- "keywords" : suser,
- "tab_id" : sortby,
- "context" : {
- 'is_paginated' : is_paginated,
- 'pages': objects_list.num_pages,
- 'page': page,
- 'has_previous': users.has_previous(),
- 'has_next': users.has_next(),
- 'previous': users.previous_page_number(),
- 'next': users.next_page_number(),
- 'base_url' : base_url
- }
-
- }, context_instance=RequestContext(request))
+ "users" : users,
+ "suser" : suser,
+ "keywords" : suser,
+ "tab_id" : sortby,
+ "context" : {
+ 'is_paginated' : is_paginated,
+ 'pages': objects_list.num_pages,
+ 'page': page,
+ 'has_previous': users.has_previous(),
+ 'has_next': users.has_next(),
+ 'previous': users.previous_page_number(),
+ 'next': users.next_page_number(),
+ 'base_url' : base_url
+ }
+
+ }, context_instance=RequestContext(request))
def user(request, id):
sort = request.GET.get('sort', 'stats')
@@ -1130,6 +1236,7 @@ def edit_user(request, id):
from django_authopenid.views import set_new_email
set_new_email(user, new_email)
+ user.username = sanitize_html(form.cleaned_data['username'])
user.real_name = sanitize_html(form.cleaned_data['realname'])
user.website = sanitize_html(form.cleaned_data['website'])
user.location = sanitize_html(form.cleaned_data['city'])
@@ -1147,9 +1254,9 @@ def edit_user(request, id):
else:
form = EditUserForm(user)
return render_to_response('user_edit.html', {
- 'form' : form,
- 'gravatar_faq_url' : reverse('faq') + '#gravatar',
- }, context_instance=RequestContext(request))
+ 'form' : form,
+ 'gravatar_faq_url' : reverse('faq') + '#gravatar',
+ }, context_instance=RequestContext(request))
def user_stats(request, user_id, user_view):
user = get_object_or_404(User, id=user_id)
@@ -1216,32 +1323,52 @@ def user_stats(request, user_id, user_view):
'answer_count',
'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)
votes_total = VOTE_RULES['scope_votes_per_user_per_day']
- tags = user.created_tags.all().order_by('-used_count')[:50]
- if settings.DJANGO_VERSION < 1.1:
+
+ 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)
+ try:
+ from django.db.models import Count
awards = Award.objects.extra(
- select={'id': 'badge.id', 'count': 'count(badge_id)', 'name':'badge.name', 'description': 'badge.description', 'type': 'badge.type'},
- tables=['award', 'badge'],
- order_by=['-awarded_at'],
- where=['user_id=%s AND badge_id=badge.id'],
- params=[user.id]
- ).values('id', 'count', 'name', 'description', 'type')
+ select={'id': 'badge.id',
+ 'name':'badge.name',
+ 'description': 'badge.description',
+ 'type': 'badge.type'},
+ tables=['award', 'badge'],
+ order_by=['-awarded_at'],
+ where=['user_id=%s AND badge_id=badge.id'],
+ params=[user.id]
+ ).values('id', 'name', 'description', 'type')
total_awards = awards.count()
- awards.query.group_by = ['badge_id']
- else:
+ awards = awards.annotate(count = Count('badge__id'))
+ user_tags = user_tags.annotate(user_tag_usage_count=Count('name'))
+
+ except ImportError:
awards = Award.objects.extra(
- select={'id': 'badge.id', 'name':'badge.name', 'description': 'badge.description', 'type': 'badge.type'},
- tables=['award', 'badge'],
- order_by=['-awarded_at'],
- where=['user_id=%s AND badge_id=badge.id'],
- params=[user.id]
- ).values('id', 'name', 'description', 'type')
+ select={'id': 'badge.id',
+ 'count': 'count(badge_id)',
+ 'name':'badge.name',
+ 'description': 'badge.description',
+ 'type': 'badge.type'},
+ tables=['award', 'badge'],
+ order_by=['-awarded_at'],
+ where=['user_id=%s AND badge_id=badge.id'],
+ params=[user.id]
+ ).values('id', 'count', 'name', 'description', 'type')
total_awards = awards.count()
- from django.db.models import Count
- awards = awards.annotate(count = Count('badge__id'))
+ awards.query.group_by = ['badge_id']
+
+ user_tags = user_tags.extra(
+ select={'user_tag_usage_count': 'COUNT(1)',},
+ order_by=['-user_tag_usage_count'],
+ )
+ user_tags.query.group_by = ['name']
if auth.can_moderate_users(request.user):
moderate_user_form = ModerateUserForm(instance=user)
@@ -1249,22 +1376,23 @@ def user_stats(request, user_id, user_view):
moderate_user_form = None
return render_to_response(user_view.template_file,{
- 'moderate_user_form': moderate_user_form,
- "tab_name" : user_view.id,
- "tab_description" : user_view.tab_description,
- "page_title" : user_view.page_title,
- "view_user" : user,
- "questions" : questions,
- "answered_questions" : answered_questions,
- "up_votes" : up_votes,
- "down_votes" : down_votes,
- "total_votes": up_votes + down_votes,
- "votes_today_left": votes_total-votes_today,
- "votes_total_per_day": votes_total,
- "tags" : tags,
- "awards": awards,
- "total_awards" : total_awards,
- }, context_instance=RequestContext(request))
+ 'moderate_user_form': moderate_user_form,
+ "tab_name" : user_view.id,
+ "tab_description" : user_view.tab_description,
+ "page_title" : user_view.page_title,
+ "view_user" : user,
+ "questions" : questions,
+ "answered_questions" : answered_questions,
+ "up_votes" : up_votes,
+ "down_votes" : down_votes,
+ "total_votes": up_votes + down_votes,
+ "votes_today_left": votes_total-votes_today,
+ "votes_total_per_day": votes_total,
+ "user_tags" : user_tags[:50],
+ "tags" : tags,
+ "awards": awards,
+ "total_awards" : total_awards,
+ }, context_instance=RequestContext(request))
def user_recent(request, user_id, user_view):
user = get_object_or_404(User, id=user_id)
@@ -1314,7 +1442,7 @@ def user_recent(request, user_id, user_view):
)
if len(questions) > 0:
questions = [(Event(q['active_at'], q['activity_type'], q['title'], '', '0', \
- q['question_id'])) for q in questions]
+ q['question_id'])) for q in questions]
activities.extend(questions)
# answers
@@ -1341,7 +1469,7 @@ def user_recent(request, user_id, user_view):
)
if len(answers) > 0:
answers = [(Event(q['active_at'], q['activity_type'], q['title'], '', q['answer_id'], \
- q['question_id'])) for q in answers]
+ q['question_id'])) for q in answers]
activities.extend(answers)
# question comments
@@ -1369,7 +1497,7 @@ def user_recent(request, user_id, user_view):
if len(comments) > 0:
comments = [(Event(q['added_at'], q['activity_type'], q['title'], '', '0', \
- q['question_id'])) for q in comments]
+ q['question_id'])) for q in comments]
activities.extend(comments)
# answer comments
@@ -1400,7 +1528,7 @@ def user_recent(request, user_id, user_view):
if len(comments) > 0:
comments = [(Event(q['added_at'], q['activity_type'], q['title'], '', q['answer_id'], \
- q['question_id'])) for q in comments]
+ q['question_id'])) for q in comments]
activities.extend(comments)
# question revisions
@@ -1429,7 +1557,7 @@ def user_recent(request, user_id, user_view):
if len(revisions) > 0:
revisions = [(Event(q['added_at'], q['activity_type'], q['title'], q['summary'], '0', \
- q['question_id'])) for q in revisions]
+ q['question_id'])) for q in revisions]
activities.extend(revisions)
# answer revisions
@@ -1462,7 +1590,7 @@ def user_recent(request, user_id, user_view):
if len(revisions) > 0:
revisions = [(Event(q['added_at'], q['activity_type'], q['title'], q['summary'], \
- q['answer_id'], q['question_id'])) for q in revisions]
+ q['answer_id'], q['question_id'])) for q in revisions]
activities.extend(revisions)
# accepted answers
@@ -1514,12 +1642,12 @@ def user_recent(request, user_id, user_view):
activities.sort(lambda x,y: cmp(y.time, x.time))
return render_to_response(user_view.template_file,{
- "tab_name" : user_view.id,
- "tab_description" : user_view.tab_description,
- "page_title" : user_view.page_title,
- "view_user" : user,
- "activities" : activities[:user_view.data_size]
- }, context_instance=RequestContext(request))
+ "tab_name" : user_view.id,
+ "tab_description" : user_view.tab_description,
+ "page_title" : user_view.page_title,
+ "view_user" : user,
+ "activities" : activities[:user_view.data_size]
+ }, context_instance=RequestContext(request))
def user_responses(request, user_id, user_view):
"""
@@ -1529,7 +1657,7 @@ def user_responses(request, user_id, user_view):
def __init__(self, type, title, question_id, answer_id, time, username, user_id, content):
self.type = type
self.title = title
- self.titlelink = reverse('questions') + u'%s/%s#%s' % (question_id, title, answer_id)
+ self.titlelink = reverse('question', args=[question_id]) + u'%s#%s' % (slugify(title), answer_id)
self.time = time
self.userlink = reverse('users') + u'%s/%s/' % (user_id, username)
self.username = username
@@ -1541,30 +1669,31 @@ def user_responses(request, user_id, user_view):
user = get_object_or_404(User, id=user_id)
responses = []
answers = Answer.objects.extra(
- select={
- 'title' : 'question.title',
- 'question_id' : 'question.id',
- 'answer_id' : 'answer.id',
- 'added_at' : 'answer.added_at',
- 'html' : 'answer.html',
- 'username' : 'auth_user.username',
- 'user_id' : 'auth_user.id'
- },
- select_params=[user_id],
- tables=['answer', 'question', 'auth_user'],
- where=['answer.question_id = question.id AND answer.deleted=0 AND question.deleted = 0 AND '+
- 'question.author_id = %s AND answer.author_id <> %s AND answer.author_id=auth_user.id'],
- params=[user_id, user_id],
- order_by=['-answer.id']
- ).values(
- 'title',
- 'question_id',
- 'answer_id',
- 'added_at',
- 'html',
- 'username',
- 'user_id'
- )
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'question.id',
+ 'answer_id' : 'answer.id',
+ 'added_at' : 'answer.added_at',
+ 'html' : 'answer.html',
+ 'username' : 'auth_user.username',
+ 'user_id' : 'auth_user.id'
+ },
+ select_params=[user_id],
+ tables=['answer', 'question', 'auth_user'],
+ where=['answer.question_id = question.id AND answer.deleted=0 AND question.deleted = 0 AND '+
+ 'question.author_id = %s AND answer.author_id <> %s AND answer.author_id=auth_user.id'],
+ params=[user_id, user_id],
+ order_by=['-answer.id']
+ ).values(
+ 'title',
+ 'question_id',
+ 'answer_id',
+ 'added_at',
+ 'html',
+ 'username',
+ 'user_id'
+ )
+
if len(answers) > 0:
answers = [(Response(TYPE_RESPONSE['QUESTION_ANSWERED'], a['title'], a['question_id'],
a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers]
@@ -1573,27 +1702,27 @@ def user_responses(request, user_id, user_view):
# question comments
comments = Comment.objects.extra(
- select={
- 'title' : 'question.title',
- 'question_id' : 'comment.object_id',
- 'added_at' : 'comment.added_at',
- 'comment' : 'comment.comment',
- 'username' : 'auth_user.username',
- 'user_id' : 'auth_user.id'
- },
- tables=['question', 'auth_user', 'comment'],
- where=['question.deleted = 0 AND question.author_id = %s AND comment.object_id=question.id AND '+
- 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id'],
- params=[user_id, question_type_id, user_id],
- order_by=['-comment.added_at']
- ).values(
- 'title',
- 'question_id',
- 'added_at',
- 'comment',
- 'username',
- 'user_id'
- )
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'comment.object_id',
+ 'added_at' : 'comment.added_at',
+ 'comment' : 'comment.comment',
+ 'username' : 'auth_user.username',
+ 'user_id' : 'auth_user.id'
+ },
+ tables=['question', 'auth_user', 'comment'],
+ where=['question.deleted = 0 AND question.author_id = %s AND comment.object_id=question.id AND '+
+ 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id'],
+ params=[user_id, question_type_id, user_id],
+ order_by=['-comment.added_at']
+ ).values(
+ 'title',
+ 'question_id',
+ 'added_at',
+ 'comment',
+ 'username',
+ 'user_id'
+ )
if len(comments) > 0:
comments = [(Response(TYPE_RESPONSE['QUESTION_COMMENTED'], c['title'], c['question_id'],
@@ -1739,16 +1868,27 @@ def user_votes(request, user_id, user_view):
def user_reputation(request, user_id, user_view):
user = get_object_or_404(User, id=user_id)
- reputation = Repute.objects.extra(
- select={'positive': 'sum(positive)', 'negative': 'sum(negative)', 'question_id':'question_id',
- 'title': 'question.title'},
- tables=['repute', 'question'],
- order_by=['-reputed_at'],
- where=['user_id=%s AND question_id=question.id'],
- params=[user.id]
- ).values('positive', 'negative', 'question_id', 'title', 'reputed_at', 'reputation')
-
- reputation.query.group_by = ['question_id']
+ try:
+ from django.db.models import Sum
+ reputation = Repute.objects.extra(
+ select={'question_id':'question_id',
+ 'title': 'question.title'},
+ tables=['repute', 'question'],
+ order_by=['-reputed_at'],
+ where=['user_id=%s AND question_id=question.id'],
+ params=[user.id]
+ ).values('question_id', 'title', 'reputed_at', 'reputation')
+ reputation = reputation.annotate(positive=Sum("positive"), negative=Sum("negative"))
+ except ImportError:
+ reputation = Repute.objects.extra(
+ select={'positive':'sum(positive)', 'negative':'sum(negative)', 'question_id':'question_id',
+ 'title': 'question.title'},
+ tables=['repute', 'question'],
+ order_by=['-reputed_at'],
+ where=['user_id=%s AND question_id=question.id'],
+ params=[user.id]
+ ).values('positive', 'negative', 'question_id', 'title', 'reputed_at', 'reputation')
+ reputation.query.group_by = ['question_id']
rep_list = []
for rep in Repute.objects.filter(user=user).order_by('reputed_at'):
@@ -1757,14 +1897,14 @@ def user_reputation(request, user_id, user_view):
reps = ','.join(rep_list)
reps = '[%s]' % reps
- return render_to_response(user_view.template_file,{
- "tab_name" : user_view.id,
- "tab_description" : user_view.tab_description,
- "page_title" : user_view.page_title,
- "view_user" : user,
- "reputation" : reputation,
- "reps" : reps
- }, context_instance=RequestContext(request))
+ return render_to_response(user_view.template_file, {
+ "tab_name": user_view.id,
+ "tab_description": user_view.tab_description,
+ "page_title": user_view.page_title,
+ "view_user": user,
+ "reputation": reputation,
+ "reps": reps
+ }, context_instance=RequestContext(request))
def user_favorites(request, user_id, user_view):
user = get_object_or_404(User, id=user_id)
@@ -1816,34 +1956,39 @@ def user_favorites(request, user_id, user_view):
"view_user" : user
}, context_instance=RequestContext(request))
-
def user_email_subscriptions(request, user_id, user_view):
user = get_object_or_404(User, id=user_id)
if request.method == 'POST':
- form = EditUserEmailFeedsForm(request.POST)
- if form.is_valid():
+ email_feeds_form = EditUserEmailFeedsForm(request.POST)
+ tag_filter_form = TagFilterSelectionForm(request.POST, instance=user)
+ if email_feeds_form.is_valid() and tag_filter_form.is_valid():
+
+ action_status = None
+ tag_filter_saved = tag_filter_form.save()
+ if tag_filter_saved:
+ action_status = _('changes saved')
if 'save' in request.POST:
- saved = form.save(user)
- if saved:
+ feeds_saved = email_feeds_form.save(user)
+ if feeds_saved:
action_status = _('changes saved')
elif 'stop_email' in request.POST:
- saved = form.reset().save(user)
+ email_stopped = email_feeds_form.reset().save(user)
initial_values = EditUserEmailFeedsForm.NO_EMAIL_INITIAL
- form = EditUserEmailFeedsForm(initial=initial_values)
- if saved:
+ email_feeds_form = EditUserEmailFeedsForm(initial=initial_values)
+ if email_stopped:
action_status = _('email updates canceled')
- if not saved:
- action_status = None
else:
- form = EditUserEmailFeedsForm()
- form.set_initial_values(user)
+ email_feeds_form = EditUserEmailFeedsForm()
+ email_feeds_form.set_initial_values(user)
+ tag_filter_form = TagFilterSelectionForm(instance=user)
action_status = None
return render_to_response(user_view.template_file,{
'tab_name':user_view.id,
'tab_description':user_view.tab_description,
'page_title':user_view.page_title,
'view_user':user,
- 'email_feeds_form':form,
+ 'email_feeds_form':email_feeds_form,
+ 'tag_filter_selection_form':tag_filter_form,
'action_status':action_status,
}, context_instance=RequestContext(request))
@@ -1985,7 +2130,7 @@ def upload(request):
if not file_name_suffix in settings.ALLOW_FILE_TYPES:
raise FileTypeNotAllow
- # genetate new file name
+ # generate new file name
new_file_name = str(time.time()).replace('.', str(random.randint(0,100000))) + file_name_suffix
# use default storage to store file
default_storage.save(new_file_name, f)
@@ -2022,7 +2167,7 @@ def book(request, short_name, unanswered=False):
"""
books = Book.objects.extra(where=['short_name = %s'], params=[short_name])
match_count = len(books)
- if match_count == 0 :
+ if match_count == 0:
raise Http404
else:
# the book info
@@ -2193,10 +2338,16 @@ def search(request):
view_id = "latest"
orderby = "-added_at"
- objects = Question.objects.filter(deleted=False).extra(where=['title like %s'], params=['%' + keywords + '%']).order_by(orderby)
+ if settings.USE_SPHINX_SEARCH == True:
+ #search index is now free of delete questions and answers
+ #so there is not "antideleted" filtering here
+ objects = Question.search.query(keywords)
+ #no related selection either because we're relying on full text search here
+ else:
+ objects = Question.objects.filter(deleted=False).extra(where=['title like %s'], params=['%' + keywords + '%']).order_by(orderby)
+ # RISK - inner join queries
+ objects = objects.select_related();
- # RISK - inner join queries
- objects = objects.select_related();
objects_list = Paginator(objects, pagesize)
questions = objects_list.page(page)