def update_top_contributors(): """"Update the top contributor lists and titles.""" if not waffle.switch_is_active('karma'): return KarmaManager().update_top() top25 = KarmaManager().top_users(count=25) Title.objects.set_top10_contributors(top25[:10]) Title.objects.set_top25_contributors(top25[10:25])
def setUp(self, switch_is_active): switch_is_active.return_value = True super(KarmaAPITests, self).setUp() try: self.mgr = KarmaManager() redis_client('karma').flushdb() except RedisError: raise SkipTest self.user1 = user(save=True) self.user2 = user(save=True) self.user3 = user(save=True) TestAction1(user=self.user1).save() TestAction2(user=self.user2).save() TestAction2(user=self.user2).save() TestAction1(user=self.user3).save() TestAction1(user=self.user3).save() TestAction1(user=self.user3).save() self.mgr.update_top() self.client.login(username=self.user1.username, password='******') add_permission(self.user1, models.Title, 'view_dashboard')
def recalculate_karma_points(): """Go through all karma action data and recalculate points.""" if not waffle.switch_is_active('karma'): return for chunk in chunked(list(KarmaManager().user_ids()), 2500): _process_recalculate_chunk.apply_async(args=[chunk])
def creator_num_points(self): try: return KarmaManager().count( 'all', user=self.creator, type='points') except RedisError as e: statsd.incr('redis.errror') log.error('Redis connection error: %s' % e)
def setUp(self, switch_is_active): switch_is_active.return_value = True super(KarmaManagerTests, self).setUp() try: self.mgr = KarmaManager() redis_client('karma').flushdb() except RedisError: raise SkipTest self.user1 = user(save=True) self.user2 = user(save=True) self.user3 = user(save=True) today = date.today() # user1 actions (3 + 3 + 7): TestAction1(user=self.user1, day=today).save() TestAction1(user=self.user1, day=today).save() TestAction2(user=self.user1, day=today).save() # user2 actions (3 + 7 + 7): TestAction1(user=self.user2, day=today - timedelta(days=8)).save() TestAction2(user=self.user2, day=today - timedelta(days=32)).save() TestAction2(user=self.user2, day=today - timedelta(days=360)).save() # user3 actions (3 + 3 + 3 + 7): TestAction1(user=self.user3, day=today - timedelta(days=10)).save() TestAction1(user=self.user3, day=today - timedelta(days=40)).save() TestAction1(user=self.user3, day=today - timedelta(days=190)).save() TestAction2(user=self.user3, day=today - timedelta(days=3)).save()
def details(request): """Returns monthly or daily totals for an action type. Feeds the dashboard chart. """ mgr = KarmaManager() form = DetailAPIForm(request.GET) if not form.is_valid(): return {'success': False, 'errors': form.errors} userid = form.cleaned_data.get('userid') or 'overview' daterange = form.cleaned_data.get('daterange') or '1y' counts = {} count_func = mgr.monthly_counts if daterange == '1w': count_func = mgr.daily_counts for t in KarmaManager.action_types.keys(): counts[t], time_units = count_func(type=t, **form.cleaned_data) return { 'success': True, 'time_units': time_units, 'counts': counts, 'userid': userid }
def overview(request): """Returns the overview for a daterange. GET paramaters: * daterange - 7d, 1m, 3m, 6m or 1y (default: 1y) Returns an overview dict with a count for all action types. """ form = OverviewAPIForm(request.GET) if not form.is_valid(): return {'success': False, 'errors': form.errors} daterange = form.cleaned_data.get('daterange') or '1y' mgr = KarmaManager() overview = {} for t in KarmaManager.action_types.keys(): overview[t] = mgr.count(daterange=daterange, type=t) # TODO: Maybe have a karma action not assigned to a user for this? num_days = KarmaManager.date_ranges[daterange] start_day = date.today() - timedelta(days=num_days) overview['question'] = Question.objects.filter( created__gt=start_day).count() return {'success': True, 'overview': overview}
def setUp(self): super(KarmaActionTests, self).setUp() self.user = user(save=True) try: self.mgr = KarmaManager() redis_client('karma').flushdb() except RedisError: raise SkipTest
def _process_recalculate_chunk(data, **kwargs): """Recalculate karma points for a chunk of user ids.""" mgr = KarmaManager() actions = [ AnswerAction, AnswerMarkedHelpfulAction, AnswerMarkedNotHelpfulAction, FirstAnswerAction, SolutionAction ] actions_dict = dict((a.action_type, a.points) for a in actions) for userid in data: mgr.recalculate_points(userid, actions_dict)
def karma(request): """Admin view that displays karma related data.""" if not request.user.has_perm('users.view_karma_points'): raise PermissionDenied if request.POST.get('init'): init_karma.delay() messages.add_message(request, messages.SUCCESS, 'init_karma task queued!') return HttpResponseRedirect(request.path) if request.POST.get('recalculate'): recalculate_karma_points.delay() messages.add_message(request, messages.SUCCESS, 'recalculate_karma_points task queued!') return HttpResponseRedirect(request.path) if request.POST.get('update-top'): update_top_contributors.delay() messages.add_message(request, messages.SUCCESS, 'update_top_contributors task queued!') return HttpResponseRedirect(request.path) kmgr = KarmaManager() top_alltime = [ _user_karma_alltime(u, kmgr) for u in kmgr.top_users() or [] ] top_week = [ _user_karma_week(u, kmgr) for u in kmgr.top_users(daterange='1w') or [] ] username = request.GET.get('username') user_karma = None if username: try: user = User.objects.get(username=username) d = kmgr.user_data(user) user_karma = [{'key': k, 'value': d[k]} for k in sorted(d.keys())] except User.DoesNotExist: pass return render_to_response( 'karma/admin/karma.html', { 'title': 'Karma', 'top_alltime': top_alltime, 'top_week': top_week, 'username': username, 'user_karma': user_karma }, RequestContext(request, {}))
def creator_num_answers(self): # If karma is enabled, try to use the karma backend (redis) to get # the number of answers. Fallback to database. if waffle.switch_is_active('karma'): try: count = KarmaManager().count( user=self.creator, type=AnswerAction.action_type) if count != None: return count except RedisError as e: statsd.incr('redis.errror') log.error('Redis connection error: %s' % e) return Answer.objects.filter(creator=self.creator).count()
def user_num_answers(user): """Count the number of answers a user has. If karma is enabled, and redis is working, this will query that (much faster), otherwise it will just count objects in the database. """ if waffle.switch_is_active('karma'): try: km = KarmaManager() count = km.count(user=user, type=AnswerAction.action_type) if count is not None: return count except RedisError as e: statsd.incr('redis.errror') log.error('Redis connection error: %s' % e) return Answer.objects.filter(creator=user).count()
def test_creator_nums_redis(self, switch_is_active): """Test creator_num_* pulled from karma data.""" try: KarmaManager() redis_client('karma').flushdb() except RedisError: raise SkipTest switch_is_active.return_value = True answer = Answer.objects.all()[0] AnswerAction(answer.creator).save() AnswerAction(answer.creator).save() SolutionAction(answer.creator).save() eq_(answer.creator_num_solutions, 1) eq_(answer.creator_num_answers, 2)
def users(request): """Returns list of user karma information. GET paramaters: * daterange - 7d, 1m, 3m, 6m or 1y (default: 1y) * sort - field to sort on (default: points). Order is always descending. * page - starts at 1 (default: 1) * pagesize - (default: 100) Returns list of objects with the following fields: userid, username, points, <action_types> """ form = UserAPIForm(request.GET) if not form.is_valid(): return {'success': False, 'errors': form.errors} daterange = form.cleaned_data.get('daterange') or '1y' sort = form.cleaned_data.get('sort') or 'points' page = form.cleaned_data.get('page') or 1 pagesize = form.cleaned_data.get('pagesize') or 100 mgr = KarmaManager() users = mgr.top_users(daterange=daterange, type=sort, count=pagesize, offset=(page - 1) * pagesize) or [] now = datetime.now() action_types = KarmaManager.action_types.keys() schema = ['id', 'username', 'lastactivity', 'points'] + action_types user_list = [] for u in users: user = [u.id, u.username] last_activity = Answer.last_activity_for(u) user.append((now - last_activity).days if last_activity else None) user.append(mgr.count(u, daterange=daterange, type='points')) for t in action_types: user.append(mgr.count(u, daterange=daterange, type=t)) user_list.append(user) return {'success': True, 'results': user_list, 'schema': schema}
def user_num_solutions(user): """Count the number of solutions a user has. This means the number of answers the user has submitted that are then marked as the solution to the question they belong to. If karma is enabled, and redis is working, this will query that (much faster), otherwise it will just count objects in the database. """ if waffle.switch_is_active('karma'): try: km = KarmaManager() count = km.count(user=user, type=SolutionAction.action_type) if count is not None: return count except RedisError as e: statsd.incr('redis.errror') log.error('Redis connection error: %s' % e) return Question.objects.filter(solution__in=Answer.objects .filter(creator=user)).count()
def questions(request): """View the questions.""" filter_ = request.GET.get('filter') tagged = request.GET.get('tagged') tags = None sort_ = request.GET.get('sort', None) if sort_ == 'requested': order = '-num_votes_past_week' elif sort_ == 'created': order = '-created' else: order = '-updated' question_qs = Question.objects.select_related('creator', 'last_answer', 'last_answer__creator') if not waffle.switch_is_active('hide-total-question-votes'): question_qs = question_qs.extra({ '_num_votes': 'SELECT COUNT(*) FROM questions_questionvote WHERE ' 'questions_questionvote.question_id = ' 'questions_question.id' }) question_qs = question_qs.filter(creator__is_active=1) if filter_ == 'no-replies': question_qs = question_qs.filter(num_answers=0, is_locked=False) elif filter_ == 'replies': question_qs = question_qs.filter(num_answers__gt=0) elif filter_ == 'solved': question_qs = question_qs.exclude(solution=None) elif filter_ == 'unsolved': question_qs = question_qs.filter(solution=None) elif filter_ == 'my-contributions' and request.user.is_authenticated(): criteria = Q(answers__creator=request.user) | Q(creator=request.user) question_qs = question_qs.filter(criteria).distinct() elif filter_ == 'recent-unanswered': # Only unanswered questions from the last 72 hours. start = datetime.now() - timedelta(hours=72) question_qs = question_qs.filter(num_answers=0, created__gt=start, is_locked=False) else: filter_ = None feed_urls = ((reverse('questions.feed'), QuestionsFeed().title()), ) if tagged: tag_slugs = tagged.split(',') tags = Tag.objects.filter(slug__in=tag_slugs) if tags: for t in tags: question_qs = question_qs.filter(tags__name__in=[t.name]) if len(tags) == 1: feed_urls += ((reverse('questions.tagged_feed', args=[tags[0].slug]), TaggedQuestionsFeed().title(tags[0])), ) else: question_qs = Question.objects.get_empty_query_set() # Exclude questions over 90 days old without an answer. oldest_date = date.today() - timedelta(days=90) question_qs = question_qs.exclude(created__lt=oldest_date, num_answers=0) # Set the order. question_qs = question_qs.order_by(order) try: with statsd.timer('questions.view.paginate.%s' % filter_): questions_page = simple_paginate( request, question_qs, per_page=constants.QUESTIONS_PER_PAGE) except (PageNotAnInteger, EmptyPage): # If we aren't on page 1, redirect there. # TODO: Is 404 more appropriate? if request.GET.get('page', '1') != '1': url = build_paged_url(request) return HttpResponseRedirect(urlparams(url, page=1)) # Recent answered stats recent_asked_count = Question.recent_asked_count() recent_unanswered_count = Question.recent_unanswered_count() if recent_asked_count: recent_answered_percent = int( (float(recent_asked_count - recent_unanswered_count) / recent_asked_count) * 100) else: recent_answered_percent = 0 data = { 'questions': questions_page, 'feeds': feed_urls, 'filter': filter_, 'sort': sort_, 'tags': tags, 'tagged': tagged, 'recent_asked_count': recent_asked_count, 'recent_unanswered_count': recent_unanswered_count, 'recent_answered_percent': recent_answered_percent } if (waffle.flag_is_active(request, 'karma') and waffle.switch_is_active('karma')): kmgr = KarmaManager() data.update(karma_top=kmgr.top_users()) if request.user.is_authenticated(): ranking = kmgr.ranking(request.user) if ranking <= constants.HIGHEST_RANKING: data.update(karma_ranking=ranking) else: data.update(top_contributors=_get_top_contributors()) with statsd.timer('questions.view.render'): return jingo.render(request, 'questions/questions.html', data)
def questions(request): """View the questions.""" filter_ = request.GET.get('filter') tagged = request.GET.get('tagged') tags = None sort_ = request.GET.get('sort') cache_count = True # Some counts are too esoteric to cache right now. if sort_ == 'requested': order = '-num_votes_past_week' else: sort_ = None order = '-updated' question_qs = Question.objects.select_related( 'creator', 'last_answer', 'last_answer__creator') question_qs = question_qs.extra( {'_num_votes': 'SELECT COUNT(*) FROM questions_questionvote WHERE ' 'questions_questionvote.question_id = ' 'questions_question.id'}) question_qs = question_qs.filter(creator__is_active=1, is_locked=False) if filter_ == 'no-replies': question_qs = question_qs.filter(num_answers=0) elif filter_ == 'replies': question_qs = question_qs.filter(num_answers__gt=0) elif filter_ == 'solved': question_qs = question_qs.exclude(solution=None) elif filter_ == 'unsolved': question_qs = question_qs.filter(solution=None) elif filter_ == 'my-contributions' and request.user.is_authenticated(): criteria = Q(answers__creator=request.user) | Q(creator=request.user) question_qs = question_qs.filter(criteria).distinct() cache_count = False else: filter_ = None feed_urls = ((reverse('questions.feed'), QuestionsFeed().title()),) if tagged: cache_count = False tag_slugs = tagged.split(',') tags = Tag.objects.filter(slug__in=tag_slugs) if tags: for t in tags: question_qs = question_qs.filter(tags__name__in=[t.name]) if len(tags) == 1: feed_urls += ((reverse('questions.tagged_feed', args=[tags[0].slug]), TaggedQuestionsFeed().title(tags[0])),) else: question_qs = Question.objects.get_empty_query_set() question_qs = question_qs.order_by(order) if cache_count: cache_key = u'questions:count:%s' % filter_ count = cache.get(cache_key) if not count: count = question_qs.count() cache.add(cache_key, count, settings.QUESTIONS_COUNT_TTL) else: count = question_qs.count() questions_ = paginate(request, question_qs, count=count, per_page=constants.QUESTIONS_PER_PAGE) data = {'questions': questions_, 'feeds': feed_urls, 'filter': filter_, 'sort': sort_, 'tags': tags, 'tagged': tagged} if (waffle.flag_is_active(request, 'karma') and waffle.switch_is_active('karma')): kmgr = KarmaManager() data.update(karma_top=kmgr.top_users()) if request.user.is_authenticated(): ranking = kmgr.ranking(request.user) if ranking <= constants.HIGHEST_RANKING: data.update(karma_ranking=ranking) else: data.update(top_contributors=_get_top_contributors()) return jingo.render(request, 'questions/questions.html', data)
def _delete(self): statsd.incr('karma.delete.{t}'.format(t=self.action_type)) KarmaManager().delete_action(self)
def _save(self, redis=None): statsd.incr('karma.{t}'.format(t=self.action_type)) KarmaManager(redis).save_action(self)
def _process_recalculate_chunk(data): """Recalculate karma points for a chunk of user ids.""" mgr = KarmaManager() for userid in data: mgr.recalculate_points(userid)