Example #1
0
    def get_updated_questions_for_user(self,user):

        #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

        now = datetime.datetime.now()
        #Q_set1 - base questionquery set for this user
        Q_set1 = Question.objects.exclude(
                                last_activity_by=user
                            ).exclude(
                                last_activity_at__lt=user.date_joined#exclude old stuff
                            ).exclude(
                                deleted=True
                            ).exclude(
                                closed=True
                            ).order_by('-last_activity_at')
        #todo: for some reason filter on did not work as expected ~Q(viewed__who=user) | 
        #      Q(viewed__who=user,viewed__when__lt=F('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!

        #questions that are not seen by the user
        Q_set2 = Q_set1.filter(~Q(viewed__who=user))
        #questions seen before the last modification
        Q_set3 = Q_set1.filter(Q(viewed__who=user,viewed__when__lt=F('last_activity_at')))

        #todo may shortcirquit here is len(user_feeds) == 0
        user_feeds = EmailFeedSetting.objects.filter(subscriber=user).exclude(frequency='n')
        if len(user_feeds) == 0:
            return {};#short cirquit
        for feed in user_feeds:
            #each group of updates has it's own cutoff time
            #to be saved as a new parameter for each query set
            #won't send email for a given question if it has been done
            #after the cutoff_time
            cutoff_time = now - EmailFeedSetting.DELTA_TABLE[feed.frequency]
            if feed.reported_at == None or feed.reported_at <= cutoff_time:
                Q_set_A = Q_set2#.exclude(last_activity_at__gt=cutoff_time)#report these excluded later
                Q_set_B = Q_set3#.exclude(last_activity_at__gt=cutoff_time)
                feed.reported_at = now
                feed.save()#may not actually report anything, depending on filters below
                if feed.feed_type == 'q_sel':
                    q_sel_A = Q_set_A.filter(followed_by=user)
                    q_sel_A.cutoff_time = cutoff_time #store cutoff time per query set
                    q_sel_B = Q_set_B.filter(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(answers__author=user)[:settings.MAX_ALERTS_PER_EMAIL]
                    q_ans_A.cutoff_time = cutoff_time
                    q_ans_B = Q_set_B.filter(answers__author=user)[:settings.MAX_ALERTS_PER_EMAIL]
                    q_ans_B.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_A = Q_set_A.exclude( tags__in=ignored_tags )[:settings.MAX_ALERTS_PER_EMAIL]
                        q_all_B = Q_set_B.exclude( tags__in=ignored_tags )[:settings.MAX_ALERTS_PER_EMAIL]
                    else:
                        selected_tags = Tag.objects.filter(user_selections__reason='good', \
                                                            user_selections__user=user)
                        q_all_A = Q_set_A.filter( tags__in=selected_tags )
                        q_all_B = Q_set_B.filter( tags__in=selected_tags )
                    q_all_A.cutoff_time = cutoff_time
                    q_all_B.cutoff_time = cutoff_time
        #build list in this order
        q_list = OrderedDict()

        extend_question_list(q_sel_A, q_list)
        extend_question_list(q_sel_B, q_list)

        if user.tag_filter_setting == 'interesting':
            extend_question_list(q_all_A, q_list)
            extend_question_list(q_all_B, q_list)

        extend_question_list(q_ask_A, q_list, limit=True)
        extend_question_list(q_ask_B, q_list, limit=True)

        extend_question_list(q_ans_A, q_list, limit=True)
        extend_question_list(q_ans_B, q_list, limit=True)

        if user.tag_filter_setting == 'ignored':
            extend_question_list(q_all_A, q_list, limit=True)
            extend_question_list(q_all_B, q_list, limit=True)

        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():
            #this loop edits meta_data for each question
            #so that user will receive counts on new edits new answers, etc
            #maybe not so important actually??

            #keeps email activity per question per user
            try:
                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

            #wait some more time before emailing about this question
            if emailed_at > cutoff_time:
                #here we are maybe losing opportunity to record the finding
                #of yet unseen version of a question
                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 = QuestionRevision.objects.filter(question=q,\
                                                    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 = Answer.objects.filter(question=q,\
                                            added_at__gt=emailed_at)
            new_ans = new_ans.exclude(author=user)
            meta_data['new_ans'] = len(new_ans)
            ans_rev = AnswerRevision.objects.filter(answer__question=q,\
                                            revised_at__gt=emailed_at)
            ans_rev = ans_rev.exclude(author=user)
            meta_data['ans_rev'] = len(ans_rev)

            if len(q_rev)+len(new_ans)+len(ans_rev) == 0:
                meta_data['skip'] = True
            else:
                meta_data['skip'] = False
                update_info.active_at = now
                update_info.save() #save question email update activity 
        #q_list is actually a ordered dictionary
        #print 'user %s gets %d' % (user.username, len(q_list.keys()))
        #todo: sort question list by update time
        return q_list 
Example #2
0
    def get_updated_questions_for_user(self,user):
        q_sel = None 
        q_ask = None 
        q_ans = None 
        q_all = None 
        now = datetime.datetime.now()
        Q_set1 = Question.objects.exclude(
                                        last_activity_by=user,
                                  ).exclude(
                                        last_activity_at__lt=user.date_joined
                                  ).filter(
                                        Q(viewed__who=user,viewed__when__lt=F('last_activity_at')) | \
                                        ~Q(viewed__who=user)
                                  ).exclude(
                                        deleted=True
                                  ).exclude(
                                        closed=True
                                  )
            
        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)

        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:
                meta_data['q_rev'] = 0
                meta_data['new_q'] = True
            else:
                meta_data['new_q'] = False
                
            new_ans = Answer.objects.filter(question=q,\
                                            added_at__lt=cutoff_time,\
                                            added_at__gt=emailed_at)
            new_ans = new_ans.exclude(author=user)
            meta_data['new_ans'] = len(new_ans)
            ans_rev = AnswerRevision.objects.filter(answer__question=q,\
                                            revised_at__lt=cutoff_time,\
                                            revised_at__gt=emailed_at)
            ans_rev = ans_rev.exclude(author=user)
            meta_data['ans_rev'] = len(ans_rev)
            if len(q_rev) == 0 and len(new_ans) == 0 and len(ans_rev) == 0:
                meta_data['nothing_new'] = True
            else:
                meta_data['nothing_new'] = False
                update_info.active_at = now
                update_info.save() #save question email update activity 
        return q_list