From da2d0ed6ec25732dc180f31220ca8af39252c7cf Mon Sep 17 00:00:00 2001 From: Julian Arni Date: Mon, 25 Feb 2013 11:16:37 -0500 Subject: [PATCH] Foldit with puzzle leaderboard --- common/lib/xmodule/xmodule/foldit_module.py | 2 +- lms/djangoapps/foldit/models.py | 41 ++++++------- lms/djangoapps/foldit/tests.py | 64 +++++++++++++++------ lms/djangoapps/foldit/views.py | 28 ++++++--- 4 files changed, 86 insertions(+), 49 deletions(-) diff --git a/common/lib/xmodule/xmodule/foldit_module.py b/common/lib/xmodule/xmodule/foldit_module.py index bfe921a068..b80f54a41c 100644 --- a/common/lib/xmodule/xmodule/foldit_module.py +++ b/common/lib/xmodule/xmodule/foldit_module.py @@ -73,7 +73,7 @@ class FolditModule(XModule): """ from foldit.models import Score - return [(e['username'], e['total_score']) for e in Score.get_tops_n(10)] + return [(e['username'], e['score']) for e in Score.get_tops_n(10)] def get_html(self): """ diff --git a/lms/djangoapps/foldit/models.py b/lms/djangoapps/foldit/models.py index 3202402e52..df1be3e87c 100644 --- a/lms/djangoapps/foldit/models.py +++ b/lms/djangoapps/foldit/models.py @@ -39,23 +39,23 @@ class Score(models.Model): return (-score) * 10 + 8000 * sum_of # TODO: delete this, incorporate it in get_tops_n - @staticmethod - def get_top_n(puzzle_id, n): - """ - Arguments: - puzzle_id (int): id of the puzzle for which to look - n (int): number of top scores to return. + #@staticmethod + #def get_top_n(puzzle_id, n): + #""" + #Arguments: + #puzzle_id (int): id of the puzzle for which to look + #n (int): number of top scores to return. - Returns: - The top (lowest energy, highest display score) n scores for the puzzle. If - there are fewer than n, returns all. Output is a list of dictionaries, sorted - by display_score: - [ {username: 'a_user', - score: 8500}, ...] - """ - scores = Score.objects.filter(puzzle_id=puzzle_id).order_by('-best_score')[:n] - return [{'username': s.user.username, 'score': Score.display_score(s.best_score)} - for s in scores] + #Returns: + #The top (lowest energy, highest display score) n scores for the puzzle. If + #there are fewer than n, returns all. Output is a list of dictionaries, sorted + #by display_score: + #[ {username: 'a_user', + #score: 8500}, ...] + #""" + #scores = Score.objects.filter(puzzle_id=puzzle_id).order_by('-best_score')[:n] + #return [{'username': s.user.username, 'score': Score.display_score(s.best_score)} + #for s in scores] @staticmethod def get_tops_n(n, puzzles=['994559']): @@ -72,13 +72,14 @@ class Score(models.Model): [ {username: 'a_user', score: 12000} ...] """ - scores = Score.objects.filter(puzzle_id__in=puzzles).annotate( - total_score=models.Sum('best_score')).order_by( - '-total_score')[:n] + scores = Score.objects \ + .filter(puzzle_id__in=puzzles) \ + .annotate(total_score=models.Sum('best_score')) \ + .order_by('-total_score')[:n] num = len(puzzles) return [{'username': s.user.username, - 'total_score': Score.display_score(s.total_score, num)} + 'score': Score.display_score(s.total_score, num)} for s in scores] diff --git a/lms/djangoapps/foldit/tests.py b/lms/djangoapps/foldit/tests.py index d560416f4b..b81119d614 100644 --- a/lms/djangoapps/foldit/tests.py +++ b/lms/djangoapps/foldit/tests.py @@ -25,19 +25,22 @@ class FolditTestCase(TestCase): pwd = 'abc' self.user = User.objects.create_user('testuser', 'test@test.com', pwd) + self.user2 = User.objects.create_user('testuser2', 'test2@test.com', pwd) self.unique_user_id = unique_id_for_user(self.user) + self.unique_user_id2 = unique_id_for_user(self.user2) now = datetime.now() self.tomorrow = now + timedelta(days=1) self.yesterday = now - timedelta(days=1) UserProfile.objects.create(user=self.user) + UserProfile.objects.create(user=self.user2) - def make_request(self, post_data): + def make_request(self, post_data, user=self.user): request = self.factory.post(self.url, post_data) - request.user = self.user + request.user = user return request - def make_puzzle_score_request(self, puzzle_ids, best_scores): + def make_puzzle_score_request(self, puzzle_ids, best_scores, user=self.user): """ Given lists of puzzle_ids and best_scores (must have same length), make a SetPlayerPuzzleScores request and return the response. @@ -52,8 +55,8 @@ class FolditTestCase(TestCase): scores = [score_dict(pid, bs) for pid, bs in zip(puzzle_ids, best_scores)] scores_str = json.dumps(scores) - verify = {"Verify": verify_code(self.user.email, scores_str), - "VerifyMethod":"FoldItVerify"} + verify = {"Verify": verify_code(user.email, scores_str), + "VerifyMethod": "FoldItVerify"} data = {'SetPlayerPuzzleScoresVerify': json.dumps(verify), 'SetPlayerPuzzleScores': scores_str} @@ -65,7 +68,7 @@ class FolditTestCase(TestCase): def test_SetPlayerPuzzleScores(self): - puzzle_id = 994391 + puzzle_id = [994391] best_score = 0.078034 response = self.make_puzzle_score_request([puzzle_id], [best_score]) @@ -76,14 +79,12 @@ class FolditTestCase(TestCase): "Status": "Success"}]}])) # There should now be a score in the db. - top_10 = Score.get_top_n(puzzle_id, 10) + top_10 = Score.get_tops_n(puzzle_id, 10) self.assertEqual(len(top_10), 1) self.assertEqual(top_10[0]['score'], Score.display_score(best_score)) - def test_SetPlayerPuzzleScores_many(self): - response = self.make_puzzle_score_request([1, 2], [0.078034, 0.080000]) self.assertEqual(response.content, json.dumps( @@ -96,19 +97,17 @@ class FolditTestCase(TestCase): "Status": "Success"}]}])) - - def test_SetPlayerPuzzleScores_multiple(self): """ Check that multiple posts with the same id are handled properly (keep latest for each user, have multiple users work properly) """ orig_score = 0.07 - puzzle_id = 1 + puzzle_id = ['1'] response = self.make_puzzle_score_request([puzzle_id], [orig_score]) # There should now be a score in the db. - top_10 = Score.get_top_n(puzzle_id, 10) + top_10 = Score.get_tops_n(puzzle_id, 10) self.assertEqual(len(top_10), 1) self.assertEqual(top_10[0]['score'], Score.display_score(best_score)) @@ -116,7 +115,7 @@ class FolditTestCase(TestCase): better_score = 0.06 response = self.make_puzzle_score_request([1], [better_score]) - top_10 = Score.get_top_n(puzzle_id, 10) + top_10 = Score.get_tops_n(puzzle_id, 10) self.assertEqual(len(top_10), 1) self.assertEqual(top_10[0]['score'], Score.display_score(better_score)) @@ -124,24 +123,51 @@ class FolditTestCase(TestCase): worse_score = 0.065 response = self.make_puzzle_score_request([1], [worse_score]) - top_10 = Score.get_top_n(puzzle_id, 10) + top_10 = Score.get_tops_n(puzzle_id, 10) self.assertEqual(len(top_10), 1) # should still be the better score self.assertEqual(top_10[0]['score'], Score.display_score(better_score)) + def test_SetPlayerPyzzleScores_manyplayers(self): + """ + Check that when we send scores from multiple users, the correct order + of scores is displayed. + """ + puzzle_id = ['1'] + player1_score = 0.07 + player2_score = 0.08 + response1 = self.make_puzzle_score_request([puzzle_id], [player1_score], + self.user) + # There should now be a score in the db. + top_10 = Score.get_tops_n(puzzle_id, 10) + self.assertEqual(len(top_10), 1) + self.assertEqual(top_10[0]['score'], Score.display_score(player1_score)) + + response2 = self.make_puzzle_score_request([puzzle_id], [player2_score], + self.user2) + + # There should now be two scores in the db + self.assertEqual(len(top_10), 2) + + # Top score should be player2_score. Second should be player1_score + self.assertEqual(top_10[0]['score'], Score.display_score(player2_score)) + self.assertEqual(top_10[1]['score'], Score.display_score(player1_score)) + + # Top score user should be self.user2.username + self.assertEqual(top_10[0]['username'], self.user2.username) def test_SetPlayerPuzzleScores_error(self): - scores = [ {"PuzzleID": 994391, + scores = [{"PuzzleID": 994391, "ScoreType": "score", "BestScore": 0.078034, - "CurrentScore":0.080035, - "ScoreVersion":23}] + "CurrentScore": 0.080035, + "ScoreVersion": 23}] validation_str = json.dumps(scores) verify = {"Verify": verify_code(self.user.email, validation_str), - "VerifyMethod":"FoldItVerify"} + "VerifyMethod": "FoldItVerify"} # change the real string -- should get an error scores[0]['ScoreVersion'] = 22 diff --git a/lms/djangoapps/foldit/views.py b/lms/djangoapps/foldit/views.py index 8b284704d6..9939d1aa63 100644 --- a/lms/djangoapps/foldit/views.py +++ b/lms/djangoapps/foldit/views.py @@ -111,16 +111,26 @@ def save_scores(user, puzzle_scores): current_score = score['CurrentScore'] score_version = score['ScoreVersion'] - # TODO: save the score - # SetPlayerPuzzleScoreResponse object - Score.objects.get_or_create( - user=user, - unique_user_id=unique_id_for_user(user), - puzzle_id=puzzle_id, - best_score=best_score, - current_score=current_score, - score_version=score_version) + # Score entries are unique on user/unique_user_id/puzzle_id/score_version + try: + obj = Score.objects.get( + user=user, + unique_user_id=unique_id_for_user(user), + puzzle_id=puzzle_id, + score_version=score_version) + obj.current_score = current_score + obj.best_score = best_score + + except Score.DoesNotExist: + obj = Score( + user=user, + unique_user_id=unique_id_for_user(user), + puzzle_id=puzzle_id, + current_score=current_score, + best_score=best_score, + score_version=score_version) + obj.save() # TODO: get info from db instead? score_responses.append({'PuzzleID': puzzle_id,