diff --git a/common/lib/xmodule/xmodule/foldit_module.py b/common/lib/xmodule/xmodule/foldit_module.py index ea16fee7f1..bfe921a068 100644 --- a/common/lib/xmodule/xmodule/foldit_module.py +++ b/common/lib/xmodule/xmodule/foldit_module.py @@ -66,6 +66,14 @@ class FolditModule(XModule): PuzzleComplete.completed_puzzles(self.system.anonymous_student_id), key=lambda d: (d['set'], d['subset'])) + def puzzle_leaders(self, n=10): + """ + 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 + + return [(e['username'], e['total_score']) for e in Score.get_tops_n(10)] def get_html(self): """ @@ -80,6 +88,7 @@ class FolditModule(XModule): 'success': self.is_complete(), 'goal_level': goal_level, 'completed': self.completed_puzzles(), + 'top_scores': self.puzzle_leaders(), } return self.system.render_template('foldit.html', context) @@ -97,6 +106,7 @@ class FolditModule(XModule): return 1 + class FolditDescriptor(XmlDescriptor, EditingDescriptor): """ Module for adding open ended response questions to courses diff --git a/lms/djangoapps/foldit/models.py b/lms/djangoapps/foldit/models.py index 6ea180ff2c..3202402e52 100644 --- a/lms/djangoapps/foldit/models.py +++ b/lms/djangoapps/foldit/models.py @@ -26,17 +26,19 @@ class Score(models.Model): created = models.DateTimeField(auto_now_add=True) @staticmethod - def display_score(score): + def display_score(score, sum_of=1): """ Argument: - score (float), as stored in the DB + score (float), as stored in the DB (i.e., "rosetta score") + sum_of (int): if this score is the sum of scores of individual + problems, how many elements are in that sum Returns: score (float), as displayed to the user in the game and in the leaderboard """ - # TODO: put in correct formula - return -score + return (-score) * 10 + 8000 * sum_of + # TODO: delete this, incorporate it in get_tops_n @staticmethod def get_top_n(puzzle_id, n): """ @@ -52,9 +54,33 @@ class Score(models.Model): 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)} + return [{'username': s.user.username, 'score': Score.display_score(s.best_score)} for s in scores] + @staticmethod + def get_tops_n(n, puzzles=['994559']): + """ + Arguments: + puzzles: a list of puzzle ids that we will use. If not specified, + defaults to puzzle used in 7012x. + n (int): number of top scores to return + + + Returns: + The top n sum of scores for puzzles in . Output is a list + of disctionaries, sorted by display_score: + [ {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] + num = len(puzzles) + + return [{'username': s.user.username, + 'total_score': Score.display_score(s.total_score, num)} + for s in scores] + class PuzzleComplete(models.Model): """ diff --git a/lms/djangoapps/foldit/views.py b/lms/djangoapps/foldit/views.py index 8bd3684c04..8b284704d6 100644 --- a/lms/djangoapps/foldit/views.py +++ b/lms/djangoapps/foldit/views.py @@ -10,6 +10,8 @@ from django.views.decorators.csrf import csrf_exempt from foldit.models import Score, PuzzleComplete from student.models import unique_id_for_user +import re + log = logging.getLogger(__name__) @@ -38,6 +40,13 @@ def foldit_ops(request): "user %s, scores json %r, verify %r", request.user, puzzle_scores_json, pz_verify_json) else: + # This is needed because we are not getting valid json - the + # value of ScoreType is an unquoted string. Right now regexes are + # quoting the string, but ideally the json itself would be fixed. + # To allow for fixes without breaking this, the regex should only + # match unquoted strings, + a = re.compile(r':([a-zA-Z]*),') + puzzle_scores_json = re.sub(a, ':"\g<1>",', puzzle_scores_json) puzzle_scores = json.loads(puzzle_scores_json) responses.append(save_scores(request.user, puzzle_scores)) @@ -98,10 +107,22 @@ def save_scores(user, puzzle_scores): # BestScore (energy), CurrentScore (Energy), ScoreVersion (int) puzzle_id = score['PuzzleID'] + best_score = score['BestScore'] + 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) + + # TODO: get info from db instead? score_responses.append({'PuzzleID': puzzle_id, 'Status': 'Success'}) diff --git a/lms/templates/foldit.html b/lms/templates/foldit.html index 2c16ebbfeb..2460e25f8e 100644 --- a/lms/templates/foldit.html +++ b/lms/templates/foldit.html @@ -25,4 +25,21 @@ You have not yet gotten to level ${goal_level}. % endfor - \ No newline at end of file +
+ +

Puzzle Leaderboard

+ + + + + + + % for pair in top_scores: + + + + + % endfor +
UserScore
${pair[0]}${pair[1]}
+ +