Beispiel #1
0
    def test_get_status(self):
        """Test the verification statuses of a user for a given 'checkpoint'
        and 'course_id'.
        """
        reverification_service = ReverificationService()
        self.assertIsNone(
            reverification_service.get_status(self.user.id,
                                              unicode(self.course_key),
                                              self.final_checkpoint_location))

        checkpoint_obj = VerificationCheckpoint.objects.create(
            course_id=unicode(self.course_key),
            checkpoint_location=self.final_checkpoint_location)
        VerificationStatus.objects.create(checkpoint=checkpoint_obj,
                                          user=self.user,
                                          status='submitted')
        self.assertEqual(
            reverification_service.get_status(self.user.id,
                                              unicode(self.course_key),
                                              self.final_checkpoint_location),
            'submitted')

        VerificationStatus.objects.create(checkpoint=checkpoint_obj,
                                          user=self.user,
                                          status='approved')
        self.assertEqual(
            reverification_service.get_status(self.user.id,
                                              unicode(self.course_key),
                                              self.final_checkpoint_location),
            'approved')
Beispiel #2
0
    def test_declined_verification_on_skip(self):
        """Test that status with value 'declined' is added in credit
        requirement status model when a user skip's an ICRV.
        """
        reverification_service = ReverificationService()
        checkpoint = VerificationCheckpoint.objects.create(
            course_id=unicode(self.course_id),
            checkpoint_location=self.final_checkpoint_location
        )
        # Create credit course and set credit requirements.
        CreditCourse.objects.create(course_key=self.course_key, enabled=True)
        set_credit_requirements(
            self.course_key,
            [
                {
                    "namespace": "reverification",
                    "name": checkpoint.checkpoint_location,
                    "display_name": "Assessment 1",
                    "criteria": {},
                }
            ]
        )

        reverification_service.skip_verification(self.user.id, unicode(self.course_id), self.final_checkpoint_location)
        requirement_status = get_credit_requirement_status(
            self.course_key, self.user.username, 'reverification', checkpoint.checkpoint_location
        )
        self.assertEqual(SkippedReverification.objects.filter(user=self.user, course_id=self.course_id).count(), 1)
        self.assertEqual(len(requirement_status), 1)
        self.assertEqual(requirement_status[0].get('name'), checkpoint.checkpoint_location)
        self.assertEqual(requirement_status[0].get('status'), 'declined')
Beispiel #3
0
    def test_get_attempts(self):
        """
        Check verification attempts count against a user for a given
        'checkpoint' and 'course_id'.
        """
        checkpoint_name = 'final_term'
        reverification_service = ReverificationService()
        course_id = unicode(self.course_key)
        self.assertEqual(
            reverification_service.get_attempts(self.user.id,
                                                course_id,
                                                checkpoint_name,
                                                location_id=None), 0)

        # now create a checkpoint and add user's entry against it then test
        # that the 'get_attempts' service method returns count accordingly
        checkpoint_obj = VerificationCheckpoint.objects.create(
            course_id=course_id, checkpoint_name=checkpoint_name)
        VerificationStatus.objects.create(checkpoint=checkpoint_obj,
                                          user=self.user,
                                          status='submitted')
        self.assertEqual(
            reverification_service.get_attempts(self.user.id,
                                                course_id,
                                                checkpoint_name,
                                                location_id=None), 1)
Beispiel #4
0
    def test_get_status(self):
        """
        Test the verification statuses of a user for a given 'checkpoint' and
        'course_id'.
        """
        checkpoint_name = 'final_term'
        reverification_service = ReverificationService()
        self.assertIsNone(reverification_service.get_status(self.user.id, unicode(self.course_key), checkpoint_name))

        checkpoint_obj = VerificationCheckpoint.objects.create(
            course_id=unicode(self.course_key),
            checkpoint_name=checkpoint_name
        )

        VerificationStatus.objects.create(checkpoint=checkpoint_obj, user=self.user, status='submitted')
        self.assertEqual(
            reverification_service.get_status(self.user.id, unicode(self.course_key), checkpoint_name),
            'submitted'
        )

        VerificationStatus.objects.create(checkpoint=checkpoint_obj, user=self.user, status='submitted')
        self.assertEqual(
            reverification_service.get_status(self.user.id, unicode(self.course_key), checkpoint_name),
            'submitted'
        )
