Commit da2d0ed6 by Julian Arni

Foldit with puzzle leaderboard

parent e5294591
...@@ -73,7 +73,7 @@ class FolditModule(XModule): ...@@ -73,7 +73,7 @@ class FolditModule(XModule):
""" """
from foldit.models import Score 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): def get_html(self):
""" """
......
...@@ -39,23 +39,23 @@ class Score(models.Model): ...@@ -39,23 +39,23 @@ class Score(models.Model):
return (-score) * 10 + 8000 * sum_of return (-score) * 10 + 8000 * sum_of
# TODO: delete this, incorporate it in get_tops_n # TODO: delete this, incorporate it in get_tops_n
@staticmethod #@staticmethod
def get_top_n(puzzle_id, n): #def get_top_n(puzzle_id, n):
""" #"""
Arguments: #Arguments:
puzzle_id (int): id of the puzzle for which to look #puzzle_id (int): id of the puzzle for which to look
n (int): number of top scores to return. #n (int): number of top scores to return.
Returns: #Returns:
The top (lowest energy, highest display score) n scores for the puzzle. If #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 #there are fewer than n, returns all. Output is a list of dictionaries, sorted
by display_score: #by display_score:
[ {username: 'a_user', #[ {username: 'a_user',
score: 8500}, ...] #score: 8500}, ...]
""" #"""
scores = Score.objects.filter(puzzle_id=puzzle_id).order_by('-best_score')[:n] #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)} #return [{'username': s.user.username, 'score': Score.display_score(s.best_score)}
for s in scores] #for s in scores]
@staticmethod @staticmethod
def get_tops_n(n, puzzles=['994559']): def get_tops_n(n, puzzles=['994559']):
...@@ -72,13 +72,14 @@ class Score(models.Model): ...@@ -72,13 +72,14 @@ class Score(models.Model):
[ {username: 'a_user', [ {username: 'a_user',
score: 12000} ...] score: 12000} ...]
""" """
scores = Score.objects.filter(puzzle_id__in=puzzles).annotate( scores = Score.objects \
total_score=models.Sum('best_score')).order_by( .filter(puzzle_id__in=puzzles) \
'-total_score')[:n] .annotate(total_score=models.Sum('best_score')) \
.order_by('-total_score')[:n]
num = len(puzzles) num = len(puzzles)
return [{'username': s.user.username, 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] for s in scores]
......
...@@ -25,19 +25,22 @@ class FolditTestCase(TestCase): ...@@ -25,19 +25,22 @@ class FolditTestCase(TestCase):
pwd = 'abc' pwd = 'abc'
self.user = User.objects.create_user('testuser', 'test@test.com', pwd) 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_id = unique_id_for_user(self.user)
self.unique_user_id2 = unique_id_for_user(self.user2)
now = datetime.now() now = datetime.now()
self.tomorrow = now + timedelta(days=1) self.tomorrow = now + timedelta(days=1)
self.yesterday = now - timedelta(days=1) self.yesterday = now - timedelta(days=1)
UserProfile.objects.create(user=self.user) 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 = self.factory.post(self.url, post_data)
request.user = self.user request.user = user
return request 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 Given lists of puzzle_ids and best_scores (must have same length), make a
SetPlayerPuzzleScores request and return the response. SetPlayerPuzzleScores request and return the response.
...@@ -52,8 +55,8 @@ class FolditTestCase(TestCase): ...@@ -52,8 +55,8 @@ class FolditTestCase(TestCase):
scores = [score_dict(pid, bs) for pid, bs in zip(puzzle_ids, best_scores)] scores = [score_dict(pid, bs) for pid, bs in zip(puzzle_ids, best_scores)]
scores_str = json.dumps(scores) scores_str = json.dumps(scores)
verify = {"Verify": verify_code(self.user.email, scores_str), verify = {"Verify": verify_code(user.email, scores_str),
"VerifyMethod":"FoldItVerify"} "VerifyMethod": "FoldItVerify"}
data = {'SetPlayerPuzzleScoresVerify': json.dumps(verify), data = {'SetPlayerPuzzleScoresVerify': json.dumps(verify),
'SetPlayerPuzzleScores': scores_str} 'SetPlayerPuzzleScores': scores_str}
...@@ -65,7 +68,7 @@ class FolditTestCase(TestCase): ...@@ -65,7 +68,7 @@ class FolditTestCase(TestCase):
def test_SetPlayerPuzzleScores(self): def test_SetPlayerPuzzleScores(self):
puzzle_id = 994391 puzzle_id = [994391]
best_score = 0.078034 best_score = 0.078034
response = self.make_puzzle_score_request([puzzle_id], [best_score]) response = self.make_puzzle_score_request([puzzle_id], [best_score])
...@@ -76,14 +79,12 @@ class FolditTestCase(TestCase): ...@@ -76,14 +79,12 @@ class FolditTestCase(TestCase):
"Status": "Success"}]}])) "Status": "Success"}]}]))
# There should now be a score in the db. # 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(len(top_10), 1)
self.assertEqual(top_10[0]['score'], Score.display_score(best_score)) self.assertEqual(top_10[0]['score'], Score.display_score(best_score))
def test_SetPlayerPuzzleScores_many(self): def test_SetPlayerPuzzleScores_many(self):
response = self.make_puzzle_score_request([1, 2], [0.078034, 0.080000]) response = self.make_puzzle_score_request([1, 2], [0.078034, 0.080000])
self.assertEqual(response.content, json.dumps( self.assertEqual(response.content, json.dumps(
...@@ -96,19 +97,17 @@ class FolditTestCase(TestCase): ...@@ -96,19 +97,17 @@ class FolditTestCase(TestCase):
"Status": "Success"}]}])) "Status": "Success"}]}]))
def test_SetPlayerPuzzleScores_multiple(self): def test_SetPlayerPuzzleScores_multiple(self):
""" """
Check that multiple posts with the same id are handled properly Check that multiple posts with the same id are handled properly
(keep latest for each user, have multiple users work properly) (keep latest for each user, have multiple users work properly)
""" """
orig_score = 0.07 orig_score = 0.07
puzzle_id = 1 puzzle_id = ['1']
response = self.make_puzzle_score_request([puzzle_id], [orig_score]) response = self.make_puzzle_score_request([puzzle_id], [orig_score])
# There should now be a score in the db. # 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(len(top_10), 1)
self.assertEqual(top_10[0]['score'], Score.display_score(best_score)) self.assertEqual(top_10[0]['score'], Score.display_score(best_score))
...@@ -116,7 +115,7 @@ class FolditTestCase(TestCase): ...@@ -116,7 +115,7 @@ class FolditTestCase(TestCase):
better_score = 0.06 better_score = 0.06
response = self.make_puzzle_score_request([1], [better_score]) 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(len(top_10), 1)
self.assertEqual(top_10[0]['score'], Score.display_score(better_score)) self.assertEqual(top_10[0]['score'], Score.display_score(better_score))
...@@ -124,24 +123,51 @@ class FolditTestCase(TestCase): ...@@ -124,24 +123,51 @@ class FolditTestCase(TestCase):
worse_score = 0.065 worse_score = 0.065
response = self.make_puzzle_score_request([1], [worse_score]) 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) self.assertEqual(len(top_10), 1)
# should still be the better score # should still be the better score
self.assertEqual(top_10[0]['score'], Score.display_score(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): def test_SetPlayerPuzzleScores_error(self):
scores = [ {"PuzzleID": 994391, scores = [{"PuzzleID": 994391,
"ScoreType": "score", "ScoreType": "score",
"BestScore": 0.078034, "BestScore": 0.078034,
"CurrentScore":0.080035, "CurrentScore": 0.080035,
"ScoreVersion":23}] "ScoreVersion": 23}]
validation_str = json.dumps(scores) validation_str = json.dumps(scores)
verify = {"Verify": verify_code(self.user.email, validation_str), verify = {"Verify": verify_code(self.user.email, validation_str),
"VerifyMethod":"FoldItVerify"} "VerifyMethod": "FoldItVerify"}
# change the real string -- should get an error # change the real string -- should get an error
scores[0]['ScoreVersion'] = 22 scores[0]['ScoreVersion'] = 22
......
...@@ -111,16 +111,26 @@ def save_scores(user, puzzle_scores): ...@@ -111,16 +111,26 @@ def save_scores(user, puzzle_scores):
current_score = score['CurrentScore'] current_score = score['CurrentScore']
score_version = score['ScoreVersion'] score_version = score['ScoreVersion']
# TODO: save the score
# SetPlayerPuzzleScoreResponse object # SetPlayerPuzzleScoreResponse object
Score.objects.get_or_create( # Score entries are unique on user/unique_user_id/puzzle_id/score_version
user=user, try:
unique_user_id=unique_id_for_user(user), obj = Score.objects.get(
puzzle_id=puzzle_id, user=user,
best_score=best_score, unique_user_id=unique_id_for_user(user),
current_score=current_score, puzzle_id=puzzle_id,
score_version=score_version) 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? # TODO: get info from db instead?
score_responses.append({'PuzzleID': puzzle_id, score_responses.append({'PuzzleID': puzzle_id,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment