]> git.openstreetmap.org Git - osqa.git/commitdiff
Fixes (finaly) the email digest. Needs some improvements and some tweaks in the user...
authorhernani <hernani@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Mon, 28 Jun 2010 15:50:19 +0000 (15:50 +0000)
committerhernani <hernani@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Mon, 28 Jun 2010 15:50:19 +0000 (15:50 +0000)
git-svn-id: http://svn.osqa.net/svnroot/osqa/trunk@463 0cfe37f9-358a-4d5e-be75-b63607b5c754

forum/management/commands/send_email_alerts.py
forum/skins/default/templates/notifications/digest.html

index fa9d442be15ee380296d5ab0aae74f086d7d59b0..4cdb2e1d35636fa27a5061539d83095db69a067c 100644 (file)
-from datetime import datetime, timedelta
+import datetime
+from forum.models import *
+from django.db import models
+from forum.utils.mail import send_template_email
 from django.core.management.base import NoArgsCommand
-from django.utils.translation import ugettext as _
-from django.template import loader, Context, Template
-from django.core.mail import EmailMultiAlternatives
-from django.utils import translation
-from django.conf import settings
-from forum import settings
 from forum.settings.email import EMAIL_DIGEST_CONTROL
-from forum import actions
-from forum.models import KeyValue, Action, User, QuestionSubscription
-from forum.utils.mail import send_email
+from django.utils import translation
 import logging
 
-class QuestionRecord:
-    def __init__(self, question):
-        self.question = question
-        self.records = []
+SHOW_N_MORE_ACTIVE_NEW_MEMBERS = 5
+SUB_QUESTION_LIST_LENGTH = 5
+TRY_N_USER_TAGS = 5
 
-    def log_activity(self, activity):
-        self.records.append(activity)
 
-    def get_activity_since(self, since):
-        activity = [r for r in self.records if r.action_date > since]
-        answers = [a for a in activity if a.action_type == "answer"]
-        comments = [a for a in activity if a.activity_type == "comment"]
 
-        accepted = [a for a in activity if a.activity_type == "accept_answer"]
+class DigestQuestionsIndex(object):
+    def __init__(self, from_date):
+        self.from_date = from_date
 
-        if len(accepted):
-            accepted = accepted[-1:][0]
-        else:
-            accepted = None
+        new_questions = Question.objects.filter_state(deleted=False).\
+            filter(added_at__gt=from_date).\
+            annotate(n_actions=models.Count('actions')).\
+            annotate(child_count=models.Count('all_children'))
 
-        return {
-        'answers': answers,
-        'comments': comments,
-        'accepted': accepted,
-        }
+        hotness = lambda q: 3*q.child_count + q.n_actions
 
+        for q in new_questions:
+            q.hotness=hotness(q)
 
-class Command(NoArgsCommand):
-    def handle_noargs(self, **options):
+        self.questions = sorted(new_questions, lambda q1, q2: q2.hotness - q1.hotness)
+        self.count = len(self.questions)
+
+    def unseen_question(self, user, question):
         try:
-            translation.activate(settings.LANGUAGE_CODE)
+            subscription = QuestionSubscription.objects.get(question=q, user=user)
         except:
-            logging.error("Unable to set the locale in the send emails cron job")
-
-        digest_control = EMAIL_DIGEST_CONTROL.value
-
-        if digest_control is None:
-            digest_control = KeyValue(key='DIGEST_CONTROL', value={
-            'LAST_DAILY': datetime.now() - timedelta(days=1),
-            'LAST_WEEKLY': datetime.now() - timedelta(days=1),
-            })
-
-        self.send_digest('daily', 'd', digest_control.value['LAST_DAILY'])
-        digest_control.value['LAST_DAILY'] = datetime.now()
-
-        if digest_control.value['LAST_WEEKLY'] + timedelta(days=7) <= datetime.now():
-            self.send_digest('weekly', 'w', digest_control.value['LAST_WEEKLY'])
-            digest_control.value['LAST_WEEKLY'] = datetime.now()
+            subscription = None
 
-        EMAIL_DIGEST_CONTROL.set_value(digest_control)
+        return (not subscription) or subscription.last_view < q.last_activity_at
 
+    def get_for_user(self, user):
+        user_tags = list(user.marked_tags.filter(user_selections__reason='good'))
 
