diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | forum/const.py | 2 | ||||
-rw-r--r-- | forum/forms.py | 8 | ||||
-rw-r--r-- | forum/management/commands/send_email_alerts.py | 145 | ||||
-rw-r--r-- | forum/models.py | 5 | ||||
-rw-r--r-- | forum/sitemap.py | 11 | ||||
-rw-r--r-- | forum/urls.py | 5 | ||||
-rw-r--r-- | forum/views.py | 21 | ||||
-rw-r--r-- | settings.py | 1 | ||||
-rw-r--r-- | templates/about.html | 26 | ||||
-rw-r--r-- | templates/unanswered.html | 132 |
11 files changed, 127 insertions, 231 deletions
@@ -1,5 +1,5 @@ *.pyc *.swp *.log -settings_local.py nbproject +settings_local.py diff --git a/forum/const.py b/forum/const.py index 91b6f5e5..76fd4a24 100644 --- a/forum/const.py +++ b/forum/const.py @@ -89,4 +89,4 @@ CONST = { } #how to filter questions by tags in email digests? -TAG_EMAIL_FILTER_CHOICES = (('ignored', _('exclude ignored tags')),('interesting',_('allow only interesting tags'))) +TAG_EMAIL_FILTER_CHOICES = (('ignored', _('exclude ignored tags')),('interesting',_('allow only selected tags'))) diff --git a/forum/forms.py b/forum/forms.py index ecdd9c95..ad2c5bac 100644 --- a/forum/forms.py +++ b/forum/forms.py @@ -241,6 +241,14 @@ class TagFilterSelectionForm(forms.ModelForm): 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'))) diff --git a/forum/management/commands/send_email_alerts.py b/forum/management/commands/send_email_alerts.py index 4a636e87..777381ec 100644 --- a/forum/management/commands/send_email_alerts.py +++ b/forum/management/commands/send_email_alerts.py @@ -38,84 +38,79 @@ class Command(NoArgsCommand): ).exclude( closed=True ) - #todo: still need to add back individually selected and other questions.... - #these may be filtered out by tags - if user.tag_filter_setting == 'ignored': - ignored_tags = Tag.objects.filter(user_selections___reason='bad',user_selections__user=user) - Q_set1 = Q_set1.exclude( tags__in=ignored_tags ) - logging.debug('removed ignored tags') - else: - selected_tags = Tag.objects.filter(user_selections___reason='good',user_selections__user=user) - Q_set1 = Q_set1.filter( tags__in=selected_tags ) - logging.debug('filtered for only selected tags') - - 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: - Q_set = Q_set1.exclude(last_activity_at__gt=cutoff_time)#report these excluded later - 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) - q_sel.cutoff_time = cutoff_time #store cutoff time per query set - elif feed.feed_type == 'q_ask': - q_ask = Q_set.filter(author=user) - q_ask.cutoff_time = cutoff_time - elif feed.feed_type == 'q_ans': - q_ans = Q_set.filter(answers__author=user) - q_ans.cutoff_time = cutoff_time - elif feed.feed_type == 'q_all': - q_all = Q_set - 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} + + 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: + Q_set = Q_set1.exclude(last_activity_at__gt=cutoff_time)#report these excluded later + 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) + q_sel.cutoff_time = cutoff_time #store cutoff time per query set + elif feed.feed_type == 'q_ask': + q_ask = Q_set.filter(author=user) + q_ask.cutoff_time = cutoff_time + elif feed.feed_type == 'q_ans': + q_ans = Q_set.filter(answers__author=user) + q_ans.cutoff_time = cutoff_time + elif feed.feed_type == 'q_all': + 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) + 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') + 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: + 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: diff --git a/forum/models.py b/forum/models.py index b031b0c8..eb3650f8 100644 --- a/forum/models.py +++ b/forum/models.py @@ -13,6 +13,7 @@ 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 +from django.contrib.sitemaps import ping_google import django.dispatch import settings import logging @@ -208,6 +209,10 @@ class Question(models.Model): """ initial_addition = (self.id is None) super(Question, self).save(**kwargs) + try: + ping_google() + except Exception: + logging.debug('problem pinging google did you register you sitemap with google?') if initial_addition: tags = Tag.objects.get_or_create_multiple(self.tagname_list(), self.author) 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/urls.py b/forum/urls.py index 927e149c..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', diff --git a/forum/views.py b/forum/views.py index c76abf3b..20ca2f6a 100644 --- a/forum/views.py +++ b/forum/views.py @@ -876,7 +876,8 @@ def tags(request): '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) @@ -1886,12 +1887,14 @@ def user_reputation(request, user_id, user_view): 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'): dic = '[%s,%s]' % (calendar.timegm(rep.reputed_at.timetuple()) * 1000, rep.reputation) rep_list.append(dic) 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, @@ -1957,19 +1960,21 @@ def user_email_subscriptions(request, user_id, user_view): 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(): - tag_filter_form.save() + + action_status = None + tag_filter_saved = tag_filter_form.save() + if tag_filter_saved: + action_status = _('changes saved') if 'save' in request.POST: - saved = email_feeds_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 = email_feeds_form.reset().save(user) + email_stopped = email_feeds_form.reset().save(user) initial_values = EditUserEmailFeedsForm.NO_EMAIL_INITIAL email_feeds_form = EditUserEmailFeedsForm(initial=initial_values) - if saved: + if email_stopped: action_status = _('email updates canceled') - if not saved: - action_status = None else: email_feeds_form = EditUserEmailFeedsForm() email_feeds_form.set_initial_values(user) diff --git a/settings.py b/settings.py index a32b2303..6310ce41 100644 --- a/settings.py +++ b/settings.py @@ -59,6 +59,7 @@ INSTALLED_APPS = ( 'django.contrib.sites', 'django.contrib.admin', 'django.contrib.humanize', + 'django.contrib.sitemaps', 'forum', 'django_authopenid', 'djangosphinx', diff --git a/templates/about.html b/templates/about.html index 671964cc..2ca75500 100644 --- a/templates/about.html +++ b/templates/about.html @@ -12,31 +12,29 @@ </div> <div class="content"> - <!-- default text - <p>edit file templates/about.html. Below are just suggestions of what can go here</p> - <p>what is your site for?</p> - <p>how does it work? what are roles of members?</p> - <p>is there a place to find out more about this website?</p> - --> - <p>{% blocktrans %}<strong>CNPROG <span class="orange">Q&A</span></strong> is a collaboratively edited question - and answer site created for the <strong>CNPROG</strong> community. - {% endblocktrans %} + <p><strong>NMR Wiki <span class="orange">Q&A</span></strong> is a collaboratively edited question + and answer site created for the <strong>Magnetic Resonance</strong> community, i.e. those people who + work in the fields of <strong>NMR</strong>, <strong>EPR</strong>, <strong>MRI</strong></strong>, etc. + NMR Wiki Q&A is affiliated with <strong><a href="http://nmrwiki.org">NMR Wiki</a></strong> - + the public wiki knowledge base about the Magnetic Resonance, which currently counts ~300 registered users. The most useful information collected here + will be further distilled on the wiki site. </p> - <p>{% blocktrans %}Here you can <strong>ask</strong> and <strong>answer</strong> questions, <strong>comment</strong> + <p>Here you can <strong>ask</strong> and <strong>answer</strong> questions, <strong>comment</strong> and <strong>vote</strong> for the questions of others and their answers. Both questions and answers <strong>can be revised</strong> and improved. Questions can be <strong>tagged</strong> with - the relevant keywords to simplify future access and organize the accumulated material.{% endblocktrans %} + the relevant keywords to simplify future access and organize the accumulated material. </p> - <p>{% blocktrans %}This <span class="orange">Q&A</span> site is moderated by its members, hopefully - including yourself! + <p>This <span class="orange">Q&A</span> site is moderated by its members, hopefully - including yourself! Moderation rights are gradually assigned to the site users based on the accumulated <strong>"reputation"</strong> points. These points are added to the users account when others vote for his/her questions or answers. - These points (very) roughly reflect the level of trust of the community.{% endblocktrans %} + These points (very) roughly reflect the level of trust of the community. </p> <p>No points are necessary to ask or answer the questions - so please - <strong><a href="{% url user_signin %}">join us!</a></strong> </p> - <p>If you would like to find out more about this site - please see <strong><a href="{% url faq %}">frequently asked questions</a></strong>. + <p> + If you would like to find out more about this site - please see <strong><a href="{% url faq %}">frequently asked questions</a></strong>. </p> </div> {% endblock %} diff --git a/templates/unanswered.html b/templates/unanswered.html deleted file mode 100644 index 80a23631..00000000 --- a/templates/unanswered.html +++ /dev/null @@ -1,132 +0,0 @@ -{% extends "base.html" %} -<!-- unanswered.html --> -{% load extra_tags %} -{% load i18n %} -{% load humanize %} -{% load extra_filters %} -{% load smart_if %} -{% block title %}{% spaceless %}{% trans "Unanswered questions" %}{% endspaceless %}{% endblock %} -{% block forejs %} - <script type="text/javascript"> - $().ready(function(){ - $("#nav_unanswered").attr('className',"on"); - }); - - </script> -{% endblock %} -{% block content %} -<div class="tabBar"> - <span class="headQuestions">{% trans "Unanswered questions" %}</span> - <div class="tabsA"> - <a id="latest" href="{% url unanswered %}?sort=latest" class="on" title="{% trans "most recently asked questions" %}">{% trans "newest" %}</a> - </div> -</div> -<div id="listA"> - {% for question in questions.object_list %} - <div class="qstA"> - <h2> - <a href="{{ question.get_absolute_url }}">{{ question.get_question_title }}</a> - </h2> - <div class="stat"> - <table> - <tr> - <td><span class="num">{{ question.answer_count|intcomma }}</span> </td> - <td><span class="num">{{ question.score|intcomma }}</span> </td> - <td><span class="num">{{ question.view_count|cnprog_intword|safe }}</span> </td> - </tr> - <tr> - <td><span class="unit">{% trans "answers" %}</span></td> - <td><span class="unit">{% trans "votes" %}</span></td> - <td><span class="unit">{% trans "views" %}</span></td> - </tr> - </table> - </div> - - <div class="summary"> - {{ question.summary }}... - </div> - - {% ifequal tab_id 'active'%} - {% if question.wiki and settings.WIKI_ON %} - <span class="from wiki">{% trans "community wiki" %}</span> - <span class="date" title="{{ question.added_at }}">{% diff_date question.added_at %}</span> - {% else %} - <div class="from"> - {% comment %}{% gravatar question.last_activity_by 24 %}{% endcomment %} - <span class="author"><a href="{{ question.last_activity_by.get_profile_url }}">{{ question.last_activity_by }}</a></span> - <span class="score">{% get_score_badge question.last_activity_by %} </span> - <span class="date" title="{{ question.last_activity_at }}">{% diff_date question.last_activity_at %}</span> - </div> - {% endif %} - {% else %} - {% if question.wiki and settings.WIKI_ON %} - <span class="from wiki">{% trans "community wiki" %}</span> - <span class="date" title="{{ question.added_at }}">{% diff_date question.added_at %}</span> - {% else %} - <div class="from"> - {% comment %}{% gravatar question.author 24 %}{% endcomment %} - {% if question.last_activity_at != question.added_at %} - {% if question.author.id != question.last_activity_by.id %} - {% trans "Posted:" %} - <span class="author"><a href="{{ question.author.get_profile_url }}">{{ question.author }}</a></span> - <span class="score">{% get_score_badge question.author %} </span> - / {% trans "Updated:" %} - <span class="author"><a href="{{ question.last_activity_by.get_profile_url }}">{{ question.last_activity_by }}</a></span> - <span class="score">{% get_score_badge question.last_activity_by %} </span> - <span class="date" title="{{ question.last_activity_at }}">{% diff_date question.last_activity_at %}</span> - {% else %} - {% trans "Updated:" %} - <span class="author"><a href="{{ question.last_activity_by.get_profile_url }}">{{ question.last_activity_by }}</a></span> - <span class="score">{% get_score_badge question.last_activity_by %} </span> - <span class="date" title="{{ question.last_activity_at }}">{% diff_date question.last_activity_at %}</span> - {% endif %} - {% else %} - {% trans "Posted:" %} - <span class="author"><a href="{{ question.author.get_profile_url }}">{{ question.author }}</a></span> - <span class="score">{% get_score_badge question.author %} </span> - <span class="date" title="{{ question.added_at }}">{% diff_date question.added_at %}</span> - {% endif %} - </div> - {% endif %} - {% endifequal %} - - <div class="tags"> - {% for tag in question.tagname_list %} - <a href="{% url forum.views.tag tag|urlencode %}" title="{% trans "see questions tagged" %}'{{ tag }}'{% trans "using tags" %}" rel="tag">{{ tag }}</a> - {% endfor %} - </div> - </div> - {% endfor %} -</div> -{% endblock %} - -{% block tail %} - <div class="pager"> - {% cnprog_paginator context %} - </div> - <div class="pagesize"> - {% cnprog_pagesize context %} - </div> -{% endblock %} - -{% block sidebar %} -<div class="boxC"> - {% blocktrans with questions_count|intcomma as num_q %}have {{num_q}} unanswered questions{% endblocktrans %} - <!---<p>问题按 <strong>问题创建时间</strong> 排序。最新加入的问题将显示在最前面。</p>--> -</div> -<div class="boxC"> - <h3 class="subtitle">{% trans "Related tags" %}</h3> - <div class="body"> - <div class="tags"> - {% for tag in tags %} - <a rel="tag" title="{% trans "see questions tagged"%}'{{ tag.name }}'{% trans "using tags" %}" href="{% url forum.views.tag tag.name|urlencode %}">{{ tag.name }}</a> - <span class="tag-number"> × {{ tag.used_count|intcomma }}</span> - <br/> - {% endfor %} - <br/> - </div> - </div> -</div> - -{% endblock %} -<!-- end unanswered.html --> |