From e4312d67f0934f999057fe564861cd8df4af2a8c Mon Sep 17 00:00:00 2001 From: Victor Shnayder Date: Tue, 19 Feb 2013 09:43:19 -0500 Subject: [PATCH] work in progress on supporting science puzzles --- lms/djangoapps/foldit/models.py | 30 +++++++++++ lms/djangoapps/foldit/tests.py | 95 ++++++++++++++++++++++----------- 2 files changed, 93 insertions(+), 32 deletions(-) diff --git a/lms/djangoapps/foldit/models.py b/lms/djangoapps/foldit/models.py index ea4f099216..6ea180ff2c 100644 --- a/lms/djangoapps/foldit/models.py +++ b/lms/djangoapps/foldit/models.py @@ -25,6 +25,36 @@ class Score(models.Model): score_version = models.IntegerField() created = models.DateTimeField(auto_now_add=True) + @staticmethod + def display_score(score): + """ + Argument: + score (float), as stored in the DB + + Returns: + score (float), as displayed to the user in the game and in the leaderboard + """ + # TODO: put in correct formula + return -score + + @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': display_score(s.best_score)} + for s in scores] + class PuzzleComplete(models.Model): """ diff --git a/lms/djangoapps/foldit/tests.py b/lms/djangoapps/foldit/tests.py index 4d387b44e8..d560416f4b 100644 --- a/lms/djangoapps/foldit/tests.py +++ b/lms/djangoapps/foldit/tests.py @@ -9,7 +9,7 @@ from django.conf import settings from django.core.urlresolvers import reverse from foldit.views import foldit_ops, verify_code -from foldit.models import PuzzleComplete +from foldit.models import PuzzleComplete, Score from student.models import UserProfile, unique_id_for_user from datetime import datetime, timedelta @@ -37,13 +37,19 @@ class FolditTestCase(TestCase): request.user = self.user return request - def test_SetPlayerPuzzleScores(self): - - scores = [ {"PuzzleID": 994391, + def make_puzzle_score_request(self, puzzle_ids, best_scores): + """ + Given lists of puzzle_ids and best_scores (must have same length), make a + SetPlayerPuzzleScores request and return the response. + """ + def score_dict(puzzle_id, best_score): + return {"PuzzleID": puzzle_id, "ScoreType": "score", - "BestScore": 0.078034, - "CurrentScore":0.080035, - "ScoreVersion":23}] + "BestScore": best_score, + # current scores don't actually matter + "CurrentScore": best_score + 0.01, + "ScoreVersion": 23} + 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), @@ -55,51 +61,76 @@ class FolditTestCase(TestCase): response = foldit_ops(request) self.assertEqual(response.status_code, 200) + return response + + def test_SetPlayerPuzzleScores(self): + + puzzle_id = 994391 + best_score = 0.078034 + response = self.make_puzzle_score_request([puzzle_id], [best_score]) self.assertEqual(response.content, json.dumps( [{"OperationID": "SetPlayerPuzzleScores", "Value": [{ - "PuzzleID": 994391, + "PuzzleID": puzzle_id, "Status": "Success"}]}])) + # There should now be a score in the db. + top_10 = Score.get_top_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): - scores = [ {"PuzzleID": 994391, - "ScoreType": "score", - "BestScore": 0.078034, - "CurrentScore":0.080035, - "ScoreVersion":23}, - {"PuzzleID": 994392, - "ScoreType": "score", - "BestScore": 0.078000, - "CurrentScore":0.080011, - "ScoreVersion":23}] - - scores_str = json.dumps(scores) - - verify = {"Verify": verify_code(self.user.email, scores_str), - "VerifyMethod":"FoldItVerify"} - data = {'SetPlayerPuzzleScoresVerify': json.dumps(verify), - 'SetPlayerPuzzleScores': scores_str} - - request = self.make_request(data) - - response = foldit_ops(request) - self.assertEqual(response.status_code, 200) + response = self.make_puzzle_score_request([1, 2], [0.078034, 0.080000]) self.assertEqual(response.content, json.dumps( [{"OperationID": "SetPlayerPuzzleScores", "Value": [{ - "PuzzleID": 994391, + "PuzzleID": 1, "Status": "Success"}, - {"PuzzleID": 994392, + {"PuzzleID": 2, "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 + 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) + self.assertEqual(len(top_10), 1) + self.assertEqual(top_10[0]['score'], Score.display_score(best_score)) + + # Reporting a better score should overwrite + better_score = 0.06 + response = self.make_puzzle_score_request([1], [better_score]) + + top_10 = Score.get_top_n(puzzle_id, 10) + self.assertEqual(len(top_10), 1) + self.assertEqual(top_10[0]['score'], Score.display_score(better_score)) + + # reporting a worse score shouldn't + worse_score = 0.065 + response = self.make_puzzle_score_request([1], [worse_score]) + + top_10 = Score.get_top_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_SetPlayerPuzzleScores_error(self): scores = [ {"PuzzleID": 994391,