""" Leaderboard step in the OpenAssessment XBlock. """ from xblock.core import XBlock from django.utils.translation import ugettext as _ from openassessment.assessment.errors import PeerAssessmentError, SelfAssessmentError from openassessment.fileupload import api as file_upload_api from openassessment.fileupload.exceptions import FileUploadError from openassessment.xblock.data_conversion import create_submission_dict class LeaderboardMixin(object): """Leaderboard Mixin introduces all handlers for displaying the leaderboard Abstracts all functionality and handlers associated with the Leaderboard. Leaderboard is a Mixin for the OpenAssessmentBlock. Functions in the Leaderboard call into the OpenAssessmentBlock functions and will not work outside of OpenAssessmentBlock. """ @XBlock.handler def render_leaderboard(self, data, suffix=''): """ Render the leaderboard. Args: data: Not used. Kwargs: suffix: Not used. Returns: unicode: HTML content of the leaderboard. """ # Import is placed here to avoid model import at project startup. from submissions import api as sub_api # Retrieve the status of the workflow. If no workflows have been # started this will be an empty dict, so status will be None. workflow = self.get_workflow_info() status = workflow.get('status') # Render the grading section based on the status of the workflow try: if status == "done": path, context = self.render_leaderboard_complete(self.get_student_item_dict()) else: # status is 'self' or 'peer', which implies that the workflow is incomplete path, context = self.render_leaderboard_incomplete() except (sub_api.SubmissionError, PeerAssessmentError, SelfAssessmentError): return self.render_error(_(u"An unexpected error occurred.")) else: return self.render_assessment(path, context) def render_leaderboard_complete(self, student_item_dict): """ Render the leaderboard complete state. Args: student_item_dict (dict): The student item Returns: template_path (string), tuple of context (dict) """ # Import is placed here to avoid model import at project startup. from submissions import api as sub_api # Retrieve top scores from the submissions API # Since this uses the read-replica and caches the results, # there will be some delay in the request latency. scores = sub_api.get_top_submissions( student_item_dict['course_id'], student_item_dict['item_id'], student_item_dict['item_type'], self.leaderboard_show ) for score in scores: score['files'] = [] if 'file_keys' in score['content']: file_keys = score['content'].get('file_keys', []) descriptions = score['content'].get('files_descriptions', []) for idx, key in enumerate(file_keys): file_download_url = self._get_file_download_url(key) if file_download_url: file_description = descriptions[idx] if idx < len(descriptions) else '' score['files'].append((file_download_url, file_description)) elif 'file_key' in score['content']: file_download_url = self._get_file_download_url(score['content']['file_key']) if file_download_url: score['files'].append((file_download_url, '')) if 'text' in score['content'] or 'parts' in score['content']: submission = {'answer': score.pop('content')} score['submission'] = create_submission_dict(submission, self.prompts) elif isinstance(score['content'], basestring): pass # Currently, we do not handle non-text submissions. else: score['submission'] = "" score.pop('content', None) context = {'topscores': scores, 'allow_latex': self.allow_latex, 'file_upload_type': self.file_upload_type, 'xblock_id': self.get_xblock_id()} return 'openassessmentblock/leaderboard/oa_leaderboard_show.html', context def render_leaderboard_incomplete(self): """ Render the grade incomplete state. Returns: template_path (string), tuple of context (dict) """ return 'openassessmentblock/leaderboard/oa_leaderboard_waiting.html', {'xblock_id': self.get_xblock_id()} def _get_file_download_url(self, file_key): """ Internal function for retrieving the download url at which the file that corresponds to the file_key can be downloaded. Arguments: file_key (string): Corresponding file key. Returns: file_download_url (string) or empty string in case of error. """ try: file_download_url = file_upload_api.get_download_url(file_key) except FileUploadError: file_download_url = '' return file_download_url