Beispiel #5
0
    def test_not_in_verified_track(self):
        # No longer enrolled in a verified track
        self.enrollment.update_enrollment(mode=CourseMode.HONOR)

        # Should be marked as "skipped" (opted out)
        service = ReverificationService()
        status = service.get_status(self.user.id, unicode(self.course_key), self.final_checkpoint_location)
        self.assertEqual(status, service.NON_VERIFIED_TRACK)
Beispiel #6
0
    def test_not_in_verified_track(self):
        # No longer enrolled in a verified track
        self.enrollment.update_enrollment(mode=CourseMode.HONOR)

        # Should be marked as "skipped" (opted out)
        service = ReverificationService()
        status = service.get_status(self.user.id, unicode(self.course_id), self.final_checkpoint_location)
        self.assertEqual(status, service.NON_VERIFIED_TRACK)
    def test_get_status(self):
        """ Check if the user has any verification attempt for the checkpoint and course_id """

        checkpoint_name = "final_term"
        rev = ReverificationService()
        self.assertIsNone(rev.get_status(self.user.id, unicode(self.course_key), checkpoint_name))
        checkpoint_obj = VerificationCheckpoint.objects.create(
            course_id=unicode(self.course_key), checkpoint_name=checkpoint_name
        )

        VerificationStatus.objects.create(checkpoint=checkpoint_obj, user=self.user, status="submitted")
        self.assertEqual(rev.get_status(self.user.id, unicode(self.course_key), checkpoint_name), "submitted")
Beispiel #8
0
    def test_get_status(self):
        """ Check if the user has any verification attempt for the checkpoint and course_id """

        checkpoint_name = 'final_term'
        rev = ReverificationService()
        self.assertIsNone(rev.get_status(self.user.id, unicode(self.course_key), checkpoint_name))
        checkpoint_obj = VerificationCheckpoint.objects.create(
            course_id=unicode(self.course_key), checkpoint_name=checkpoint_name
        )

        VerificationStatus.objects.create(checkpoint=checkpoint_obj, user=self.user, status='submitted')
        self.assertEqual(rev.get_status(self.user.id, unicode(self.course_key), checkpoint_name), 'submitted')
    def test_start_verification(self, checkpoint_name):
        """Testing start verification service. If checkpoint exists for specific course then returns the checkpoint
        otherwise created that checkpoint.
        """

        rev = ReverificationService()
        rev.start_verification(unicode(self.course_key), checkpoint_name, self.item.location)
        expected_url = ("/verify_student/reverify" "/{course_key}" "/{checkpoint_name}" "/{usage_id}/").format(
            course_key=unicode(self.course_key), checkpoint_name=checkpoint_name, usage_id=self.item.location
        )

        self.assertEqual(
            expected_url, rev.start_verification(unicode(self.course_key), checkpoint_name, self.item.location)
        )
Beispiel #10
0
    def test_start_verification(self, checkpoint_name):
        """Testing start verification service. If checkpoint exists for specific course then returns the checkpoint
        otherwise created that checkpoint.
        """

        rev = ReverificationService()
        rev.start_verification(unicode(self.course_key), checkpoint_name, self.item.location)
        expected_url = (
            '/verify_student/reverify'
            '/{course_key}'
            '/{checkpoint_name}'
            '/{usage_id}/'
        ).format(course_key=unicode(self.course_key), checkpoint_name=checkpoint_name, usage_id=self.item.location)

        self.assertEqual(
            expected_url, rev.start_verification(unicode(self.course_key), checkpoint_name, self.item.location)
        )
Beispiel #11
0
    def test_skip_verification(self):
        """
        Test adding skip attempt of a user for a reverification checkpoint.
        """
        reverification_service = ReverificationService()
        VerificationCheckpoint.objects.create(
            course_id=unicode(self.course_key),
            checkpoint_location=self.final_checkpoint_location)

        reverification_service.skip_verification(
            self.user.id, unicode(self.course_key),
            self.final_checkpoint_location)
        self.assertEqual(
            SkippedReverification.objects.filter(
                user=self.user, course_id=self.course_key).count(), 1)

        # now test that a user can have only one entry for a skipped
        # reverification for a course
        reverification_service.skip_verification(
            self.user.id, unicode(self.course_key),
            self.final_checkpoint_location)
        self.assertEqual(
            SkippedReverification.objects.filter(
                user=self.user, course_id=self.course_key).count(), 1)

        # testing service for skipped attempt.
        self.assertEqual(
            reverification_service.get_status(self.user.id,
                                              unicode(self.course_key),
                                              self.final_checkpoint_location),
            'skipped')
