def get_transcript_from_contentstore(video, language, output_format, transcripts_info, youtube_id=None): """ Get video transcript from content store. Arguments: video (Video Descriptor): Video descriptor language (unicode): transcript language output_format (unicode): transcript output format transcripts_info (dict): transcript info for a video youtube_id (unicode): youtube video id Returns: tuple containing content, filename, mimetype """ input_format, base_name, transcript_content = None, None, None if output_format not in (Transcript.SRT, Transcript.SJSON, Transcript.TXT): raise NotFoundError('Invalid transcript format `{output_format}`'.format(output_format=output_format)) sub, other_languages = transcripts_info['sub'], transcripts_info['transcripts'] transcripts = dict(other_languages) # this is sent in case of a translation dispatch and we need to use it as our subs_id. possible_sub_ids = [youtube_id, sub, video.youtube_id_1_0] + get_html5_ids(video.html5_sources) for sub_id in possible_sub_ids: try: transcripts[u'en'] = sub_id input_format, base_name, transcript_content = get_transcript_for_video( video.location, subs_id=sub_id, file_name=transcripts[language], language=language ) break except (KeyError, NotFoundError): continue if transcript_content is None: raise NotFoundError('No transcript for `{lang}` language'.format( lang=language )) # add language prefix to transcript file only if language is not None language_prefix = '{}_'.format(language) if language else '' transcript_name = u'{}{}.{}'.format(language_prefix, base_name, output_format) transcript_content = Transcript.convert(transcript_content, input_format=input_format, output_format=output_format) if not transcript_content.strip(): raise NotFoundError('No transcript content') if youtube_id: youtube_ids = youtube_speed_dict(video) transcript_content = json.dumps( generate_subs(youtube_ids.get(youtube_id, 1), 1, json.loads(transcript_content)) ) return transcript_content, transcript_name, Transcript.mime_types[output_format]
def find(self, location, throw_on_not_found=True, as_stream=False): content_id = self.asset_db_key(location) fs_pointer = self.fs_files.find_one(content_id, fields={'_id': 1}) if fs_pointer is None: if throw_on_not_found: raise NotFoundError() else: return None content_id = fs_pointer['_id'] try: if as_stream: fp = self.fs.get(content_id) thumbnail_location = getattr(fp, 'thumbnail_location', None) if thumbnail_location: thumbnail_location = location.course_key.make_asset_key( 'thumbnail', thumbnail_location[4]) return StaticContentStream( location, fp.displayname, fp.content_type, fp, last_modified_at=fp.uploadDate, thumbnail_location=thumbnail_location, import_path=getattr(fp, 'import_path', None), length=fp.length, locked=getattr(fp, 'locked', False)) else: with self.fs.get(content_id) as fp: thumbnail_location = getattr(fp, 'thumbnail_location', None) if thumbnail_location: thumbnail_location = location.course_key.make_asset_key( 'thumbnail', thumbnail_location[4]) return StaticContent(location, fp.displayname, fp.content_type, fp.read(), last_modified_at=fp.uploadDate, thumbnail_location=thumbnail_location, import_path=getattr( fp, 'import_path', None), length=fp.length, locked=getattr(fp, 'locked', False)) except NoFile: if throw_on_not_found: raise NotFoundError() else: return None
def test_handle(self, store_mock, get_mock, del_mock, call_mock): pr.Command().handle(*self.args, **self.options_get) get_mock.assert_called_once_with( *[CourseLocator.from_string(arg) for arg in self.args]) pr.Command().handle(*self.args, **self.options_create) call_mock.assert_called_once_with('create_report_task', *['create'], **{'course_id': self.args[0]}) pr.Command().handle(*self.args, **self.options_delete) del_mock.assert_called_once_with( *[CourseLocator.from_string(arg) for arg in self.args]) msg = '^"course_id" is not specified$' with self.assertRaisesRegexp(CommandError, msg): pr.Command().handle(*[], **self.options_get) msg = '^Cannot specify "-c" option and "-d" option at the same time.$' with self.assertRaisesRegexp(CommandError, msg): pr.Command().handle(*self.args, **self.options_error) msg = '^CSV not found.$' with self.assertRaisesRegexp(CommandError, msg): get_mock.side_effect = NotFoundError() pr.Command().handle(*self.args, **self.options_get)
def find(self, location, throw_on_not_found=True, as_stream=False): id = StaticContent.get_id_from_location(location) try: if as_stream: fp = self.fs.get(id) return StaticContentStream( location, fp.displayname, fp.content_type, fp, last_modified_at=fp.uploadDate, thumbnail_location=fp.thumbnail_location if hasattr( fp, 'thumbnail_location') else None, import_path=fp.import_path if hasattr(fp, 'import_path') else None, length=fp.length) else: with self.fs.get(id) as fp: return StaticContent( location, fp.displayname, fp.content_type, fp.read(), last_modified_at=fp.uploadDate, thumbnail_location=fp.thumbnail_location if hasattr( fp, 'thumbnail_location') else None, import_path=fp.import_path if hasattr( fp, 'import_path') else None, length=fp.length) except NoFile: if throw_on_not_found: raise NotFoundError() else: return None
def test_get_pgreport_csv(self): gzipdata = gzip.GzipFile(fileobj=self.gzipfile, mode='wb') gzipdata.write("row1\nrow2\nrow3\n") gzipdata.close() scontent_mock = MagicMock() cstore_mock = MagicMock() content_mock = MagicMock() content_mock.stream_data.return_value = self.gzipfile.getvalue() cstore_mock.find.return_value = content_mock with nested( patch('pgreport.views.StaticContent', return_value=scontent_mock), patch('pgreport.views.contentstore', return_value=cstore_mock), patch('sys.stdout', new_callable=StringIO.StringIO) ) as (smock, cmock, stdmock): get_pgreport_csv(self.course.id) smock.compute_location.assert_called_once_with(ANY, "progress_students.csv.gz") cmock.assert_called_once_with() cmock.return_value.find.assert_called_once_with(ANY, throw_on_not_found=True, as_stream=True) content_mock.stream_data.assert_called_once_with() self.assertEquals(stdmock.getvalue(), 'row1\nrow2\nrow3\n') cstore_mock.find.side_effect = NotFoundError() with patch('pgreport.views.contentstore', return_value=cstore_mock): with self.assertRaises(NotFoundError): get_pgreport_csv(self.course.id)
def get_answer(self, data): """ For the "show answer" button. Returns the answers: {'answers' : answers} """ event_info = dict() event_info['problem_id'] = self.location.url() self.system.track_function('showanswer', event_info) if not self.answer_available(): raise NotFoundError('Answer is not available') else: answers = self.lcp.get_question_answers() self.set_state_from_lcp() # answers (eg <solution>) may have embedded images # but be careful, some problems are using non-string answer dicts new_answers = dict() for answer_id in answers: try: new_answer = { answer_id: self.system.replace_urls(answers[answer_id]) } except TypeError: log.debug( u'Unable to perform URL substitution on answers[%s]: %s', answer_id, answers[answer_id]) new_answer = {answer_id: answers[answer_id]} new_answers.update(new_answer) return {'answers': new_answers}
def handle_ajax(self, dispatch, data): """ Update values of xfields, that were changed by student. """ accepted_keys = [ 'speed', 'saved_video_position', 'transcript_language', 'transcript_download_format', 'youtube_is_available' ] conversions = { 'speed': json.loads, 'saved_video_position': RelativeTime.isotime_to_timedelta, 'youtube_is_available': json.loads, } if dispatch == 'save_user_state': for key in data: if hasattr(self, key) and key in accepted_keys: if key in conversions: value = conversions[key](data[key]) else: value = data[key] setattr(self, key, value) if key == 'speed': self.global_speed = self.speed return json.dumps({'success': True}) log.debug(u"GET {0}".format(data)) log.debug(u"DISPATCH {0}".format(dispatch)) raise NotFoundError('Unexpected dispatch type')
def get_stream(self, location): id = StaticContent.get_id_from_location(location) try: handle = self.fs.get(id) except NoFile: raise NotFoundError() return handle
def get_stream(self, location): content_id = self.asset_db_key(location) try: handle = self.fs.get(content_id) except NoFile: raise NotFoundError() return handle
def find(self, location): id = StaticContent.get_id_from_location(location) try: with self.fs.get(id) as fp: return StaticContent(location, fp.displayname, fp.content_type, fp.read(), fp.uploadDate, thumbnail_location=fp.thumbnail_location if hasattr(fp, 'thumbnail_location') else None, import_path=fp.import_path if hasattr(fp, 'import_path') else None) except NoFile: raise NotFoundError()
def get_stream(self, location): content_id = self.asset_db_key(location) fs_pointer = self.fs_files.find_one(content_id, fields={'_id': 1}) try: handle = self.fs.get(fs_pointer['_id']) except NoFile: raise NotFoundError() return handle
def find(self, location, throw_on_not_found=True, as_stream=False): content_id, __ = self.asset_db_key(location) try: if as_stream: fp = self.fs.get(content_id) # Need to replace dict IDs with SON for chunk lookup to work under Python 3 # because field order can be different and mongo cares about the order if isinstance(fp._id, dict): fp._file['_id'] = content_id thumbnail_location = getattr(fp, 'thumbnail_location', None) if thumbnail_location: thumbnail_location = location.course_key.make_asset_key( 'thumbnail', thumbnail_location[4]) return StaticContentStream( location, fp.displayname, fp.content_type, fp, last_modified_at=fp.uploadDate, thumbnail_location=thumbnail_location, import_path=getattr(fp, 'import_path', None), length=fp.length, locked=getattr(fp, 'locked', False), content_digest=getattr(fp, 'md5', None), ) else: with self.fs.get(content_id) as fp: # Need to replace dict IDs with SON for chunk lookup to work under Python 3 # because field order can be different and mongo cares about the order if isinstance(fp._id, dict): fp._file['_id'] = content_id thumbnail_location = getattr(fp, 'thumbnail_location', None) if thumbnail_location: thumbnail_location = location.course_key.make_asset_key( 'thumbnail', thumbnail_location[4]) return StaticContent( location, fp.displayname, fp.content_type, fp.read(), last_modified_at=fp.uploadDate, thumbnail_location=thumbnail_location, import_path=getattr(fp, 'import_path', None), length=fp.length, locked=getattr(fp, 'locked', False), content_digest=getattr(fp, 'md5', None), ) except NoFile: if throw_on_not_found: raise NotFoundError(content_id) else: return None
def get_attrs(self, location): """ Gets all of the attributes associated with the given asset. Note, returns even built in attrs such as md5 which you cannot resubmit in an update; so, don't call set_attrs with the result of this but only with the set of attrs you want to explicitly update. The attrs will be a superset of _id, contentType, chunkSize, filename, uploadDate, & md5 :param location: a c4x asset location """ item = self.fs_files.find_one(location_to_query(location)) if item is None: raise NotFoundError() return item
def handle_ajax(self, dispatch, data): """ Update values of xfields, that were changed by student. """ accepted_keys = [ 'speed', 'auto_advance', 'saved_video_position', 'transcript_language', 'transcript_download_format', 'youtube_is_available', 'bumper_last_view_date', 'bumper_do_not_show_again' ] conversions = { 'speed': json.loads, 'auto_advance': json.loads, 'saved_video_position': RelativeTime.isotime_to_timedelta, 'youtube_is_available': json.loads, 'bumper_last_view_date': to_boolean, 'bumper_do_not_show_again': to_boolean, } if dispatch == 'save_user_state': for key in data: if key in accepted_keys: if key in conversions: value = conversions[key](data[key]) else: value = data[key] if key == 'bumper_last_view_date': value = now() if key == 'speed' and math.isnan(value): message = u"Invalid speed value {}, must be a float.".format( value) log.warning(message) return json.dumps({'success': False, 'error': message}) setattr(self, key, value) if key == 'speed': self.global_speed = self.speed # lint-amnesty, pylint: disable=attribute-defined-outside-init return json.dumps({'success': True}) log.debug(u"GET {0}".format(data)) log.debug(u"DISPATCH {0}".format(dispatch)) raise NotFoundError('Unexpected dispatch type')
def handle_ajax(self, dispatch, data): ACCEPTED_KEYS = ['speed'] if dispatch == 'save_user_state': for key in data: if hasattr(self, key) and key in ACCEPTED_KEYS: setattr(self, key, json.loads(data[key])) if key == 'speed': self.global_speed = self.speed return json.dumps({'success': True}) log.debug(u"GET {0}".format(data)) log.debug(u"DISPATCH {0}".format(dispatch)) raise NotFoundError('Unexpected dispatch type')
def set_attrs(self, location, attr_dict): """ Like set_attr but sets multiple key value pairs. Returns nothing. Raises NotFoundError if no such item exists Raises AttributeError is attr_dict has any attrs which are one of the build in attrs. :param location: a c4x asset location """ for attr in attr_dict.iterkeys(): if attr in ['_id', 'md5', 'uploadDate', 'length']: raise AttributeError("{} is a protected attribute.".format(attr)) asset_db_key, __ = self.asset_db_key(location) # catch upsert error and raise NotFoundError if asset doesn't exist result = self.fs_files.update({'_id': asset_db_key}, {"$set": attr_dict}, upsert=False) if not result.get('updatedExisting', True): raise NotFoundError(asset_db_key)
def set_attrs(self, location, attr_dict): """ Like set_attr but sets multiple key value pairs. Returns nothing. Raises NotFoundError if no such item exists Raises AttributeError is attr_dict has any attrs which are one of the build in attrs. :param location: a c4x asset location """ for attr in attr_dict.iterkeys(): if attr in ['_id', 'md5', 'uploadDate', 'length']: raise AttributeError( "{} is a protected attribute.".format(attr)) item = self.fs_files.find_one(location_to_query(location)) if item is None: raise NotFoundError() self.fs_files.update({"_id": item["_id"]}, {"$set": attr_dict})
def handle_ajax(self, dispatch, data): accepted_keys = ['speed', 'saved_video_position', 'transcript_language'] if dispatch == 'save_user_state': for key in data: if hasattr(self, key) and key in accepted_keys: if key == 'saved_video_position': relative_position = RelativeTime.isotime_to_timedelta(data[key]) self.saved_video_position = relative_position else: setattr(self, key, json.loads(data[key])) if key == 'speed': self.global_speed = self.speed return json.dumps({'success': True}) log.debug(u"GET {0}".format(data)) log.debug(u"DISPATCH {0}".format(dispatch)) raise NotFoundError('Unexpected dispatch type')
def get_transcript_from_val(edx_video_id, lang=None, output_format=Transcript.SRT): """ Get video transcript from edx-val. Arguments: edx_video_id (unicode): video identifier lang (unicode): transcript language output_format (unicode): transcript output format Returns: tuple containing content, filename, mimetype """ transcript = get_video_transcript_content(edx_video_id, lang) if not transcript: raise NotFoundError(u'Transcript not found for {}, lang: {}'.format(edx_video_id, lang)) transcript_conversion_props = dict(transcript, output_format=output_format) transcript = convert_video_transcript(**transcript_conversion_props) filename = transcript['filename'] content = transcript['content'] mimetype = Transcript.mime_types[output_format] return content, filename, mimetype
def set_attrs(self, location, attr_dict): """ Like set_attr but sets multiple key value pairs. Returns nothing. Raises NotFoundError if no such item exists Raises AttributeError is attr_dict has any attrs which are one of the build in attrs. :param location: a c4x asset location """ for attr in attr_dict.iterkeys(): if attr in ['_id', 'md5', 'uploadDate', 'length']: raise AttributeError( "{} is a protected attribute.".format(attr)) asset_db_key = self.asset_db_key(location) # FIXME remove fetch and use a form of update which fails if doesn't exist item = self.fs_files.find_one(asset_db_key) if item is None: raise NotFoundError(asset_db_key) self.fs_files.update(asset_db_key, {"$set": attr_dict})
def get_transcript(video, lang=None, output_format=Transcript.SRT, youtube_id=None): """ Get video transcript from edx-val or content store. Arguments: video (Video Descriptor): Video Descriptor lang (unicode): transcript language output_format (unicode): transcript output format youtube_id (unicode): youtube video id Returns: tuple containing content, filename, mimetype """ transcripts_info = video.get_transcripts_info() if not lang: lang = video.get_default_transcript_language(transcripts_info) try: edx_video_id = clean_video_id(video.edx_video_id) if not edx_video_id: raise NotFoundError return get_transcript_from_val(edx_video_id, lang, output_format) except NotFoundError: # If this is not in a modulestore course or library, don't try loading from contentstore: if not isinstance(video.scope_ids.usage_id.course_key, (CourseLocator, LibraryLocator)): raise NotFoundError( u'Video transcripts cannot yet be loaded from Blockstore (block: {})' .format(video.scope_ids.usage_id), ) return get_transcript_from_contentstore( video, lang, youtube_id=youtube_id, output_format=output_format, transcripts_info=transcripts_info)
def get_transcript_from_blockstore(video_block, language, output_format, transcripts_info): """ Get video transcript from Blockstore. Blockstore expects video transcripts to be placed into the 'static/' subfolder of the XBlock's folder in a Blockstore bundle. For example, if the video XBlock's definition is in the standard location of video/video1/definition.xml Then the .srt files should be placed at e.g. video/video1/static/video1-en.srt This is the same place where other public static files are placed for other XBlocks, such as image files used by HTML blocks. Video XBlocks in Blockstore must set the 'transcripts' XBlock field to a JSON dictionary listing the filename of the transcript for each language: <video youtube_id_1_0="3_yD_cEKoCk" transcripts='{"en": "3_yD_cEKoCk-en.srt"}' display_name="Welcome Video with Transcript" download_track="true" /> This method is tested in openedx/core/djangoapps/content_libraries/tests/test_static_assets.py Arguments: video_block (Video XBlock): The video XBlock language (str): transcript language output_format (str): transcript output format transcripts_info (dict): transcript info for a video, from video_block.get_transcripts_info() Returns: tuple containing content, filename, mimetype """ if output_format not in (Transcript.SRT, Transcript.SJSON, Transcript.TXT): raise NotFoundError( 'Invalid transcript format `{output_format}`'.format( output_format=output_format)) transcripts = transcripts_info['transcripts'] if language not in transcripts: raise NotFoundError( "Video {} does not have a transcript file defined for the '{}' language in its OLX." .format( video_block.scope_ids.usage_id, language, )) filename = transcripts[language] if not filename.endswith('.srt'): # We want to standardize on .srt raise NotFoundError( "Video XBlocks in Blockstore only support .srt transcript files.") # Try to load the transcript file out of Blockstore # In lieu of an XBlock API for this (like block.runtime.resources_fs), we use the blockstore API directly. bundle_uuid = video_block.scope_ids.def_id.bundle_uuid path = video_block.scope_ids.def_id.olx_path.rpartition( '/')[0] + '/static/' + filename bundle_version = video_block.scope_ids.def_id.bundle_version # Either bundle_version or draft_name will be set. draft_name = video_block.scope_ids.def_id.draft_name try: content_binary = blockstore_cache.get_bundle_file_data_with_cache( bundle_uuid, path, bundle_version, draft_name) except blockstore_api.BundleFileNotFound: raise NotFoundError( "Transcript file '{}' missing for video XBlock {}".format( path, video_block.scope_ids.usage_id, )) # Now convert the transcript data to the requested format: filename_no_extension = os.path.splitext(filename)[0] output_filename = '{}.{}'.format(filename_no_extension, output_format) output_transcript = Transcript.convert( content_binary.decode('utf-8'), input_format=Transcript.SRT, output_format=output_format, ) if not output_transcript.strip(): raise NotFoundError('No transcript content') return output_transcript, output_filename, Transcript.mime_types[ output_format]
def check_problem(self, data): """ Checks whether answers to a problem are correct Returns a map of correct/incorrect answers: {'success' : 'correct' | 'incorrect' | AJAX alert msg string, 'contents' : html} """ event_info = dict() event_info['state'] = self.lcp.get_state() event_info['problem_id'] = self.location.url() answers = self.make_dict_of_responses(data) event_info['answers'] = convert_files_to_filenames(answers) # Too late. Cannot submit if self.closed(): event_info['failure'] = 'closed' self.system.track_function('problem_check_fail', event_info) raise NotFoundError('Problem is closed') # Problem submitted. Student should reset before checking again if self.done and self.rerandomize == "always": event_info['failure'] = 'unreset' self.system.track_function('problem_check_fail', event_info) raise NotFoundError( 'Problem must be reset before it can be checked again') # Problem queued. Students must wait a specified waittime before they are allowed to submit if self.lcp.is_queued(): current_time = datetime.datetime.now(UTC()) prev_submit_time = self.lcp.get_recentmost_queuetime() waittime_between_requests = self.system.xqueue['waittime'] if (current_time - prev_submit_time ).total_seconds() < waittime_between_requests: msg = u'You must wait at least {wait} seconds between submissions'.format( wait=waittime_between_requests) return { 'success': msg, 'html': '' } # Prompts a modal dialog in ajax callback try: correct_map = self.lcp.grade_answers(answers) self.attempts = self.attempts + 1 self.lcp.done = True self.set_state_from_lcp() except (StudentInputError, ResponseError, LoncapaProblemError) as inst: log.warning("StudentInputError in capa_module:problem_check", exc_info=True) # If the user is a staff member, include # the full exception, including traceback, # in the response if self.system.user_is_staff: msg = u"Staff debug info: {tb}".format( tb=cgi.escape(traceback.format_exc())) # Otherwise, display just an error message, # without a stack trace else: msg = u"Error: {msg}".format(msg=inst.message) return {'success': msg} except Exception as err: if self.system.DEBUG: msg = u"Error checking problem: {}".format(err.message) msg += u'\nTraceback:\n{}'.format(traceback.format_exc()) return {'success': msg} raise self.publish_grade() # success = correct if ALL questions in this problem are correct success = 'correct' for answer_id in correct_map: if not correct_map.is_correct(answer_id): success = 'incorrect' # NOTE: We are logging both full grading and queued-grading submissions. In the latter, # 'success' will always be incorrect event_info['correct_map'] = correct_map.get_dict() event_info['success'] = success event_info['attempts'] = self.attempts self.system.track_function('problem_check', event_info) if hasattr(self.system, 'psychometrics_handler' ): # update PsychometricsData using callback self.system.psychometrics_handler(self.get_state_for_lcp()) # render problem into HTML html = self.get_problem_html(encapsulate=False) return { 'success': success, 'contents': html, }
def rescore_problem(self): """ Checks whether the existing answers to a problem are correct. This is called when the correct answer to a problem has been changed, and the grade should be re-evaluated. Returns a dict with one key: {'success' : 'correct' | 'incorrect' | AJAX alert msg string } Raises NotFoundError if called on a problem that has not yet been answered, or NotImplementedError if it's a problem that cannot be rescored. Returns the error messages for exceptions occurring while performing the rescoring, rather than throwing them. """ event_info = { 'state': self.lcp.get_state(), 'problem_id': self.location.url() } if not self.lcp.supports_rescoring(): event_info['failure'] = 'unsupported' self.system.track_function('problem_rescore_fail', event_info) raise NotImplementedError( "Problem's definition does not support rescoring") if not self.done: event_info['failure'] = 'unanswered' self.system.track_function('problem_rescore_fail', event_info) raise NotFoundError( 'Problem must be answered before it can be graded again') # get old score, for comparison: orig_score = self.lcp.get_score() event_info['orig_score'] = orig_score['score'] event_info['orig_total'] = orig_score['total'] try: correct_map = self.lcp.rescore_existing_answers() except (StudentInputError, ResponseError, LoncapaProblemError) as inst: log.warning("Input error in capa_module:problem_rescore", exc_info=True) event_info['failure'] = 'input_error' self.system.track_function('problem_rescore_fail', event_info) return {'success': u"Error: {0}".format(inst.message)} except Exception as err: event_info['failure'] = 'unexpected' self.system.track_function('problem_rescore_fail', event_info) if self.system.DEBUG: msg = u"Error checking problem: {0}".format(err.message) msg += u'\nTraceback:\n' + traceback.format_exc() return {'success': msg} raise # rescoring should have no effect on attempts, so don't # need to increment here, or mark done. Just save. self.set_state_from_lcp() self.publish_grade() new_score = self.lcp.get_score() event_info['new_score'] = new_score['score'] event_info['new_total'] = new_score['total'] # success = correct if ALL questions in this problem are correct success = 'correct' for answer_id in correct_map: if not correct_map.is_correct(answer_id): success = 'incorrect' # NOTE: We are logging both full grading and queued-grading submissions. In the latter, # 'success' will always be incorrect event_info['correct_map'] = correct_map.get_dict() event_info['success'] = success event_info['attempts'] = self.attempts self.system.track_function('problem_rescore', event_info) # psychometrics should be called on rescoring requests in the same way as check-problem if hasattr(self.system, 'psychometrics_handler' ): # update PsychometricsData using callback self.system.psychometrics_handler(self.get_state_for_lcp()) return {'success': success}
def handle_ajax(self, dispatch, data): # TODO: bounds checking ''' get = request.POST instance ''' if dispatch == 'goto_position': self.position = int(data['position']) return json.dumps({'success': True}) raise NotFoundError('Unexpected dispatch type')
def check_problem(self, get): ''' Checks whether answers to a problem are correct, and returns a map of correct/incorrect answers: {'success' : 'correct' | 'incorrect' | AJAX alert msg string, 'contents' : html} ''' event_info = dict() event_info['state'] = self.lcp.get_state() event_info['problem_id'] = self.location.url() answers = self.make_dict_of_responses(get) event_info['answers'] = convert_files_to_filenames(answers) # Too late. Cannot submit if self.closed(): event_info['failure'] = 'closed' self.system.track_function('save_problem_check_fail', event_info) raise NotFoundError('Problem is closed') # Problem submitted. Student should reset before checking again if self.done and self.rerandomize == "always": event_info['failure'] = 'unreset' self.system.track_function('save_problem_check_fail', event_info) raise NotFoundError( 'Problem must be reset before it can be checked again') # Problem queued. Students must wait a specified waittime before they are allowed to submit if self.lcp.is_queued(): current_time = datetime.datetime.now(UTC()) prev_submit_time = self.lcp.get_recentmost_queuetime() waittime_between_requests = self.system.xqueue['waittime'] if (current_time - prev_submit_time ).total_seconds() < waittime_between_requests: msg = 'You must wait at least %d seconds between submissions' % waittime_between_requests return { 'success': msg, 'html': '' } # Prompts a modal dialog in ajax callback try: correct_map = self.lcp.grade_answers(answers) self.set_state_from_lcp() except (StudentInputError, ResponseError, LoncapaProblemError) as inst: log.warning("StudentInputError in capa_module:problem_check", exc_info=True) # If the user is a staff member, include # the full exception, including traceback, # in the response if self.system.user_is_staff: msg = "Staff debug info: %s" % traceback.format_exc() # Otherwise, display just an error message, # without a stack trace else: msg = "Error: %s" % str(inst.message) return {'success': msg} except Exception, err: if self.system.DEBUG: msg = "Error checking problem: " + str(err) msg += '\nTraceback:\n' + traceback.format_exc() return {'success': msg} raise
def handle_ajax(self, dispatch, get): raise NotFoundError('Unexpected dispatch type')
def handle_ajax(self, _dispatch, _data): raise NotFoundError('Unexpected dispatch type')
def get_transcript_from_contentstore(video, language, output_format, youtube_id=None, is_bumper=False): """ Get video transcript from content store. Arguments: video (Video Descriptor): Video descriptor language (unicode): transcript language output_format (unicode): transcript output format youtube_id (unicode): youtube video id is_bumper (bool): indicates bumper video Returns: tuple containing content, filename, mimetype """ if output_format not in (Transcript.SRT, Transcript.SJSON, Transcript.TXT): raise NotFoundError( 'Invalid transcript format `{output_format}`'.format( output_format=output_format)) transcripts_info = video.get_transcripts_info(is_bumper=is_bumper) sub, other_languages = transcripts_info['sub'], transcripts_info[ 'transcripts'] transcripts = dict(other_languages) # this is sent in case of a translation dispatch and we need to use it as our subs_id. if youtube_id: transcripts['en'] = youtube_id elif sub: transcripts['en'] = sub elif video.youtube_id_1_0: transcripts['en'] = video.youtube_id_1_0 elif language == u'en': raise NotFoundError('No transcript for `en` language') try: input_format, base_name, transcript_content = get_transcript_for_video( video.location, subs_id=transcripts['en'], file_name=language and transcripts[language], language=language) except KeyError: raise NotFoundError # add language prefix to transcript file only if language is not None language_prefix = '{}_'.format(language) if language else '' transcript_name = u'{}{}.{}'.format(language_prefix, base_name, output_format) transcript_content = Transcript.convert(transcript_content, input_format=input_format, output_format=output_format) if not transcript_content.strip(): raise NotFoundError('No transcript content') if youtube_id: youtube_ids = youtube_speed_dict(video) transcript_content = json.dumps( generate_subs(youtube_ids.get(youtube_id, 1), 1, json.loads(transcript_content))) return transcript_content, transcript_name, Transcript.mime_types[ output_format]
def handle_ajax(self, dispatch, data): """ Update values of xfields, that were changed by student. """ from openedx.core.djangoapps.video_analytics.views import create_video_analytics_record accepted_keys = [ 'speed', 'auto_advance', 'saved_video_position', 'transcript_language', 'transcript_download_format', 'youtube_is_available', 'bumper_last_view_date', 'bumper_do_not_show_again' ] conversions = { 'speed': json.loads, 'auto_advance': json.loads, 'saved_video_position': RelativeTime.isotime_to_timedelta, 'youtube_is_available': json.loads, 'bumper_last_view_date': to_boolean, 'bumper_do_not_show_again': to_boolean, } if dispatch == 'save_user_state': for key in data: if key in accepted_keys: if key in conversions: value = conversions[key](data[key]) else: value = data[key] if key == 'bumper_last_view_date': value = now() if key == 'speed' and math.isnan(value): message = u"Invalid speed value {}, must be a float.".format( value) log.warning(message) return json.dumps({'success': False, 'error': message}) setattr(self, key, value) if key == 'speed': self.global_speed = self.speed log.info("start vide analytics record code") user_id = self.runtime.user_id course_id = str(self.runtime.course_id) if self.html5_sources: video_path = str(self.html5_sources[0]) elif self.youtube_id_1_0: video_path = self.youtube_id_1_0 elif self.youtube_id_1_25: video_path = self.youtube_id_1_25 elif self.youtube_id_1_5: video_path = self.youtube_id_1_5 elif self.youtube_id_0_75: video_path = self.youtube_id_0_75 else: video_path = '' watch_min = round(self.saved_video_position.total_seconds() / 60, 2) block_id = str(self.parent.block_id) start_date = datetime.datetime.now() total_length = 0.0 log.info("video analytics record will create") create_video_analytics_record(user_id, course_id, video_path, watch_min, block_id, start_date, total_length) log.info("video analytics record created") return json.dumps({'success': True}) log.debug(u"GET {0}".format(data)) log.debug(u"DISPATCH {0}".format(dispatch)) raise NotFoundError('Unexpected dispatch type')