Skip to content

Commit

Permalink
Merge pull request #394 from edx/fix/foldit-leaderboard
Browse files Browse the repository at this point in the history
Fix/foldit leaderboard
  • Loading branch information
adampalay committed Jul 19, 2013
2 parents 032b02d + a04539a commit 73e6e6f
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 26 deletions.
9 changes: 6 additions & 3 deletions common/lib/xmodule/xmodule/foldit_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,18 @@ def completed_puzzles(self):
PuzzleComplete.completed_puzzles(self.system.anonymous_student_id),
key=lambda d: (d['set'], d['subset']))

def puzzle_leaders(self, n=10):
def puzzle_leaders(self, n=10, courses=None):
"""
Returns a list of n pairs (user, score) corresponding to the top
scores; the pairs are in descending order of score.
"""
from foldit.models import Score

leaders = [(e['username'], e['score']) for e in Score.get_tops_n(10)]
leaders.sort(key=lambda x:-x[1])
if courses is None:
courses = [self.location.course_id]

leaders = [(leader['username'], leader['score']) for leader in Score.get_tops_n(10, course_list=courses)]
leaders.sort(key=lambda x: -x[1])

return leaders

Expand Down
37 changes: 24 additions & 13 deletions lms/djangoapps/foldit/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

log = logging.getLogger(__name__)


class Score(models.Model):
"""
This model stores the scores of different users on FoldIt problems.
Expand Down Expand Up @@ -35,9 +36,8 @@ def display_score(score, sum_of=1):
"""
return (-score) * 10 + 8000 * sum_of


@staticmethod
def get_tops_n(n, puzzles=['994559']):
def get_tops_n(n, puzzles=['994559'], course_list=None):
"""
Arguments:
puzzles: a list of puzzle ids that we will use. If not specified,
Expand All @@ -46,22 +46,34 @@ def get_tops_n(n, puzzles=['994559']):
Returns:
The top n sum of scores for puzzles in <puzzles>. Output is a list
of disctionaries, sorted by display_score:
The top n sum of scores for puzzles in <puzzles>,
filtered by course. If no courses is specified we default
the pool of students to all courses. Output is a list
of dictionaries, sorted by display_score:
[ {username: 'a_user',
score: 12000} ...]
"""
if not(type(puzzles) == list):

if not isinstance(puzzles, list):
puzzles = [puzzles]
scores = Score.objects \
.filter(puzzle_id__in=puzzles) \
.annotate(total_score=models.Sum('best_score')) \
.order_by('total_score')[:n]
if course_list is None:
scores = Score.objects \
.filter(puzzle_id__in=puzzles) \
.annotate(total_score=models.Sum('best_score')) \
.order_by('total_score')[:n]
else:
scores = Score.objects \
.filter(puzzle_id__in=puzzles) \
.filter(user__courseenrollment__course_id__in=course_list) \
.annotate(total_score=models.Sum('best_score')) \
.order_by('total_score')[:n]
num = len(puzzles)

return [{'username': s.user.username,
'score': Score.display_score(s.total_score, num)}
for s in scores]
return [
{'username': score.user.username,
'score': Score.display_score(score.total_score, num)}
for score in scores
]


class PuzzleComplete(models.Model):
Expand Down Expand Up @@ -94,7 +106,6 @@ def __unicode__(self):
self.puzzle_set, self.puzzle_subset,
self.created)


@staticmethod
def completed_puzzles(anonymous_user_id):
"""
Expand Down
60 changes: 50 additions & 10 deletions lms/djangoapps/foldit/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
import logging
from functools import partial

from django.contrib.auth.models import User
from django.test import TestCase
from django.test.client import RequestFactory
from django.core.urlresolvers import reverse

from foldit.views import foldit_ops, verify_code
from foldit.models import PuzzleComplete, Score
from student.models import UserProfile, unique_id_for_user
from student.models import unique_id_for_user
from student.tests.factories import CourseEnrollmentFactory, UserFactory, UserProfileFactory

from datetime import datetime, timedelta
from pytz import UTC
Expand All @@ -23,17 +23,25 @@ def setUp(self):
self.factory = RequestFactory()
self.url = reverse('foldit_ops')

pwd = 'abc'
self.user = User.objects.create_user('testuser', '[email protected]', pwd)
self.user2 = User.objects.create_user('testuser2', '[email protected]', pwd)
self.unique_user_id = unique_id_for_user(self.user)
self.unique_user_id2 = unique_id_for_user(self.user2)
self.course_id = 'course/id/1'
self.course_id2 = 'course/id/2'

self.user = UserFactory.create()
self.user2 = UserFactory.create()

self.course_enrollment = CourseEnrollmentFactory.create(
user=self.user, course_id=self.course_id
)
self.course_enrollment2 = CourseEnrollmentFactory.create(
user=self.user2, course_id=self.course_id2
)

now = datetime.now(UTC)
self.tomorrow = now + timedelta(days=1)
self.yesterday = now - timedelta(days=1)

UserProfile.objects.create(user=self.user)
UserProfile.objects.create(user=self.user2)
self.user.profile
self.user2.profile

def make_request(self, post_data, user=None):
request = self.factory.post(self.url, post_data)
Expand Down Expand Up @@ -150,6 +158,38 @@ def test_SetPlayerPuzzleScores_multiple(self):
delta=0.5
)

def test_SetPlayerPuzzleScores_multiplecourses(self):
puzzle_id = "1"

player1_score = 0.05
player2_score = 0.06

course_list_1 = [self.course_id]
course_list_2 = [self.course_id2]

response1 = self.make_puzzle_score_request(
puzzle_id, player1_score, self.user
)
course_1_top_10 = Score.get_tops_n(10, puzzle_id, course_list_1)
course_2_top_10 = Score.get_tops_n(10, puzzle_id, course_list_2)
total_top_10 = Score.get_tops_n(10, puzzle_id)

# player1 should now be in the top 10 of course 1 and not in course 2
self.assertEqual(len(course_1_top_10), 1)
self.assertEqual(len(course_2_top_10), 0)
self.assertEqual(len(total_top_10), 1)

response2 = self.make_puzzle_score_request(
puzzle_id, player2_score, self.user2
)
course_2_top_10 = Score.get_tops_n(10, puzzle_id, course_list_2)
total_top_10 = Score.get_tops_n(10, puzzle_id)

# player2 should now be in the top 10 of course 2 and not in course 1
self.assertEqual(len(course_1_top_10), 1)
self.assertEqual(len(course_2_top_10), 1)
self.assertEqual(len(total_top_10), 2)

def test_SetPlayerPuzzleScores_manyplayers(self):
"""
Check that when we send scores from multiple users, the correct order
Expand Down Expand Up @@ -306,7 +346,7 @@ def test_SetPlayerPuzzlesComplete_level_complete(self):
self.set_puzzle_complete_response([13, 14, 15, 53524]))

is_complete = partial(
PuzzleComplete.is_level_complete, self.unique_user_id)
PuzzleComplete.is_level_complete, unique_id_for_user(self.user))

self.assertTrue(is_complete(1, 1))
self.assertTrue(is_complete(1, 3))
Expand Down

0 comments on commit 73e6e6f

Please sign in to comment.