Beispiel #12
0
    def test_start_verification(self, checkpoint_name):
        """Test the 'start_verification' service method.

        Check that if a reverification checkpoint exists for a specific course
        then 'start_verification' method returns that checkpoint otherwise it
        creates that checkpoint.
        """
        reverification_service = ReverificationService()
        checkpoint_location = u'i4x://{org}/{course}/edx-reverification-block/{checkpoint}'.format(
            org=self.course_id.org, course=self.course_id.course, checkpoint=checkpoint_name
        )
        expected_url = (
            '/verify_student/reverify'
            '/{course_key}'
            '/{checkpoint_location}/'
        ).format(course_key=unicode(self.course_id), checkpoint_location=checkpoint_location)

        self.assertEqual(
            reverification_service.start_verification(unicode(self.course_id), checkpoint_location),
            expected_url
        )
Beispiel #13
0
    def test_skip_verification(self):
        """ Adding the test skip verification attempt for the user """

        checkpoint_name = 'final_term'
        rev = ReverificationService()

        VerificationCheckpoint.objects.create(
            course_id=unicode(self.course_key), checkpoint_name=checkpoint_name
        )

        rev.skip_verification(checkpoint_name, self.user.id, unicode(self.course_key))

        self.assertEqual(1, SkippedReverification.objects.filter(user=self.user, course_id=self.course_key).count())

        rev.skip_verification(checkpoint_name, self.user.id, unicode(self.course_key))

        self.assertEqual(1, SkippedReverification.objects.filter(user=self.user, course_id=self.course_key).count())
