def test_file_descriptor_tuples_after_sharing_with_old_team( mock_get_download_url, mock_remove_file, shared_file_upload_fixture, mock_block ): # Include a deleted file entry, and later assert that we have an empty FileDescriptor # record returned by ``file_descriptor_tuples()`` block = mock_block( descriptions=['The first file', 'The deleted file', 'The second file'], names=['File A', 'File that is deleted', 'File B'], sizes=[22, 666, 44], ) block.team.team_id = DEFAULT_TEAM_ID block.is_team_assignment.return_value = True student_item_dict = { 'student_id': DEFAULT_OWNER_ID, 'course_id': DEFAULT_COURSE_ID, 'item_id': DEFAULT_ITEM_ID, } key_a = api.get_student_file_key(student_item_dict, index=0) key_deleted = api.get_student_file_key(student_item_dict, index=1) key_b = api.get_student_file_key(student_item_dict, index=2) # create a shared upload that was shared with an old team _ = shared_file_upload_fixture(team_id='an-old-team', file_key=key_a) # create one for the file we're going to delete _ = shared_file_upload_fixture(team_id=block.team.team_id, file_key=key_deleted) # create a shared upload that's shared with the learner's current team _ = shared_file_upload_fixture(team_id=block.team.team_id, file_key=key_b) file_manager = api.FileUploadManager(block) # go and delete the file we want to delete file_manager.delete_upload(1) # team_file_descriptor_tuples() should only give back a record for the upload shared with the current team actual_descriptors = file_manager.file_descriptor_tuples(include_deleted=True) expected_descriptors = [ api.FileDescriptor( download_url=None, name=None, description=None, show_delete_button=False, ), api.FileDescriptor( download_url=mock_get_download_url.return_value, name='File B', description='The second file', show_delete_button=True, ), ] assert expected_descriptors == actual_descriptors mock_get_download_url.assert_called_once_with(key_b) mock_remove_file.assert_called_once_with(key_deleted)
def get_download_urls_from_submission(self, submission): """ Returns a download URLs for retrieving content within a submission. Args: submission (dict): Dictionary containing an answer and a file_keys. The file_keys is used to try and retrieve a download urls with related content Returns: List with URLs to related content. If there is no content related to this key, or if there is no key for the submission, returns an empty list. """ urls = [] if 'file_keys' in submission['answer']: file_keys = submission['answer'].get('file_keys', []) descriptions = submission['answer'].get('files_descriptions', []) file_names = submission['answer'].get('files_name', submission['answer'].get('files_names', [])) for idx, key in enumerate(file_keys): file_download_url = self._get_url_by_file_key(key) if file_download_url: file_description = descriptions[idx].strip() if idx < len(descriptions) else '' try: file_name = file_names[idx].strip() if idx < len(file_names) else '' except AttributeError: file_name = '' logger.error('descriptions[idx] is None in {}'.format(submission)) urls.append( file_upload_api.FileDescriptor( download_url=file_download_url, description=file_description, name=file_name, show_delete_button=False )._asdict() ) elif 'file_key' in submission['answer']: key = submission['answer'].get('file_key', '') file_download_url = self._get_url_by_file_key(key) if file_download_url: urls.append( file_upload_api.FileDescriptor( download_url=file_download_url, description='', name='', show_delete_button=False )._asdict() ) return urls
def get_download_urls_from_submission(cls, submission): """ Returns a download URLs for retrieving content within a submission. Args: submission (dict): Dictionary containing an answer and a file_keys. The file_keys is used to try and retrieve a download urls with related content Returns: List with URLs to related content. If there is no content related to this key, or if there is no key for the submission, returns an empty list. """ urls = [] raw_answer = submission.get('answer') answer = OraSubmissionAnswerFactory.parse_submission_raw_answer( raw_answer) for file_upload in answer.get_file_uploads(missing_blank=True): file_download_url = cls._get_url_by_file_key(file_upload.key) if file_download_url: urls.append( file_upload_api.FileDescriptor( download_url=file_download_url, description=file_upload.description, name=file_upload.name, show_delete_button=False)._asdict()) return urls
def test_file_descriptor_tuples_no_team(mock_block): block = mock_block( descriptions=['The first file', 'The second file'], names=['File A', 'File B'], sizes=[22, 44], ) block.is_team_assignment.return_value = False file_manager = api.FileUploadManager(block) actual_descriptors = file_manager.file_descriptor_tuples() expected_descriptors = [ api.FileDescriptor(download_url='', name='File A', description='The first file', show_delete_button=True), api.FileDescriptor(download_url='', name='File B', description='The second file', show_delete_button=True), ] assert expected_descriptors == actual_descriptors
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: raw_score_content_answer = score['content'] answer = OraSubmissionAnswerFactory.parse_submission_raw_answer( raw_score_content_answer) score['files'] = [] for uploaded_file in answer.get_file_uploads(missing_blank=True): file_download_url = self._get_file_download_url( uploaded_file.key) if file_download_url: score['files'].append( file_upload_api.FileDescriptor( download_url=file_download_url, description=uploaded_file.description, name=uploaded_file.name, size=uploaded_file.size, show_delete_button=False)._asdict()) 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'], str): pass # Currently, we do not handle non-text submissions. else: score['submission'] = "" score.pop('content', None) context = { 'topscores': scores, 'allow_multiple_files': self.allow_multiple_files, 'allow_latex': self.allow_latex, 'prompts_type': self.prompts_type, 'file_upload_type': self.file_upload_type, 'xblock_id': self.get_xblock_id() } return 'openassessmentblock/leaderboard/oa_leaderboard_show.html', context
def get_files_info_from_user_state(self, username): """ Returns the files information from the user state for a given username. If the files information is present in the user state, return a list of following tuple: (file_download_url, file_description, file_name) Arguments: username(str): user's name whose state is being check for files information. Returns: List of files information tuple, if present, else empty list. """ files_info = [] user_state = self.get_user_state(username) item_dict = self.get_student_item_dict_from_username_or_email(username) if 'saved_files_descriptions' in user_state: # pylint: disable=protected-access files_descriptions = file_upload_api._safe_load_json_list( user_state.get('saved_files_descriptions'), log_error=True ) files_names = file_upload_api._safe_load_json_list( user_state.get('saved_files_names', '[]'), log_error=True ) for index, description in enumerate(files_descriptions): file_key = file_upload_api.get_student_file_key(item_dict, index) download_url = self._get_url_by_file_key(file_key) if download_url: file_name = files_names[index] if index < len(files_names) else '' files_info.append( file_upload_api.FileDescriptor( download_url=download_url, description=description, name=file_name, show_delete_button=False )._asdict() ) else: # If file has been removed, the URL doesn't exist logger.info( "URLWorkaround: no URL for description %s & key %s for user:%s", description, username, file_key ) continue return files_info
def get_all_upload_urls_for_user(self, username_or_email): """ For a particular ORA block, get the download URLs for all the files uploaded and still present. Used for an extreme edge case, where the stored files indices are out of sync with the uploaded files, this is a last resort to get the download URLs of all the files that have been uploaded by a learner in an ORA block(and haven't been deleted from the storage). Starting from 0 index to maximum file upload count possible, this checks if a file exists against every index. If present, add the info, else repeat it for the next indices. Arguments: username_or_email(str): username or email of the learner whose files' information is to be obtained. Returns: List of 3-valued tuples, with first value being file URL and other two values as empty string. The other 2 values have to be appended to work properly in the template. """ file_uploads = [] student_item_dict = self.get_student_item_dict_from_username_or_email(username_or_email) for index in range(self.MAX_FILES_COUNT): file_key = file_upload_api.get_student_file_key(student_item_dict, index) download_url = '' try: download_url = file_upload_api.get_download_url(file_key) except FileUploadError: pass if download_url: logger.info( "Download URL exists for key %s in block %s for user %s", file_key, username_or_email, str(self.location) ) file_uploads.append( file_upload_api.FileDescriptor( download_url=download_url, description='', name='', show_delete_button=False )._asdict() ) else: continue return file_uploads
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', []) file_names = score['content'].get('files_name', []) 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 '' file_name = file_names[idx] if idx < len(file_names) else '' score['files'].append( file_upload_api.FileDescriptor( download_url=file_download_url, description=file_description, name=file_name, show_delete_button=False )._asdict() ) 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_upload_api.FileDescriptor( download_url=file_download_url, description='', name='', show_delete_button=False )._asdict() ) 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'], str): pass # Currently, we do not handle non-text submissions. else: score['submission'] = "" score.pop('content', None) context = {'topscores': scores, 'allow_latex': self.allow_latex, 'prompts_type': self.prompts_type, 'file_upload_type': self.file_upload_type, 'xblock_id': self.get_xblock_id()} return 'openassessmentblock/leaderboard/oa_leaderboard_show.html', context