From: hernani Date: Mon, 28 Jun 2010 15:50:19 +0000 (+0000) Subject: Fixes (finaly) the email digest. Needs some improvements and some tweaks in the user... X-Git-Tag: live~671 X-Git-Url: https://git.openstreetmap.org./osqa.git/commitdiff_plain/7fb4da9ecca0993ce6d61750739690ab680cab30 Fixes (finaly) the email digest. Needs some improvements and some tweaks in the user controls. git-svn-id: http://svn.osqa.net/svnroot/osqa/trunk@463 0cfe37f9-358a-4d5e-be75-b63607b5c754 --- diff --git a/forum/management/commands/send_email_alerts.py b/forum/management/commands/send_email_alerts.py index fa9d442..4cdb2e1 100644 --- a/forum/management/commands/send_email_alerts.py +++ b/forum/management/commands/send_email_alerts.py @@ -1,160 +1,110 @@ -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 diff --git a/forum/skins/default/templates/notifications/digest.html b/forum/skins/default/templates/notifications/digest.html index 9a7ec04..99485e7 100644 --- a/forum/skins/default/templates/notifications/digest.html +++ b/forum/skins/default/templates/notifications/digest.html @@ -1,79 +1,96 @@ -{% extends "email_base.html" %} -{% load i18n %} -{% load humanize %} -{% load extra_tags %} - -{% block content %} -

{% trans "Hello" %} {{ user.username }},

- -

{% blocktrans with settings.APP_SHORT_NAME as app_title %} - This is the {{ digest_type }} activity digest for {{ app_title }} - {% endblocktrans %}

- - {% if new_users %} -

- {% 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 %} +

+ {% 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 %} -

+ {% else %} + {% blocktrans %} + {{ new_member_links }} have joined the {{ app_name }} community. + {% endblocktrans %} + {% endif %} + {% endif %} +

+ + {% 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 %} + +

+ {% blocktrans %} + {{ new_question_count }} {{ new_questions_link }} were posted since our last update. + {% endblocktrans %} +

+ + {% if user_questions.interesting %} +

+ {% trans "We think you might like the following questions:" %} +

- {% endif %} + {% endif %} - {% if activity_in_subscriptions %} -

- {% 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 %} -

+ {% if user_questions.may_help %} +

+ {% trans "These new questions didn't get many attention from the community, but we think you may be able to help:" %} +

- {% endif %} - {% if new_questions %} -

- {% 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 :" %} -

+ {% if user_questions.subscriptions %} +

+ {% blocktrans %}Meanwhile, some of your {{ subscriptions_link }} have new updates since you last visited them:{% endblocktrans %} +

- {% 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