-    def send_digest(self, name, char_in_db, control_date):
-        new_questions, question_records = self.prepare_activity(control_date)
-        new_users = User.objects.filter(date_joined__gt=control_date)
+        if len(user_tags) < TRY_N_USER_TAGS:
+            user_tags += list(Tag.objects.filter(models.Q(nodes__author=user) | models.Q(nodes__children__author=user)) \
+                .annotate(user_tag_usage_count=models.Count('name')).order_by('-user_tag_usage_count')[:TRY_N_USER_TAGS - len(user_tags)])
 
-        digest_subject = settings.EMAIL_SUBJECT_PREFIX + _('Daily digest')
+        user_tag_names = set([t.name for t in user_tags])
 
-        users = User.objects.filter(subscription_settings__enable_notifications=True)
 
-        msgs = []
+        subscriptions = user.subscriptions.filter(added_at__lt=self.from_date, last_activity_at__gt=models.F('questionsubscription__last_view')
+                                                  ).order_by('-questionsubscription__last_view')[:SUB_QUESTION_LIST_LENGTH]
 
-        for u in users:
-            context = {
-            'user': u,
-            'digest_type': name,
-            }
+        unseen_questions = [q for q in self.questions if self.unseen_question(user, q)]
 
-            if u.subscription_settings.member_joins == char_in_db:
-                context['new_users'] = new_users
-            else:
-                context['new_users'] = False
+        interesting = []
 
-            if u.subscription_settings.subscribed_questions == char_in_db:
-                activity_in_subscriptions = []
+        for q in unseen_questions:
+            if len(set(q.tagname_list()) & user_tag_names): interesting.append(q)
 
-                for id, r in question_records.items():
-                    try:
-                        subscription = QuestionSubscription.objects.get(question=r.question, user=u)
 
-                        record = r.get_activity_since(subscription.last_view)
+        may_help = []
+        if len(interesting):
+            if len(interesting) > SUB_QUESTION_LIST_LENGTH:
+                may_help = interesting[SUB_QUESTION_LIST_LENGTH:][-SUB_QUESTION_LIST_LENGTH:]
+                interesting = interesting[:SUB_QUESTION_LIST_LENGTH]
+        else:
+            interesting = unseen_questions[:SUB_QUESTION_LIST_LENGTH]
 
-                        if not u.subscription_settings.notify_answers:
-                            del record['answers']
+        return {'interesting': interesting, 'may_help': may_help, 'subscriptions': subscriptions}
 
-                        if not u.subscription_settings.notify_comments:
-                            if u.subscription_settings.notify_comments_own_post:
-                                record.comments = [a for a in record.comments if a.user == u]
-                                record['own_comments_only'] = True
-                            else:
-                                del record['comments']
 
-                        if not u.subscription_settings.notify_accepted:
-                            del record['accepted']
 
-                        if record.get('answers', False) or record.get('comments', False) or record.get('accepted', False
-                                                                                                       ):
-                            activity_in_subscriptions.append({'question': r.question, 'activity': record})
-                    except:
-                        pass
 
-                context['activity_in_subscriptions'] = activity_in_subscriptions
-            else:
-                context['activity_in_subscriptions'] = False
+class Command(NoArgsCommand):
+    def handle_noargs(self, **options):
+        try:
+            translation.activate(settings.LANGUAGE_CODE)
+        except:
+            logging.error("Unable to set the locale in the send emails cron job")
 
-            if u.subscription_settings.new_question == char_in_db:
-                context['new_questions'] = new_questions
-                context['watched_tags_only'] = False
-            elif u.subscription_settings.new_question_watched_tags == char_in_db:
-                context['new_questions'] = [q for q in new_questions if
-                                            q.tags.filter(id__in=u.marked_tags.filter(user_selections__reason='good')
-                                                          ).count() > 0]
-                context['watched_tags_only'] = True
-            else:
-                context['new_questions'] = False
+        digest_control = EMAIL_DIGEST_CONTROL.value
 
-            if context['new_users'] or context['activity_in_subscriptions'] or context['new_questions']:
-                send_email(digest_subject, [(u.username, u.email)], "notifications/digest.html", context, threaded=False
-                           )
+        if digest_control is None:
+            digest_control = KeyValue(key='DIGEST_CONTROL', value={
+            'LAST_DAILY': datetime.datetime.now() - datetime.timedelta(days=1),
+            'LAST_WEEKLY': datetime.datetime.now() - datetime.timedelta(days=1),
+            })
 
