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]
Exemple #2
0
    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)
Exemple #4
0
 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
Exemple #5
0
    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}
Exemple #7
0
    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')
Exemple #8
0
    def get_stream(self, location):
        id = StaticContent.get_id_from_location(location)
        try:
            handle = self.fs.get(id)
        except NoFile:
            raise NotFoundError()

        return handle
Exemple #9
0
    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
Exemple #10
0
 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()
Exemple #11
0
    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
Exemple #12
0
    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
Exemple #13
0
    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')
Exemple #15
0
    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')
Exemple #16
0
    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)
Exemple #17
0
    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})
Exemple #18
0
    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
Exemple #20
0
    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})
Exemple #21
0
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)
Exemple #22
0
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')
Exemple #26
0
    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
Exemple #27
0
 def handle_ajax(self, dispatch, get):
     raise NotFoundError('Unexpected dispatch type')
Exemple #28
0
 def handle_ajax(self, _dispatch, _data):
     raise NotFoundError('Unexpected dispatch type')
Exemple #29
0
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]
Exemple #30
0
    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')