def get_questions_needing_reminder(self, user=None, activity_type=None, recurrence_delay=None): """returns list of questions that need a reminder, corresponding the given ``activity_type`` ``user`` - is the user receiving the reminder ``recurrence_delay`` - interval between sending the reminders about the same question """ #todo: goes to thread from askbot.models import Activity #avoid circular import question_list = list() for question in self: try: activity = Activity.objects.get(user=user, question=question, activity_type=activity_type) now = datetime.datetime.now() if now < activity.active_at + recurrence_delay: continue except Activity.DoesNotExist: activity = Activity( user=user, question=question, activity_type=activity_type, content_object=question, ) activity.active_at = datetime.datetime.now() activity.save() question_list.append(question) return question_list
def get_questions_needing_reminder(self, user = None, activity_type = None, recurrence_delay = None): """returns list of questions that need a reminder, corresponding the given ``activity_type`` ``user`` - is the user receiving the reminder ``recurrence_delay`` - interval between sending the reminders about the same question """ from askbot.models import Activity#avoid circular import question_list = list() for question in self: try: activity = Activity.objects.get( user = user, question = question, activity_type = activity_type ) now = datetime.datetime.now() if now < activity.active_at + recurrence_delay: continue except Activity.DoesNotExist: activity = Activity( user = user, question = question, activity_type = activity_type, content_object = question, ) activity.active_at = datetime.datetime.now() activity.save() question_list.append(question) return question_list
def remember_last_moderator(user): act = get_last_mod_alert_activity() if act: act.content_object = user act.save() else: act = Activity(user=user, content_object=user, activity_type=const.TYPE_ACTIVITY_MODERATION_ALERT_SENT) act.save()
def remember_last_moderator(user): """Save the the `user` as the one that was notified last""" act = get_last_mod_alert_activity() if act: act.content_object = user act.save() else: act = Activity(user=user, content_object=user, activity_type=const.TYPE_ACTIVITY_MODERATION_ALERT_SENT) act.save()
def remember_last_moderator(user): act = get_last_mod_alert_activity() if act: act.content_object = user act.save() else: act = Activity( user=user, content_object=user, activity_type=const.TYPE_ACTIVITY_MODERATION_ALERT_SENT ) act.save()
def remember_last_moderator(user): """Save the the `user` as the one that was notified last""" act = get_last_mod_alert_activity() if act: act.content_object = user act.save() else: act = Activity( user=user, content_object=user, activity_type=const.TYPE_ACTIVITY_MODERATION_ALERT_SENT ) act.save()
def get_mock_context(self): from askbot.models import (Activity, Post, User) post_types = ('question', 'answer', 'comment') post = Post.objects.filter(post_type__in=post_types)[0] if post.is_question(): activity_type = const.TYPE_ACTIVITY_ASK_QUESTION elif post.is_answer(): activity_type = const.TYPE_ACTIVITY_ANSWER elif post.parent.is_question(): activity_type = const.TYPE_ACTIVITY_COMMENT_QUESTION else: activity_type = const.TYPE_ACTIVITY_COMMENT_ANSWER activity = Activity( user=post.author, content_object=post, activity_type=activity_type, question=post.get_origin_post()) return { 'post': post, 'from_user': post.author, 'to_user': User.objects.exclude(id=post.author_id)[0], 'update_activity': activity }
def get_mock_context_sample3(self): """question edit alert""" #get edited answer from django.db.models import Count from askbot.models import (Activity, Post, User) posts = Post.objects.annotate(edit_count=Count('revisions')).filter( post_type='question', edit_count__gt=1) try: post = posts[0] except IndexError: return None to_users = User.objects.exclude(id=post.author_id) if to_users.count() == 0: return None to_user = to_users[0] activity = Activity(user=post.author, content_object=post, activity_type=const.TYPE_ACTIVITY_UPDATE_QUESTION, question=post) return { 'post': post, 'from_user': post.author, 'to_user': to_user, 'update_activity': activity }
def get_mock_context_sample1(self): """New question alert""" from askbot.models import (Activity, Post, User) posts = Post.objects.filter(post_type='question') if posts.count() == 0: return None post = posts[0] to_users = User.objects.exclude(id=post.author_id) if to_users.count() == 0: return None to_user = to_users[0] activity = Activity(user=post.author, content_object=post, activity_type=const.TYPE_ACTIVITY_ASK_QUESTION, question=post) return { 'post': post, 'from_user': post.author, 'to_user': to_user, 'update_activity': activity }
def record_post_update( post = None, updated_by = None, newly_mentioned_users = None, timestamp = None, created = False ): """Called when a post is updated. Arguments: * ``newly_mentioned_users`` - users who are mentioned in the post for the first time * ``created`` - a boolean. True when ``post`` has just been created * remaining arguments are self - explanatory The method does two things: * records "red envelope" recipients of the post * sends email alerts to all subscribers to the post """ #todo: take into account created == True case (activity_type, update_object) = post.get_updated_activity_data(created) update_activity = Activity( user = updated_by, active_at = timestamp, content_object = post, activity_type = activity_type, question = post.get_origin_post() ) update_activity.save() #what users are included depends on the post type #for example for question - all Q&A contributors #are included, for comments only authors of comments and parent #post are included recipients = post.get_response_receivers( exclude_list = [updated_by, ] ) update_activity.add_recipients(recipients) assert(updated_by not in recipients) for user in set(recipients) | set(newly_mentioned_users): user.increment_response_count() user.save() #todo: weird thing is that only comments need the recipients #todo: debug these calls and then uncomment in the repo #argument to this call notification_subscribers = post.get_instant_notification_subscribers( potential_subscribers = recipients, mentioned_users = newly_mentioned_users, exclude_list = [updated_by, ] ) #todo: fix this temporary spam protection plug if created: if not (updated_by.is_administrator() or updated_by.is_moderator()): if updated_by.reputation < 15: notification_subscribers = \ [u for u in notification_subscribers if u.is_administrator()] send_instant_notifications_about_activity_in_post( update_activity = update_activity, post = post, recipients = notification_subscribers, )
def get_updated_questions_for_user(self, user): """ retreive relevant question updates for the user according to their subscriptions and recorded question views """ user_feeds = EmailFeedSetting.objects.filter( subscriber=user ).exclude(frequency__in=('n', 'i')) should_proceed = False for feed in user_feeds: if feed.should_send_now() == True: should_proceed = True break #shortcircuit - if there is no ripe feed to work on for this user if should_proceed == False: return {} #these are placeholders for separate query sets per question group #there are four groups - one for each EmailFeedSetting.feed_type #and each group has subtypes A and B #that's because of the strange thing commented below #see note on Q and F objects marked with todo tag q_sel_A = None q_sel_B = None q_ask_A = None q_ask_B = None q_ans_A = None q_ans_B = None q_all_A = None q_all_B = None #base question query set for this user #basic things - not deleted, not closed, not too old #not last edited by the same user base_qs = Post.objects.get_questions().exclude( thread__last_activity_by=user ).exclude( thread__last_activity_at__lt=user.date_joined#exclude old stuff ).exclude( deleted=True ).exclude( thread__closed=True ).order_by('-thread__last_activity_at') if askbot_settings.CONTENT_MODERATION_MODE == 'premoderation': base_qs = base_qs.filter(approved = True) #todo: for some reason filter on did not work as expected ~Q(viewed__who=user) | # Q(viewed__who=user,viewed__when__lt=F('thread__last_activity_at')) #returns way more questions than you might think it should #so because of that I've created separate query sets Q_set2 and Q_set3 #plus two separate queries run faster! #build two two queries based #questions that are not seen by the user at all not_seen_qs = base_qs.filter(~Q(viewed__who=user)) #questions that were seen, but before last modification seen_before_last_mod_qs = base_qs.filter( Q(viewed__who=user, viewed__when__lt=F('thread__last_activity_at')) ) #shorten variables for convenience Q_set_A = not_seen_qs Q_set_B = seen_before_last_mod_qs if askbot.is_multilingual(): languages = user.languages.split() else: languages = None for feed in user_feeds: if feed.feed_type == 'm_and_c': #alerts on mentions and comments are processed separately #because comments to questions do not trigger change of last_updated #this may be changed in the future though, see #http://askbot.org/en/question/96/ continue #each group of updates represented by the corresponding #query set has it's own cutoff time #that cutoff time is computed for each user individually #and stored as a parameter "cutoff_time" #we won't send email for a given question if an email has been #sent after that cutoff_time if feed.should_send_now(): if DEBUG_THIS_COMMAND == False: feed.mark_reported_now() cutoff_time = feed.get_previous_report_cutoff_time() if feed.feed_type == 'q_sel': q_sel_A = Q_set_A.filter(thread__followed_by=user) q_sel_A.cutoff_time = cutoff_time #store cutoff time per query set q_sel_B = Q_set_B.filter(thread__followed_by=user) q_sel_B.cutoff_time = cutoff_time #store cutoff time per query set elif feed.feed_type == 'q_ask': q_ask_A = Q_set_A.filter(author=user) q_ask_A.cutoff_time = cutoff_time q_ask_B = Q_set_B.filter(author=user) q_ask_B.cutoff_time = cutoff_time elif feed.feed_type == 'q_ans': q_ans_A = Q_set_A.filter(thread__posts__author=user, thread__posts__post_type='answer') q_ans_A = q_ans_A[:askbot_settings.MAX_ALERTS_PER_EMAIL] q_ans_A.cutoff_time = cutoff_time q_ans_B = Q_set_B.filter(thread__posts__author=user, thread__posts__post_type='answer') q_ans_B = q_ans_B[:askbot_settings.MAX_ALERTS_PER_EMAIL] q_ans_B.cutoff_time = cutoff_time elif feed.feed_type == 'q_all': q_all_A = user.get_tag_filtered_questions(Q_set_A) q_all_B = user.get_tag_filtered_questions(Q_set_B) q_all_A = q_all_A[:askbot_settings.MAX_ALERTS_PER_EMAIL] q_all_B = q_all_B[:askbot_settings.MAX_ALERTS_PER_EMAIL] q_all_A.cutoff_time = cutoff_time q_all_B.cutoff_time = cutoff_time #build ordered list questions for the email report q_list = SortedDict() #todo: refactor q_list into a separate class? extend_question_list(q_sel_A, q_list, languages=languages) extend_question_list(q_sel_B, q_list, languages=languages) #build list of comment and mention responses here #it is separate because posts are not marked as changed #when people add comments #mention responses could be collected in the loop above, but #it is inconvenient, because feed_type m_and_c bundles the two #also we collect metadata for these here try: feed = user_feeds.get(feed_type='m_and_c') if feed.should_send_now(): cutoff_time = feed.get_previous_report_cutoff_time() comments = Post.objects.get_comments().filter( added_at__lt=cutoff_time ).exclude(author=user).select_related('parent') q_commented = list() for c in comments: post = c.parent if post.author_id != user.pk: continue #skip is post was seen by the user after #the comment posting time q_commented.append(post.get_origin_post()) extend_question_list( q_commented, q_list, cutoff_time=cutoff_time, add_comment=True, languages=languages ) mentions = Activity.objects.get_mentions( mentioned_at__lt=cutoff_time, mentioned_whom=user ) #print 'have %d mentions' % len(mentions) #MM = Activity.objects.filter(activity_type = const.TYPE_ACTIVITY_MENTION) #print 'have %d total mentions' % len(MM) #for m in MM: # print m mention_posts = get_all_origin_posts(mentions) q_mentions_id = [q.id for q in mention_posts] q_mentions_A = Q_set_A.filter(id__in = q_mentions_id) q_mentions_A.cutoff_time = cutoff_time extend_question_list( q_mentions_A, q_list, add_mention=True, languages=languages ) q_mentions_B = Q_set_B.filter(id__in = q_mentions_id) q_mentions_B.cutoff_time = cutoff_time extend_question_list( q_mentions_B, q_list, add_mention=True, languages=languages ) except EmailFeedSetting.DoesNotExist: pass if user.email_tag_filter_strategy == const.INCLUDE_INTERESTING: extend_question_list(q_all_A, q_list, languages=languages) extend_question_list(q_all_B, q_list, languages=languages) extend_question_list(q_ask_A, q_list, limit=True, languages=languages) extend_question_list(q_ask_B, q_list, limit=True, languages=languages) extend_question_list(q_ans_A, q_list, limit=True, languages=languages) extend_question_list(q_ans_B, q_list, limit=True, languages=languages) if user.email_tag_filter_strategy == const.EXCLUDE_IGNORED: extend_question_list(q_all_A, q_list, limit=True, languages=languages) extend_question_list(q_all_B, q_list, limit=True, languages=languages) ctype = ContentType.objects.get_for_model(Post) EMAIL_UPDATE_ACTIVITY = const.TYPE_ACTIVITY_EMAIL_UPDATE_SENT #up to this point we still don't know if emails about #collected questions were sent recently #the next loop examines activity record and decides #for each question, whether it needs to be included or not #into the report for q, meta_data in q_list.items(): #this loop edits meta_data for each question #so that user will receive counts on new edits new answers, etc #and marks questions that need to be skipped #because an email about them was sent recently enough #also it keeps a record of latest email activity per question per user try: #todo: is it possible to use content_object here, instead of #content type and object_id pair? update_info = Activity.objects.get( user=user, 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 if django_settings.USE_TZ: emailed_at = timezone.make_aware(emailed_at, timezone.utc) except Activity.MultipleObjectsReturned: raise Exception( 'server error - multiple question email activities ' 'found per user-question pair' ) cutoff_time = meta_data['cutoff_time']#cutoff time for the question #skip question if we need to wait longer because #the delay before the next email has not yet elapsed #or if last email was sent after the most recent modification if emailed_at > cutoff_time or emailed_at > q.thread.last_activity_at: meta_data['skip'] = True continue #collect info on all sorts of news that happened after #the most recent emailing to the user about this question q_rev = q.revisions.filter(revised_at__gt=emailed_at) q_rev = q_rev.exclude(author=user) #now update all sorts of metadata per question 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: meta_data['new_q'] = False new_ans = Post.objects.get_answers(user).filter( thread=q.thread, added_at__gt=emailed_at, deleted=False, ) new_ans = new_ans.exclude(author=user) meta_data['new_ans'] = len(new_ans) ans_ids = Post.objects.get_answers(user).filter( thread=q.thread, added_at__gt=emailed_at, deleted=False, ).values_list('id', flat=True) ans_rev = PostRevision.objects.filter(post__id__in = ans_ids) ans_rev = ans_rev.exclude(author=user).distinct() meta_data['ans_rev'] = len(ans_rev) comments = meta_data.get('comments', 0) mentions = meta_data.get('mentions', 0) #print meta_data #finally skip question if there are no news indeed if len(q_rev) + len(new_ans) + len(ans_rev) + comments + mentions == 0: meta_data['skip'] = True #print 'skipping' else: meta_data['skip'] = False #print 'not skipping' update_info.active_at = timezone.now() if DEBUG_THIS_COMMAND == False: update_info.save() #save question email update activity #q_list is actually an ordered dictionary #print 'user %s gets %d' % (user.username, len(q_list.keys())) #todo: sort question list by update time return q_list
def record_post_update( post = None, updated_by = None, newly_mentioned_users = None, timestamp = None, created = False ): """Called when a post is updated. Arguments: * ``newly_mentioned_users`` - users who are mentioned in the post for the first time * ``created`` - a boolean. True when ``post`` has just been created * remaining arguments are self - explanatory The method does two things: * records "red envelope" recipients of the post * sends email alerts to all subscribers to the post """ start_time = time.time() #todo: take into account created == True case (activity_type, update_object) = post.get_updated_activity_data(created) update_activity = Activity( user = updated_by, active_at = timestamp, content_object = post, activity_type = activity_type, question = post.get_origin_post() ) update_activity.save() #what users are included depends on the post type #for example for question - all Q&A contributors #are included, for comments only authors of comments and parent #post are included recipients = post.get_response_receivers( exclude_list = [updated_by, ] ) update_activity.add_recipients(recipients) assert(updated_by not in recipients) for user in (set(recipients) | set(newly_mentioned_users)): user.update_response_counts() #todo: weird thing is that only comments need the recipients #todo: debug these calls and then uncomment in the repo #argument to this call pre_notif_time = time.time() notification_subscribers = post.get_instant_notification_subscribers( potential_subscribers = recipients, mentioned_users = newly_mentioned_users, exclude_list = [updated_by, ] ) #todo: fix this temporary spam protection plug if False: if not (updated_by.is_administrator() or updated_by.is_moderator()): if updated_by.reputation < 15: notification_subscribers = \ [u for u in notification_subscribers if u.is_administrator()] #Updater always gets an email notification_subscribers.append(updated_by) pre_email_time = time.time() send_instant_notifications_about_activity_in_post( update_activity = update_activity, post = post, recipients = notification_subscribers, ) debug_str = "\nTitle: %s" % post.get_origin_post().title debug_str += "\nEmailed%s\n" % get_subs_email(notification_subscribers) #debug_str += " Pre-notif Time: %8.3f\n" % float(pre_notif_time - start_time) #debug_str += " Sub Search Time: %8.3f\n" % float(pre_email_time - pre_notif_time) #debug_str += " Email Time: %8.3f\n" % float(time.time() - pre_email_time) debug_str += "Total Elapsed Time: %8.3f" % float(time.time() - start_time) logging.critical(debug_str)
def record_post_update(post=None, updated_by=None, newly_mentioned_users=None, timestamp=None, created=False, diff=None): """Called when a post is updated. Arguments: * ``newly_mentioned_users`` - users who are mentioned in the post for the first time * ``created`` - a boolean. True when ``post`` has just been created * remaining arguments are self - explanatory The method does two things: * records "red envelope" recipients of the post * sends email alerts to all subscribers to the post """ #todo: take into account created == True case (activity_type, update_object) = post.get_updated_activity_data(created) if post.post_type != 'comment': #summary = post.get_latest_revision().summary summary = diff else: #it's just a comment! summary = post.comment update_activity = Activity(user=updated_by, active_at=timestamp, content_object=post, activity_type=activity_type, question=post.get_origin_post(), summary=summary) update_activity.save() #what users are included depends on the post type #for example for question - all Q&A contributors #are included, for comments only authors of comments and parent #post are included recipients = post.get_response_receivers(exclude_list=[ updated_by, ]) update_activity.add_recipients(recipients) #create new mentions for u in newly_mentioned_users: #todo: a hack - some users will not have record of a mention #may need to fix this in the future. Added this so that #recipients of the response who are mentioned as well would #not get two notifications in the inbox for the same post if u in recipients: continue Activity.objects.create_new_mention(mentioned_whom=u, mentioned_in=post, mentioned_by=updated_by, mentioned_at=timestamp) assert (updated_by not in recipients) for user in (set(recipients) | set(newly_mentioned_users)): user.update_response_counts() #todo: weird thing is that only comments need the recipients #todo: debug these calls and then uncomment in the repo #argument to this call notification_subscribers = post.get_instant_notification_subscribers( potential_subscribers=recipients, mentioned_users=newly_mentioned_users, exclude_list=[ updated_by, ]) #todo: fix this temporary spam protection plug if created: if not (updated_by.is_administrator() or updated_by.is_moderator()): if updated_by.reputation < 15: notification_subscribers = \ [u for u in notification_subscribers if u.is_administrator()] send_instant_notifications_about_activity_in_post( update_activity=update_activity, post=post, recipients=notification_subscribers, )
def get_updated_questions_for_user(self, user): """ retreive relevant question updates for the user according to their subscriptions and recorded question views """ user_feeds = EmailFeedSetting.objects.filter(subscriber=user).exclude( frequency__in=('n', 'i')) should_proceed = False for feed in user_feeds: if feed.should_send_now() == True: should_proceed = True break #shortcircuit - if there is no ripe feed to work on for this user if should_proceed == False: return {} #these are placeholders for separate query sets per question group #there are four groups - one for each EmailFeedSetting.feed_type #and each group has subtypes A and B #that's because of the strange thing commented below #see note on Q and F objects marked with todo tag q_sel_A = None q_sel_B = None q_ask_A = None q_ask_B = None q_ans_A = None q_ans_B = None q_all_A = None q_all_B = None #base question query set for this user #basic things - not deleted, not closed, not too old #not last edited by the same user base_qs = Post.objects.get_questions( ).exclude(thread__last_activity_by=user).exclude( thread__last_activity_at__lt=user.date_joined #exclude old stuff ).exclude(deleted=True).exclude( thread__closed=True).order_by('-thread__last_activity_at') if askbot_settings.ENABLE_CONTENT_MODERATION: base_qs = base_qs.filter(approved=True) #todo: for some reason filter on did not work as expected ~Q(viewed__who=user) | # Q(viewed__who=user,viewed__when__lt=F('thread__last_activity_at')) #returns way more questions than you might think it should #so because of that I've created separate query sets Q_set2 and Q_set3 #plus two separate queries run faster! #build two two queries based #questions that are not seen by the user at all not_seen_qs = base_qs.filter(~Q(viewed__who=user)) #questions that were seen, but before last modification seen_before_last_mod_qs = base_qs.filter( Q(viewed__who=user, viewed__when__lt=F('thread__last_activity_at'))) #shorten variables for convenience Q_set_A = not_seen_qs Q_set_B = seen_before_last_mod_qs if getattr(django_settings, 'ASKBOT_MULTILINGUAL', False): languages = user.languages.split() else: languages = None for feed in user_feeds: if feed.feed_type == 'm_and_c': #alerts on mentions and comments are processed separately #because comments to questions do not trigger change of last_updated #this may be changed in the future though, see #http://askbot.org/en/question/96/ continue #each group of updates represented by the corresponding #query set has it's own cutoff time #that cutoff time is computed for each user individually #and stored as a parameter "cutoff_time" #we won't send email for a given question if an email has been #sent after that cutoff_time if feed.should_send_now(): if DEBUG_THIS_COMMAND == False: feed.mark_reported_now() cutoff_time = feed.get_previous_report_cutoff_time() if feed.feed_type == 'q_sel': q_sel_A = Q_set_A.filter(thread__followed_by=user) q_sel_A.cutoff_time = cutoff_time #store cutoff time per query set q_sel_B = Q_set_B.filter(thread__followed_by=user) q_sel_B.cutoff_time = cutoff_time #store cutoff time per query set elif feed.feed_type == 'q_ask': q_ask_A = Q_set_A.filter(author=user) q_ask_A.cutoff_time = cutoff_time q_ask_B = Q_set_B.filter(author=user) q_ask_B.cutoff_time = cutoff_time elif feed.feed_type == 'q_ans': q_ans_A = Q_set_A.filter(thread__posts__author=user, thread__posts__post_type='answer') q_ans_A = q_ans_A[:askbot_settings.MAX_ALERTS_PER_EMAIL] q_ans_A.cutoff_time = cutoff_time q_ans_B = Q_set_B.filter(thread__posts__author=user, thread__posts__post_type='answer') q_ans_B = q_ans_B[:askbot_settings.MAX_ALERTS_PER_EMAIL] q_ans_B.cutoff_time = cutoff_time elif feed.feed_type == 'q_all': q_all_A = user.get_tag_filtered_questions(Q_set_A) q_all_B = user.get_tag_filtered_questions(Q_set_B) q_all_A = q_all_A[:askbot_settings.MAX_ALERTS_PER_EMAIL] q_all_B = q_all_B[:askbot_settings.MAX_ALERTS_PER_EMAIL] q_all_A.cutoff_time = cutoff_time q_all_B.cutoff_time = cutoff_time #build ordered list questions for the email report q_list = SortedDict() #todo: refactor q_list into a separate class? extend_question_list(q_sel_A, q_list, languages=languages) extend_question_list(q_sel_B, q_list, languages=languages) #build list of comment and mention responses here #it is separate because posts are not marked as changed #when people add comments #mention responses could be collected in the loop above, but #it is inconvenient, because feed_type m_and_c bundles the two #also we collect metadata for these here try: feed = user_feeds.get(feed_type='m_and_c') if feed.should_send_now(): cutoff_time = feed.get_previous_report_cutoff_time() comments = Post.objects.get_comments().filter( added_at__lt=cutoff_time, ).exclude(author=user) q_commented = list() for c in comments: post = c.parent if post.author != user: continue #skip is post was seen by the user after #the comment posting time q_commented.append(post.get_origin_post()) extend_question_list(q_commented, q_list, cutoff_time=cutoff_time, add_comment=True, languages=languages) mentions = Activity.objects.get_mentions( mentioned_at__lt=cutoff_time, mentioned_whom=user) #print 'have %d mentions' % len(mentions) #MM = Activity.objects.filter(activity_type = const.TYPE_ACTIVITY_MENTION) #print 'have %d total mentions' % len(MM) #for m in MM: # print m mention_posts = get_all_origin_posts(mentions) q_mentions_id = [q.id for q in mention_posts] q_mentions_A = Q_set_A.filter(id__in=q_mentions_id) q_mentions_A.cutoff_time = cutoff_time extend_question_list(q_mentions_A, q_list, add_mention=True, languages=languages) q_mentions_B = Q_set_B.filter(id__in=q_mentions_id) q_mentions_B.cutoff_time = cutoff_time extend_question_list(q_mentions_B, q_list, add_mention=True, languages=languages) except EmailFeedSetting.DoesNotExist: pass if user.email_tag_filter_strategy == const.INCLUDE_INTERESTING: extend_question_list(q_all_A, q_list, languages=languages) extend_question_list(q_all_B, q_list, languages=languages) extend_question_list(q_ask_A, q_list, limit=True, languages=languages) extend_question_list(q_ask_B, q_list, limit=True, languages=languages) extend_question_list(q_ans_A, q_list, limit=True, languages=languages) extend_question_list(q_ans_B, q_list, limit=True, languages=languages) if user.email_tag_filter_strategy == const.EXCLUDE_IGNORED: extend_question_list(q_all_A, q_list, limit=True, languages=languages) extend_question_list(q_all_B, q_list, limit=True, languages=languages) ctype = ContentType.objects.get_for_model(Post) EMAIL_UPDATE_ACTIVITY = const.TYPE_ACTIVITY_EMAIL_UPDATE_SENT #up to this point we still don't know if emails about #collected questions were sent recently #the next loop examines activity record and decides #for each question, whether it needs to be included or not #into the report for q, meta_data in q_list.items(): #this loop edits meta_data for each question #so that user will receive counts on new edits new answers, etc #and marks questions that need to be skipped #because an email about them was sent recently enough #also it keeps a record of latest email activity per question per user try: #todo: is it possible to use content_object here, instead of #content type and object_id pair? update_info = Activity.objects.get( user=user, 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') cutoff_time = meta_data[ 'cutoff_time'] #cutoff time for the question #skip question if we need to wait longer because #the delay before the next email has not yet elapsed #or if last email was sent after the most recent modification if emailed_at > cutoff_time or emailed_at > q.thread.last_activity_at: meta_data['skip'] = True continue #collect info on all sorts of news that happened after #the most recent emailing to the user about this question q_rev = q.revisions.filter(revised_at__gt=emailed_at) q_rev = q_rev.exclude(author=user) #now update all sorts of metadata per question 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: meta_data['new_q'] = False new_ans = Post.objects.get_answers(user).filter( thread=q.thread, added_at__gt=emailed_at, deleted=False, ) new_ans = new_ans.exclude(author=user) meta_data['new_ans'] = len(new_ans) ans_ids = Post.objects.get_answers(user).filter( thread=q.thread, added_at__gt=emailed_at, deleted=False, ).values_list('id', flat=True) ans_rev = PostRevision.objects.filter(post__id__in=ans_ids) ans_rev = ans_rev.exclude(author=user).distinct() meta_data['ans_rev'] = len(ans_rev) comments = meta_data.get('comments', 0) mentions = meta_data.get('mentions', 0) #print meta_data #finally skip question if there are no news indeed if len(q_rev) + len(new_ans) + len( ans_rev) + comments + mentions == 0: meta_data['skip'] = True #print 'skipping' else: meta_data['skip'] = False #print 'not skipping' update_info.active_at = datetime.datetime.now() if DEBUG_THIS_COMMAND == False: update_info.save() #save question email update activity #q_list is actually an ordered dictionary #print 'user %s gets %d' % (user.username, len(q_list.keys())) #todo: sort question list by update time return q_list
return Action.objects.filter(thread__pk__in=thread_pks) User.add_to_class('ext_noattr', UserExtension()) #remove old attribute to prevent duplicates in the Activity fields. #1- search 'activity_type' into the Activity local_fields and save the index local_fields = Activity._meta.local_fields for field in local_fields: if field.name == 'activity_type': index = local_fields.index(field) break #2- remove the found element from the local_fields list. Activity._meta.local_fields.pop(index) #3- add to class Activity.add_to_class( 'activity_type', models.SmallIntegerField(choices=ae_const.TYPE_ACTIVITY_CHOICES)) #4- move the added field to the last index to the previous one field = local_fields.pop() local_fields.insert(index, field) #5- clean cache(s) #del Activity._meta._field_cache #del Activity._meta._field_name_cache #print("\n\nActivity._meta.local_fields: %s\n\n" % Activity._meta.local_fields)
thread_pks = Vote.objects.filter(pk__in=self.votes.all()).declareds().values_list('voted_post__thread__pk', flat=True).distinct() return Action.objects.filter(thread__pk__in=thread_pks) User.add_to_class('ext_noattr', UserExtension()) #remove old attribute to prevent duplicates in the Activity fields. #1- search 'activity_type' into the Activity local_fields and save the index local_fields = Activity._meta.local_fields for field in local_fields: if field.name == 'activity_type': index = local_fields.index(field) break #2- remove the found element from the local_fields list. Activity._meta.local_fields.pop(index) #3- add to class Activity.add_to_class('activity_type', models.SmallIntegerField(choices = ae_const.TYPE_ACTIVITY_CHOICES)) #4- move the added field to the last index to the previous one field = local_fields.pop() local_fields.insert(index, field) #5- clean cache(s) #del Activity._meta._field_cache #del Activity._meta._field_name_cache #print("\n\nActivity._meta.local_fields: %s\n\n" % Activity._meta.local_fields)
def record_post_update( post = None, updated_by = None, newly_mentioned_users = None, timestamp = None, created = False ): """Called when a post is updated. Arguments: * ``newly_mentioned_users`` - users who are mentioned in the post for the first time * ``created`` - a boolean. True when ``post`` has just been created * remaining arguments are self - explanatory The method does two things: * records "red envelope" recipients of the post * sends email alerts to all subscribers to the post """ #todo: take into account created == True case (activity_type, update_object) = post.get_updated_activity_data(created) update_activity = Activity( user = updated_by, active_at = timestamp, content_object = post, activity_type = activity_type, question = post.get_origin_post() ) update_activity.save() #what users are included depends on the post type #for example for question - all Q&A contributors #are included, for comments only authors of comments and parent #post are included recipients = post.get_response_receivers( exclude_list = [updated_by, ] ) update_activity.add_recipients(recipients) assert(updated_by not in recipients) for user in (set(recipients) | set(newly_mentioned_users)): user.update_response_counts() #todo: weird thing is that only comments need the recipients #todo: debug these calls and then uncomment in the repo #argument to this call notification_subscribers = post.get_instant_notification_subscribers( potential_subscribers = recipients, mentioned_users = newly_mentioned_users, exclude_list = [updated_by, ] ) #todo: fix this temporary spam protection plug if created: if not (updated_by.is_administrator() or updated_by.is_moderator()): if updated_by.reputation < 15: notification_subscribers = \ [u for u in notification_subscribers if u.is_administrator()] send_instant_notifications_about_activity_in_post( update_activity = update_activity, post = post, recipients = notification_subscribers, )
def record_post_update( post = None, updated_by = None, newly_mentioned_users = None, timestamp = None, created = False, diff = None ): """Called when a post is updated. Arguments: * ``newly_mentioned_users`` - users who are mentioned in the post for the first time * ``created`` - a boolean. True when ``post`` has just been created * remaining arguments are self - explanatory The method does two things: * records "red envelope" recipients of the post * sends email alerts to all subscribers to the post """ #todo: take into account created == True case (activity_type, update_object) = post.get_updated_activity_data(created) if post.is_comment(): #it's just a comment! summary = post.text else: #summary = post.get_latest_revision().summary summary = diff update_activity = Activity( user = updated_by, active_at = timestamp, content_object = post, activity_type = activity_type, question = post.get_origin_post(), summary = summary ) update_activity.save() #what users are included depends on the post type #for example for question - all Q&A contributors #are included, for comments only authors of comments and parent #post are included recipients = post.get_response_receivers( exclude_list = [updated_by, ] ) update_activity.add_recipients(recipients) #create new mentions for u in newly_mentioned_users: #todo: a hack - some users will not have record of a mention #may need to fix this in the future. Added this so that #recipients of the response who are mentioned as well would #not get two notifications in the inbox for the same post if u in recipients: continue Activity.objects.create_new_mention( mentioned_whom = u, mentioned_in = post, mentioned_by = updated_by, mentioned_at = timestamp ) assert(updated_by not in recipients) for user in (set(recipients) | set(newly_mentioned_users)): user.update_response_counts() #todo: weird thing is that only comments need the recipients #todo: debug these calls and then uncomment in the repo #argument to this call notification_subscribers = post.get_instant_notification_subscribers( potential_subscribers = recipients, mentioned_users = newly_mentioned_users, exclude_list = [updated_by, ] ) #todo: fix this temporary spam protection plug if created: if not (updated_by.is_administrator() or updated_by.is_moderator()): if updated_by.reputation < 15: notification_subscribers = \ [u for u in notification_subscribers if u.is_administrator()] send_instant_notifications_about_activity_in_post( update_activity = update_activity, post = post, recipients = notification_subscribers, )
def record_post_update_task( post_id, post_content_type_id, newly_mentioned_user_id_list = None, updated_by_id = None, timestamp = None, created = False, ): updated_by = User.objects.get(id = updated_by_id) post_content_type = ContentType.objects.get(id = post_content_type_id) post = post_content_type.get_object_for_this_type(id = post_id) #todo: take into account created == True case (activity_type, update_object) = post.get_updated_activity_data(created) update_activity = Activity( user = updated_by, active_at = timestamp, content_object = post, activity_type = activity_type, question = post.get_origin_post() ) update_activity.save() #what users are included depends on the post type #for example for question - all Q&A contributors #are included, for comments only authors of comments and parent #post are included recipients = post.get_response_receivers( exclude_list = [updated_by, ] ) update_activity.add_recipients(recipients) assert(updated_by not in recipients) newly_mentioned_users = User.objects.filter( id__in = newly_mentioned_user_id_list ) for user in set(recipients) | set(newly_mentioned_users): user.increment_response_count() user.save() #todo: weird thing is that only comments need the recipients #todo: debug these calls and then uncomment in the repo #argument to this call notification_subscribers = post.get_instant_notification_subscribers( potential_subscribers = recipients, mentioned_users = newly_mentioned_users, exclude_list = [updated_by, ] ) #todo: fix this temporary spam protection plug if created: if not (updated_by.is_administrator() or updated_by.is_moderator()): if updated_by.reputation < 15: notification_subscribers = \ [u for u in notification_subscribers if u.is_administrator()] send_instant_notifications_about_activity_in_post( update_activity = update_activity, post = post, recipients = notification_subscribers, )