+        from_date = digest_control.value['LAST_DAILY']
+        digest_control.value['LAST_DAILY'] = datetime.datetime.now()
 
-    def prepare_activity(self, since):
-        all_activity = Action.objects.filter(canceled=False, action_date__gt=since, action_type__in=(
-        actions.AskAction.get_type(), actions.AnswerAction.get_type(),
-        actions.CommentAction.get_type(), actions.AcceptAnswerAction.get_type()
-        )).order_by('action_date')
+        EMAIL_DIGEST_CONTROL.set_value(digest_control)
 
-        question_records = {}
-        new_questions = []
+        users = User.objects.filter(subscription_settings__enable_notifications=True)
+        new_members = User.objects.filter(is_active=True, date_joined__gt=from_date).annotate(n_actions=models.Count('actions')).order_by('-n_actions')
 
-        for activity in all_activity:
-            try:
-                question = activity.node.abs_parent
+        new_member_count = new_members.count()
 
-                if not question.id in question_records:
-                    question_records[question.id] = QuestionRecord(question)
+        if new_member_count >= SHOW_N_MORE_ACTIVE_NEW_MEMBERS:
+            new_members = new_members[:SHOW_N_MORE_ACTIVE_NEW_MEMBERS]
+            show_all_users = True
+        else:
+            show_all_users = False
 
-                question_records[question.id].log_activity(activity)
+        digest = DigestQuestionsIndex(from_date)
 
-                if activity.action_type == "ask":
-                    new_questions.append(question)
-            except:
-                pass
+        send_template_email(users, "notifications/digest.html", locals())
 
-        return new_questions, question_records
 
index 9a7ec0474c8031d1acc173485239f3ba35c061f2..99485e7be786dfe0e024d5ac10ef4e4ac29bffe2 100644 (file)
@@ -1,79 +1,96 @@
-{% extends "email_base.html" %}
-{% load i18n %}
-{% load humanize %}
-{% load extra_tags %}
-
-{% block content %}
-    <p>{% trans "Hello" %} {{ user.username }},</p>
-
-    <p>{% blocktrans with settings.APP_SHORT_NAME as app_title %}
-    This is the {{ digest_type }} activity digest for {{ app_title }}
-    {% endblocktrans %}</p>
-
-    {% if new_users %}
-        <h3>
-        {% blocktrans with new_users|length as nusers_count and new_users|length|pluralize as nusers_count_pluralize and settings.APP_SHORT_NAME as app_title %}
-            {{ nusers_count }} new user{{ nusers_count_pluralize }} joined the {{ app_title }} community:
+{% load i18n extra_tags email_tags %}
+
+{% declare %}
+    prefix = settings.EMAIL_SUBJECT_PREFIX
+    app_name = settings.APP_SHORT_NAME
+    app_url = settings.APP_URL
+
+    new_member_links = html.mark_safe(", ".join([html.objlink(u, style=settings.EMAIL_ANCHOR_STYLE) for u in new_members]))
+    new_question_count = digest.count
+
+{% enddeclare %}
+
+{% email %}
+    {% subject %}{% blocktrans %}{{ prefix }} Daily digest{% endblocktrans %}{% endsubject %}
+
+    {% htmlcontent notifications/base.html %}
+        {% blocktrans %}
+        This is a brief of what's going on the {{ app_name }} community since our last update.
+        {% endblocktrans %}
+
+        {% if new_member_count %}
+        <p style="{{ p_style }}">
+        {% if show_all_users %}
+        {% blocktrans %}
+            There are {{ new_member_count }} new members in the community. {{ new_member_links }} were the most active so far.
         {% endblocktrans %}
-        </h3>
+        {% else %}
+        {% blocktrans %}
+            {{ new_member_links }} have joined the {{ app_name }} community.
+        {% endblocktrans %}
+        {% endif %}
+        {% endif %}
+        </p>
+
+        {% if new_question_count %}
+
+        {% declare %}
+            new_questions_link = html.hyperlink(app_url + reverse('questions') + '?sort=latest', _('new questions'), style=a_style)
+            user_questions = digest.get_for_user(recipient)
+            subscriptions_link = html.hyperlink(app_url + recipient.get_subscribed_url(), _('subscriptions'), style=a_style)
+        {% enddeclare %}
+
+        <p style="{{ p_style }}">
+            {% blocktrans %}
+            {{ new_question_count }} {{ new_questions_link }} were posted since our last update.
+            {% endblocktrans %}
+        </p>
+
+        {% if user_questions.interesting %}
+        <p style="{{ p_style }}">
+            {% trans "We think you might like the following questions:" %}
+        </p>
         <ul>
