summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2009-12-09 19:57:49 -0500
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2009-12-09 20:02:36 -0500
commitfdac57838a27f3837b0ef187a9a3f86f293d29b1 (patch)
treeba9975b0e22fa02c97608f030395b4c26c31e972
parent4be0491e6adef6bf5817545ec9e050555066352f (diff)
downloadaskbot-fdac57838a27f3837b0ef187a9a3f86f293d29b1.tar.gz
askbot-fdac57838a27f3837b0ef187a9a3f86f293d29b1.tar.bz2
askbot-fdac57838a27f3837b0ef187a9a3f86f293d29b1.zip
added sitemap at /sitemap.xml and fixed per tag subscription (hopefully)
-rw-r--r--.gitignore2
-rw-r--r--forum/const.py2
-rw-r--r--forum/forms.py8
-rw-r--r--forum/management/commands/send_email_alerts.py145
-rw-r--r--forum/models.py5
-rw-r--r--forum/sitemap.py11
-rw-r--r--forum/urls.py5
-rw-r--r--forum/views.py21
-rw-r--r--settings.py1
-rw-r--r--templates/about.html26
-rw-r--r--templates/unanswered.html132
11 files changed, 127 insertions, 231 deletions
diff --git a/.gitignore b/.gitignore
index b217d5cc..69133c46 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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&amp;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&amp;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&amp;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&amp;A</span> site is moderated by its members, hopefully - including yourself!
+ <p>This <span class="orange">Q&amp;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"> &#215; {{ tag.used_count|intcomma }}</span>
- <br/>
- {% endfor %}
- <br/>
- </div>
- </div>
-</div>
-
-{% endblock %}
-<!-- end unanswered.html -->