Beispiel #14
0
def get_module_system_for_user(
        user,
        field_data_cache,
        # Arguments preceding this comment have user binding, those following don't
        descriptor,
        course_id,
        track_function,
        xqueue_callback_url_prefix,
        request_token,
        position=None,
        wrap_xmodule_display=True,
        grade_bucket_type=None,
        static_asset_path='',
        user_location=None):
    """
    Helper function that returns a module system and student_data bound to a user and a descriptor.

    The purpose of this function is to factor out everywhere a user is implicitly bound when creating a module,
    to allow an existing module to be re-bound to a user.  Most of the user bindings happen when creating the
    closures that feed the instantiation of ModuleSystem.

    The arguments fall into two categories: those that have explicit or implicit user binding, which are user
    and field_data_cache, and those don't and are just present so that ModuleSystem can be instantiated, which
    are all the other arguments.  Ultimately, this isn't too different than how get_module_for_descriptor_internal
    was before refactoring.

    Arguments:
        see arguments for get_module()
        request_token (str): A token unique to the request use by xblock initialization

    Returns:
        (LmsModuleSystem, KvsFieldData):  (module system, student_data) bound to, primarily, the user and descriptor
    """
    student_data = KvsFieldData(DjangoKeyValueStore(field_data_cache))

    def make_xqueue_callback(dispatch='score_update'):
        # Fully qualified callback URL for external queueing system
        relative_xqueue_callback_url = reverse(
            'xqueue_callback',
            kwargs=dict(course_id=course_id.to_deprecated_string(),
                        userid=str(user.id),
                        mod_id=descriptor.location.to_deprecated_string(),
                        dispatch=dispatch),
        )
        return xqueue_callback_url_prefix + relative_xqueue_callback_url

    # Default queuename is course-specific and is derived from the course that
    #   contains the current module.
    # TODO: Queuename should be derived from 'course_settings.json' of each course
    xqueue_default_queuename = descriptor.location.org + '-' + descriptor.location.course

    xqueue = {
        'interface': XQUEUE_INTERFACE,
        'construct_callback': make_xqueue_callback,
        'default_queuename': xqueue_default_queuename.replace(' ', '_'),
        'waittime': settings.XQUEUE_WAITTIME_BETWEEN_REQUESTS
    }

    # This is a hacky way to pass settings to the combined open ended xmodule
    # It needs an S3 interface to upload images to S3
    # It needs the open ended grading interface in order to get peer grading to be done
    # this first checks to see if the descriptor is the correct one, and only sends settings if it is

    # Get descriptor metadata fields indicating needs for various settings
    needs_open_ended_interface = getattr(descriptor,
                                         "needs_open_ended_interface", False)
    needs_s3_interface = getattr(descriptor, "needs_s3_interface", False)

    # Initialize interfaces to None
    open_ended_grading_interface = None
    s3_interface = None

    # Create interfaces if needed
    if needs_open_ended_interface:
        open_ended_grading_interface = settings.OPEN_ENDED_GRADING_INTERFACE
        open_ended_grading_interface[
            'mock_peer_grading'] = settings.MOCK_PEER_GRADING
        open_ended_grading_interface[
            'mock_staff_grading'] = settings.MOCK_STAFF_GRADING
    if needs_s3_interface:
        s3_interface = {
            'access_key':
            getattr(settings, 'AWS_ACCESS_KEY_ID', ''),
            'secret_access_key':
            getattr(settings, 'AWS_SECRET_ACCESS_KEY', ''),
            'storage_bucket_name':
            getattr(settings, 'AWS_STORAGE_BUCKET_NAME', 'openended')
        }

    def inner_get_module(descriptor):
        """
        Delegate to get_module_for_descriptor_internal() with all values except `descriptor` set.

        Because it does an access check, it may return None.
        """
        # TODO: fix this so that make_xqueue_callback uses the descriptor passed into
        # inner_get_module, not the parent's callback.  Add it as an argument....
        return get_module_for_descriptor_internal(
            user=user,
            descriptor=descriptor,
            field_data_cache=field_data_cache,
            course_id=course_id,
            track_function=track_function,
            xqueue_callback_url_prefix=xqueue_callback_url_prefix,
            position=position,
            wrap_xmodule_display=wrap_xmodule_display,
            grade_bucket_type=grade_bucket_type,
            static_asset_path=static_asset_path,
            user_location=user_location,
            request_token=request_token,
        )

    def _fulfill_content_milestones(user, course_key, content_key):
        """
        Internal helper to handle milestone fulfillments for the specified content module
        """
        # Fulfillment Use Case: Entrance Exam
        # If this module is part of an entrance exam, we'll need to see if the student
        # has reached the point at which they can collect the associated milestone
        if settings.FEATURES.get('ENTRANCE_EXAMS', False):
            course = modulestore().get_course(course_key)
            content = modulestore().get_item(content_key)
            entrance_exam_enabled = getattr(course, 'entrance_exam_enabled',
                                            False)
            in_entrance_exam = getattr(content, 'in_entrance_exam', False)
            if entrance_exam_enabled and in_entrance_exam:
                # We don't have access to the true request object in this context, but we can use a mock
                request = RequestFactory().request()
                request.user = user
                exam_pct = get_entrance_exam_score(request, course)
                if exam_pct >= course.entrance_exam_minimum_score_pct:
                    exam_key = UsageKey.from_string(course.entrance_exam_id)
                    relationship_types = milestones_helpers.get_milestone_relationship_types(
                    )
                    content_milestones = milestones_helpers.get_course_content_milestones(
                        course_key,
                        exam_key,
                        relationship=relationship_types['FULFILLS'])
                    # Add each milestone to the user's set...
                    user = {'id': request.user.id}
                    for milestone in content_milestones:
                        milestones_helpers.add_user_milestone(user, milestone)

    def handle_grade_event(block, event_type, event):  # pylint: disable=unused-argument
        """
        Manages the workflow for recording and updating of student module grade state
        """
        user_id = event.get('user_id', user.id)

        grade = event.get('value')
        max_grade = event.get('max_value')

        field_data_cache.set_score(
            user_id,
            descriptor.location,
            grade,
            max_grade,
        )

        # Bin score into range and increment stats
        score_bucket = get_score_bucket(grade, max_grade)

        tags = [
            u"org:{}".format(course_id.org), u"course:{}".format(course_id),
            u"score_bucket:{0}".format(score_bucket)
        ]

        if grade_bucket_type is not None:
            tags.append('type:%s' % grade_bucket_type)

        dog_stats_api.increment("lms.courseware.question_answered", tags=tags)

        # Cycle through the milestone fulfillment scenarios to see if any are now applicable
        # thanks to the updated grading information that was just submitted
        _fulfill_content_milestones(
            user,
            course_id,
            descriptor.location,
        )

        # Send a signal out to any listeners who are waiting for score change
        # events.
        SCORE_CHANGED.send(sender=None,
                           points_possible=event['max_value'],
                           points_earned=event['value'],
                           user_id=user_id,
                           course_id=unicode(course_id),
                           usage_id=unicode(descriptor.location))

    def publish(block, event_type, event):
        """A function that allows XModules to publish events."""
        if event_type == 'grade':
            handle_grade_event(block, event_type, event)
        else:
            track_function(event_type, event)

    def rebind_noauth_module_to_user(module, real_user):
        """
        A function that allows a module to get re-bound to a real user if it was previously bound to an AnonymousUser.

        Will only work within a module bound to an AnonymousUser, e.g. one that's instantiated by the noauth_handler.

        Arguments:
            module (any xblock type):  the module to rebind
            real_user (django.contrib.auth.models.User):  the user to bind to

        Returns:
            nothing (but the side effect is that module is re-bound to real_user)
        """
        if user.is_authenticated():
            err_msg = (
                "rebind_noauth_module_to_user can only be called from a module bound to "
                "an anonymous user")
            log.error(err_msg)
            raise LmsModuleRenderError(err_msg)

        field_data_cache_real_user = FieldDataCache.cache_for_descriptor_descendents(
            course_id,
            real_user,
            module.descriptor,
            asides=XBlockAsidesConfig.possible_asides(),
        )

        (inner_system, inner_student_data) = get_module_system_for_user(
            user=real_user,
            field_data_cache=
            field_data_cache_real_user,  # These have implicit user bindings, rest of args considered not to
            descriptor=module.descriptor,
            course_id=course_id,
            track_function=track_function,
            xqueue_callback_url_prefix=xqueue_callback_url_prefix,
            position=position,
            wrap_xmodule_display=wrap_xmodule_display,
            grade_bucket_type=grade_bucket_type,
            static_asset_path=static_asset_path,
            user_location=user_location,
            request_token=request_token)

        module.descriptor.bind_for_student(
            inner_system,
            real_user.id,
            [
                partial(OverrideFieldData.wrap, real_user),
                partial(LmsFieldData, student_data=inner_student_data),
            ],
        )

        module.descriptor.scope_ids = (
            module.descriptor.scope_ids._replace(user_id=real_user.id)  # pylint: disable=protected-access
        )
        module.scope_ids = module.descriptor.scope_ids  # this is needed b/c NamedTuples are immutable
        # now bind the module to the new ModuleSystem instance and vice-versa
        module.runtime = inner_system
        inner_system.xmodule_instance = module

    # Build a list of wrapping functions that will be applied in order
    # to the Fragment content coming out of the xblocks that are about to be rendered.
    block_wrappers = []

    if settings.FEATURES.get("LICENSING", False):
        block_wrappers.append(wrap_with_license)

    # Wrap the output display in a single div to allow for the XModule
    # javascript to be bound correctly
    if wrap_xmodule_display is True:
        block_wrappers.append(
            partial(
                wrap_xblock,
                'LmsRuntime',
                extra_data={'course-id': course_id.to_deprecated_string()},
                usage_id_serializer=lambda usage_id: quote_slashes(
                    usage_id.to_deprecated_string()),
                request_token=request_token,
            ))

    # TODO (cpennington): When modules are shared between courses, the static
    # prefix is going to have to be specific to the module, not the directory
    # that the xml was loaded from

    # Rewrite urls beginning in /static to point to course-specific content
    block_wrappers.append(
        partial(replace_static_urls,
                getattr(descriptor, 'data_dir', None),
                course_id=course_id,
                static_asset_path=static_asset_path
                or descriptor.static_asset_path))

    # Allow URLs of the form '/course/' refer to the root of multicourse directory
    #   hierarchy of this course
    block_wrappers.append(partial(replace_course_urls, course_id))

    # this will rewrite intra-courseware links (/jump_to_id/<id>). This format
    # is an improvement over the /course/... format for studio authored courses,
    # because it is agnostic to course-hierarchy.
    # NOTE: module_id is empty string here. The 'module_id' will get assigned in the replacement
    # function, we just need to specify something to get the reverse() to work.
    block_wrappers.append(
        partial(
            replace_jump_to_id_urls,
            course_id,
            reverse('jump_to_id',
                    kwargs={
                        'course_id': course_id.to_deprecated_string(),
                        'module_id': ''
                    }),
        ))

    if settings.FEATURES.get('DISPLAY_DEBUG_INFO_TO_STAFF'):
        if has_access(user, 'staff', descriptor, course_id):
            has_instructor_access = has_access(user, 'instructor', descriptor,
                                               course_id)
            block_wrappers.append(
                partial(add_staff_markup, user, has_instructor_access))

    # These modules store data using the anonymous_student_id as a key.
    # To prevent loss of data, we will continue to provide old modules with
    # the per-student anonymized id (as we have in the past),
    # while giving selected modules a per-course anonymized id.
    # As we have the time to manually test more modules, we can add to the list
    # of modules that get the per-course anonymized id.
    is_pure_xblock = isinstance(
        descriptor, XBlock) and not isinstance(descriptor, XModuleDescriptor)
    module_class = getattr(descriptor, 'module_class', None)
    is_lti_module = not is_pure_xblock and issubclass(module_class, LTIModule)
    if is_pure_xblock or is_lti_module:
        anonymous_student_id = anonymous_id_for_user(user, course_id)
    else:
        anonymous_student_id = anonymous_id_for_user(user, None)

    field_data = LmsFieldData(descriptor._field_data, student_data)  # pylint: disable=protected-access

    user_is_staff = has_access(user, u'staff', descriptor.location, course_id)

    system = LmsModuleSystem(
        track_function=track_function,
        render_template=render_to_string,
        static_url=settings.STATIC_URL,
        xqueue=xqueue,
        # TODO (cpennington): Figure out how to share info between systems
        filestore=descriptor.runtime.resources_fs,
        get_module=inner_get_module,
        user=user,
        debug=settings.DEBUG,
        hostname=settings.SITE_NAME,
        # TODO (cpennington): This should be removed when all html from
        # a module is coming through get_html and is therefore covered
        # by the replace_static_urls code below
        replace_urls=partial(
            static_replace.replace_static_urls,
            data_directory=getattr(descriptor, 'data_dir', None),
            course_id=course_id,
            static_asset_path=static_asset_path
            or descriptor.static_asset_path,
        ),
        replace_course_urls=partial(static_replace.replace_course_urls,
                                    course_key=course_id),
        replace_jump_to_id_urls=partial(
            static_replace.replace_jump_to_id_urls,
            course_id=course_id,
            jump_to_id_base_url=reverse('jump_to_id',
                                        kwargs={
                                            'course_id':
                                            course_id.to_deprecated_string(),
                                            'module_id':
                                            ''
                                        })),
        node_path=settings.NODE_PATH,
        publish=publish,
        anonymous_student_id=anonymous_student_id,
        course_id=course_id,
        open_ended_grading_interface=open_ended_grading_interface,
        s3_interface=s3_interface,
        cache=cache,
        can_execute_unsafe_code=(lambda: can_execute_unsafe_code(course_id)),
        get_python_lib_zip=(
            lambda: get_python_lib_zip(contentstore, course_id)),
        # TODO: When we merge the descriptor and module systems, we can stop reaching into the mixologist (cpennington)
        mixins=descriptor.runtime.mixologist._mixins,  # pylint: disable=protected-access
        wrappers=block_wrappers,
        get_real_user=user_by_anonymous_id,
        services={
            'i18n': ModuleI18nService(),
            'fs': xblock.reference.plugins.FSService(),
            'field-data': field_data,
            'user': DjangoXBlockUserService(user, user_is_staff=user_is_staff),
            "reverification": ReverificationService()
        },
        get_user_role=lambda: get_user_role(user, course_id),
        descriptor_runtime=descriptor._runtime,  # pylint: disable=protected-access
        rebind_noauth_module_to_user=rebind_noauth_module_to_user,
        user_location=user_location,
        request_token=request_token,
    )

    # pass position specified in URL to module through ModuleSystem
    if position is not None:
        try:
            position = int(position)
        except (ValueError, TypeError):
            log.exception('Non-integer %r passed as position.', position)
            position = None

    system.set('position', position)
    if settings.FEATURES.get(
            'ENABLE_PSYCHOMETRICS') and user.is_authenticated():
        system.set(
            'psychometrics_handler',  # set callback for updating PsychometricsData
            make_psychometrics_data_update_handler(course_id, user,
                                                   descriptor.location))

    system.set(u'user_is_staff', user_is_staff)
    system.set(u'user_is_admin', has_access(user, u'staff', 'global'))
    system.set(u'user_is_beta_tester',
               CourseBetaTesterRole(course_id).has_user(user))
    system.set(u'days_early_for_beta',
               getattr(descriptor, 'days_early_for_beta'))

    # make an ErrorDescriptor -- assuming that the descriptor's system is ok
    if has_access(user, u'staff', descriptor.location, course_id):
        system.error_descriptor_class = ErrorDescriptor
    else:
        system.error_descriptor_class = NonStaffErrorDescriptor

    return system, field_data