]> git.openstreetmap.org Git - osqa.git/blob - forum/management/commands/send_email_alerts.py
deleting the test file
[osqa.git] / forum / management / commands / send_email_alerts.py
1 from django.core.management.base import NoArgsCommand
2 from django.db import connection
3 from django.db.models import Q, F
4 from forum.models import *
5 from forum import const 
6 from django.core.mail import EmailMessage
7 from django.utils.translation import ugettext as _
8 from django.utils.translation import ungettext
9 import datetime
10 from django.conf import settings
11 import logging
12 from forum.utils.odict import OrderedDict
13
14 class Command(NoArgsCommand):
15     def handle_noargs(self,**options):
16         try:
17             try:
18                 self.send_email_alerts()
19             except Exception, e:
20                 print e
21         finally:
22             connection.close()
23
24     def get_updated_questions_for_user(self,user):
25         q_sel = None 
26         q_ask = None 
27         q_ans = None 
28         q_all = None 
29         now = datetime.datetime.now()
30         Q_set1 = Question.objects.exclude(
31                                         last_activity_by=user,
32                                   ).exclude(
33                                         last_activity_at__lt=user.date_joined
34                                   ).filter(
35                                         Q(viewed__who=user,viewed__when__lt=F('last_activity_at')) | \
36                                         ~Q(viewed__who=user)
37                                   ).exclude(
38                                         deleted=True
39                                   ).exclude(
40                                         closed=True
41                                   )
42             
43         user_feeds = EmailFeedSetting.objects.filter(subscriber=user).exclude(frequency='n')
44         for feed in user_feeds:
45             cutoff_time = now - EmailFeedSetting.DELTA_TABLE[feed.frequency]
46             if feed.reported_at == None or feed.reported_at <= cutoff_time:
47                 Q_set = Q_set1.exclude(last_activity_at__gt=cutoff_time)#report these excluded later
48                 feed.reported_at = now
49                 feed.save()#may not actually report anything, depending on filters below
50                 if feed.feed_type == 'q_sel':
51                     q_sel = Q_set.filter(followed_by=user)
52                     q_sel.cutoff_time = cutoff_time #store cutoff time per query set
53                 elif feed.feed_type == 'q_ask':
54                     q_ask = Q_set.filter(author=user)
55                     q_ask.cutoff_time = cutoff_time
56                 elif feed.feed_type == 'q_ans':
57                     q_ans = Q_set.filter(answers__author=user)
58                     q_ans.cutoff_time = cutoff_time
59                 elif feed.feed_type == 'q_all':
60                     if user.tag_filter_setting == 'ignored':
61                         ignored_tags = Tag.objects.filter(user_selections__reason='bad',user_selections__user=user)
62                         q_all = Q_set.exclude( tags__in=ignored_tags )
63                     else:
64                         selected_tags = Tag.objects.filter(user_selections__reason='good',user_selections__user=user)
65                         q_all = Q_set.filter( tags__in=selected_tags )
66                     q_all.cutoff_time = cutoff_time
67         #build list in this order
68         q_list = OrderedDict()
69         def extend_question_list(src, dst):
70             """src is a query set with questions
71                or an empty list
72                 dst - is an ordered dictionary
73             """
74             if src is None:
75                 return #will not do anything if subscription of this type is not used
76             cutoff_time = src.cutoff_time
77             for q in src:
78                 if q in dst:
79                     if cutoff_time < dst[q]['cutoff_time']:
80                         dst[q]['cutoff_time'] = cutoff_time
81                 else:
82                     #initialise a questions metadata dictionary to use for email reporting
83                     dst[q] = {'cutoff_time':cutoff_time}
84
85         extend_question_list(q_sel, q_list)
86         extend_question_list(q_ask, q_list)
87         extend_question_list(q_ans, q_list)
88         extend_question_list(q_all, q_list)
89
90         ctype = ContentType.objects.get_for_model(Question)
91         EMAIL_UPDATE_ACTIVITY = const.TYPE_ACTIVITY_QUESTION_EMAIL_UPDATE_SENT
92         for q, meta_data in q_list.items():
93             #todo use Activity, but first start keeping more Activity records
94             #act = Activity.objects.filter(content_type=ctype, object_id=q.id)
95             #because currently activity is not fully recorded to through
96             #revision records to see what kind modifications were done on
97             #the questions and answers
98             try:
99                 update_info = Activity.objects.get(content_type=ctype, 
100                                                     object_id=q.id,
101                                                     activity_type=EMAIL_UPDATE_ACTIVITY)
102                 emailed_at = update_info.active_at
103             except Activity.DoesNotExist:
104                 update_info = Activity(user=user, content_object=q, activity_type=EMAIL_UPDATE_ACTIVITY)
105                 emailed_at = datetime.datetime(1970,1,1)#long time ago
106             except Activity.MultipleObjectsReturned:
107                 raise Exception('server error - multiple question email activities found per user-question pair')
108
109             q_rev = QuestionRevision.objects.filter(question=q,\
110                                                     revised_at__lt=cutoff_time,\
111                                                     revised_at__gt=emailed_at)
112             q_rev = q_rev.exclude(author=user)
113             meta_data['q_rev'] = len(q_rev)
114             if len(q_rev) > 0 and q.added_at == q_rev[0].revised_at:
115                 meta_data['q_rev'] = 0
116                 meta_data['new_q'] = True
117             else:
118                 meta_data['new_q'] = False
119                 
120             new_ans = Answer.objects.filter(question=q,\
121                                             added_at__lt=cutoff_time,\
122                                             added_at__gt=emailed_at)
123             new_ans = new_ans.exclude(author=user)
124             meta_data['new_ans'] = len(new_ans)
125             ans_rev = AnswerRevision.objects.filter(answer__question=q,\
126                                             revised_at__lt=cutoff_time,\
127                                             revised_at__gt=emailed_at)
128             ans_rev = ans_rev.exclude(author=user)
129             meta_data['ans_rev'] = len(ans_rev)
130             if len(q_rev) == 0 and len(new_ans) == 0 and len(ans_rev) == 0:
131                 meta_data['nothing_new'] = True
132             else:
133                 meta_data['nothing_new'] = False
134                 update_info.active_at = now
135                 update_info.save() #save question email update activity 
136         return q_list 
137
138     def __action_count(self,string,number,output):
139         if number > 0:
140             output.append(_(string) % {'num':number})
141
142     def send_email_alerts(self):
143
144         #todo: move this to template
145         for user in User.objects.all():
146             q_list = self.get_updated_questions_for_user(user)
147             num_q = 0
148             num_moot = 0
149             for meta_data in q_list.values():
150                 if meta_data['nothing_new'] == False:
151                     num_q += 1
152                 else:
153                     num_moot += 1
154             if num_q > 0:
155                 url_prefix = settings.APP_URL
156                 subject = _('email update message subject')
157                 print 'have %d updated questions for %s' % (num_q, user.username)
158                 text = ungettext('%(name)s, this is an update message header for a question', 
159                             '%(name)s, this is an update message header for %(num)d questions',num_q) \
160                                 % {'num':num_q, 'name':user.username}
161
162                 text += '<ul>'
163                 for q, meta_data in q_list.items():
164                     act_list = []
165                     if meta_data['nothing_new']:
166                         continue
167                     else:
168                         if meta_data['new_q']:
169                             act_list.append(_('new question'))
170                         self.__action_count('%(num)d rev', meta_data['q_rev'],act_list)
171                         self.__action_count('%(num)d ans', meta_data['new_ans'],act_list)
172                         self.__action_count('%(num)d ans rev',meta_data['ans_rev'],act_list)
173                         act_token = ', '.join(act_list)
174                         text += '<li><a href="%s?sort=latest">%s</a> <font color="#777777">(%s)</font></li>' \
175                                     % (url_prefix + q.get_absolute_url(), q.title, act_token)
176                 text += '</ul>'
177                 if num_moot > 0:
178                     text += '<p></p>'
179                     text += ungettext('There is also one question which was recently '\
180                                 +'updated but you might not have seen its latest version.',
181                             'There are also %(num)d more questions which were recently updated '\
182                             +'but you might not have seen their latest version.',num_moot) \
183                                 % {'num':num_moot,}
184                     text += _('Perhaps you could look up previously sent forum reminders in your mailbox.')
185                     text += '</p>'
186
187                 link = url_prefix + user.get_profile_url() + '?sort=email_subscriptions'
188                 text += _('go to %(link)s to change frequency of email updates or %(email)s administrator') \
189                                 % {'link':link, 'email':settings.ADMINS[0][1]}
190                 msg = EmailMessage(subject, text, settings.DEFAULT_FROM_EMAIL, [user.email])
191                 msg.content_subtype = 'html'
192                 msg.send()