-        {% for nuser in new_users %}
-            <li><a href="{{ settings.APP_URL }}{{ nuser.get_profile_url }}">{{ nuser.username }}</a></li>        
+        {% for q in user_questions.interesting %}
+            <li>
+                <a style="{{ a_style }}" href="{{ app_url }}{{ q.get_absolute_url  }}">{{ q.title }}</a>
+            </li>
         {% endfor %}
         </ul>
-    {% endif %}
+        {% endif %}
 
-    {% if activity_in_subscriptions %}
-        <h3>
-        {% blocktrans with activity_in_subscriptions|length as question_count and activity_in_subscriptions|length|pluralize as question_count_pluralize %}
-            {{ question_count }} of your subscriptions have updates:
-        {% endblocktrans %}
-        </h3>
+        {% if user_questions.may_help %}
+        <p style="{{ p_style }}">
+            {% trans "These new questions didn't get many attention from the community, but we think you may be able to help:" %}
+        </p>
         <ul>
-        {% for record in activity_in_subscriptions %}
+        {% for q in user_questions.may_help %}
             <li>
-            {% trans "On question " %}<a href="{{ settings.APP_URL }}{{ record.question.get_absolute_url }}">{{ question_title }}" %}</a> -
-            {% if record.activity.answers %}
-                {% blocktrans with record.activity.answers|length as answer_count and record.activity.answers|length|pluralize as answer_count_pluralize %}
-                    {{ answer_count }} new answer{{ answer_count_pluralize }}
-                {% endblocktrans %},
-            {% endif %}
-            {% if record.activity.comments %}
-                {% blocktrans with record.activity.comments|length as comment_count and record.activity.comments|length|pluralize as comment_count_pluralize %}
-                    {{ comment_count }} new comment{{ comment_count_pluralize }}
-                {% endblocktrans %}
-                {% if own_comments_only %}
-                    {% trans "on your own post(s)" %}
-                {% endif %},
-            {% endif %}
-            {% if record.accepted %}
-                {% trans "an answer was accepted" %}
-            {% endif %}
+                <a style="{{ a_style }}" href="{{ app_url }}{{ q.get_absolute_url  }}">{{ q.title }}</a>
             </li>
         {% endfor %}
         </ul>
-    {% endif %}
 
-    {% if new_questions %}
-        <h3>
-        {% blocktrans with new_questions|length as question_count and new_questions|length|pluralize as question_count_pluralize%}
-            {{ question_count }} new question{{ question_count_pluralize }}
-        {% endblocktrans %}
-        {% if watched_tags_only %}
-            {% trans "matching your interesting tags" %}
-        {% endif %}
-        {% trans "posted :" %}
-        </h3>
+        {% if user_questions.subscriptions %}
+        <p style="{{ p_style }}">
+            {% blocktrans %}Meanwhile, some of your {{ subscriptions_link }} have new updates since you last visited them:{% endblocktrans %}
+        </p>
         <ul>
-        {% for question in new_questions %}
+        {% for q in user_questions.subscriptions %}
             <li>
-                <a href="{{ settings.APP_URL }}{{ question.get_absolute_url }}">{{ question.title }}</a> -
-                {% blocktrans with question.author.username as author_name and question.added_at|date:"D d M Y" as question_time %}
-                    Posted by {{ author_name }} in {{ question_time }}
-                {% endblocktrans %}
+                <a style="{{ a_style }}" href="{{ app_url }}{{ q.get_absolute_url  }}">{{ q.title }}</a>
             </li>
         {% endfor %}
         </ul>
-    {% endif %}
+        {% endif %}
+        
+        {% endif %}
+
+        {% endif %}
+
+    {% endhtmlcontent %}
+
+    {% textcontent notifications/base_text.html %}
 
-{% endblock %}
\ No newline at end of file
+    {% endtextcontent %}
+{% endemail %}
\ No newline at end of file