Ejemplo n.º 1
0
    def setUp(self):
        self.client = Client()

        # Mocks
        api.api_enabled = self.mock_api_enabled(True)

        # Create two accounts
        self.password = '******'
        self.student = User.objects.create_user('student', '*****@*****.**',
                                                self.password)
        self.student2 = User.objects.create_user('student2',
                                                 '*****@*****.**',
                                                 self.password)
        self.instructor = User.objects.create_user('instructor',
                                                   '*****@*****.**',
                                                   self.password)
        self.course_key = SlashSeparatedCourseKey('HarvardX', 'CB22x',
                                                  'The_Ancient_Greek_Hero')
        self.note = {
            'user': self.student,
            'course_id': self.course_key,
            'uri': '/',
            'text': 'foo',
            'quote': 'bar',
            'range_start': 0,
            'range_start_offset': 0,
            'range_end': 100,
            'range_end_offset': 0,
            'tags': 'a,b,c'
        }

        # Make sure no note with this ID ever exists for testing purposes
        self.NOTE_ID_DOES_NOT_EXIST = 99999
Ejemplo n.º 2
0
    def test_delete_course_map(self):
        """
        Test that course location is properly remove from loc_mapper and cache when course is deleted
        """
        org = u'foo_org'
        course = u'bar_course'
        run = u'baz_run'
        course_location = SlashSeparatedCourseKey(org, course, run)
        loc_mapper().create_map_entry(course_location)
        # pylint: disable=protected-access
        entry = loc_mapper().location_map.find_one({
            '_id': loc_mapper()._construct_course_son(course_location)
        })
        self.assertIsNotNone(entry, 'Entry not found in loc_mapper')
        self.assertEqual(entry['offering'], u'{1}.{2}'.format(org, course, run))

        # now delete course location from loc_mapper and cache and test that course location no longer
        # exists in loca_mapper and cache
        loc_mapper().delete_course_mapping(course_location)
        # pylint: disable=protected-access
        entry = loc_mapper().location_map.find_one({
            '_id': loc_mapper()._construct_course_son(course_location)
        })
        self.assertIsNone(entry, 'Entry found in loc_mapper')
        # pylint: disable=protected-access
        cached_value = loc_mapper()._get_location_from_cache(course_location.make_usage_key('course', run))
        self.assertIsNone(cached_value, 'course_locator found in cache')
        # pylint: disable=protected-access
        cached_value = loc_mapper()._get_course_location_from_cache(course_location)
        self.assertIsNone(cached_value, 'Entry found in cache')
Ejemplo n.º 3
0
    def setUp(self):
        self.student = '*****@*****.**'
        self.instructor = '*****@*****.**'
        self.password = '******'
        self.create_account('u1', self.student, self.password)
        self.create_account('u2', self.instructor, self.password)
        self.activate_user(self.student)
        self.activate_user(self.instructor)

        self.course_id = SlashSeparatedCourseKey("edX", "toy", "2012_Fall")
        self.location_string = self.course_id.make_usage_key('html', 'TestLocation').to_deprecated_string()
        self.toy = modulestore().get_course(self.course_id)
        location = "i4x://edX/toy/peergrading/init"
        field_data = DictFieldData({'data': "<peergrading/>", 'location': location, 'category':'peergrading'})
        self.mock_service = peer_grading_service.MockPeerGradingService()
        self.system = LmsModuleSystem(
            static_url=settings.STATIC_URL,
            track_function=None,
            get_module=None,
            render_template=render_to_string,
            replace_urls=None,
            s3_interface=test_util_open_ended.S3_INTERFACE,
            open_ended_grading_interface=test_util_open_ended.OPEN_ENDED_GRADING_INTERFACE,
            mixins=settings.XBLOCK_MIXINS,
            error_descriptor_class=ErrorDescriptor,
            descriptor_runtime=None,
        )
        self.descriptor = peer_grading_module.PeerGradingDescriptor(self.system, field_data, ScopeIds(None, None, None, None))
        self.descriptor.xmodule_runtime = self.system
        self.peer_module = self.descriptor
        self.peer_module.peer_gs = self.mock_service
        self.logout()
Ejemplo n.º 4
0
    def test_form_typo(self):
        # Munge course id
        bad_id = SlashSeparatedCourseKey(
            u'Broken{}'.format(self.course.id.org), '',
            self.course.id.run + '_typo')

        form_data = {
            'course_id': bad_id.to_deprecated_string(),
            'email_enabled': True
        }
        form = CourseAuthorizationAdminForm(data=form_data)
        # Validation shouldn't work
        self.assertFalse(form.is_valid())

        msg = u'COURSE NOT FOUND'
        msg += u' --- Entered course id was: "{0}". '.format(
            bad_id.to_deprecated_string())
        msg += 'Please recheck that you have supplied a valid course id.'
        self.assertEquals(msg, form._errors['course_id'][0])  # pylint: disable=protected-access

        with self.assertRaisesRegexp(
                ValueError,
                "The CourseAuthorization could not be created because the data didn't validate."
        ):
            form.save()
Ejemplo n.º 5
0
    def test_basic(self):
        upload_date = datetime(2013, 6, 1, 10, 30, tzinfo=UTC)

        course_key = SlashSeparatedCourseKey('org', 'class', 'run')
        location = course_key.make_asset_key('asset', 'my_file_name.jpg')
        thumbnail_location = course_key.make_asset_key(
            'thumbnail', 'my_file_name_thumb.jpg')

        output = assets._get_asset_json("my_file", upload_date, location,
                                        thumbnail_location, True)

        self.assertEquals(output["display_name"], "my_file")
        self.assertEquals(output["date_added"], "Jun 01, 2013 at 10:30 UTC")
        self.assertEquals(output["url"],
                          "/c4x/org/class/asset/my_file_name.jpg")
        self.assertEquals(output["external_url"],
                          "lms_base_url/c4x/org/class/asset/my_file_name.jpg")
        self.assertEquals(output["portable_url"], "/static/my_file_name.jpg")
        self.assertEquals(output["thumbnail"],
                          "/c4x/org/class/thumbnail/my_file_name_thumb.jpg")
        self.assertEquals(output["id"], unicode(location))
        self.assertEquals(output['locked'], True)

        output = assets._get_asset_json("name", upload_date, location, None,
                                        False)
        self.assertIsNone(output["thumbnail"])
Ejemplo n.º 6
0
    def is_enrolled_by_partial(cls, user, course_id_partial):
        """
        Returns `True` if the user is enrolled in a course that starts with
        `course_id_partial`. Otherwise, returns False.

        Can be used to determine whether a student is enrolled in a course
        whose run name is unknown.

        `user` is a Django User object. If it hasn't been saved yet (no `.id`
               attribute), this method will automatically save it before
               adding an enrollment for it.

        `course_id_partial` (CourseKey) is missing the run component
        """
        assert isinstance(course_id_partial, SlashSeparatedCourseKey)
        assert not course_id_partial.run  # None or empty string
        course_key = SlashSeparatedCourseKey(course_id_partial.org, course_id_partial.course, '')
        querystring = unicode(course_key.to_deprecated_string())
        try:
            return CourseEnrollment.objects.filter(
                user=user,
                course_id__startswith=querystring,
                is_active=1
            ).exists()
        except cls.DoesNotExist:
            return False
    def setUp(self):
        self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
        self.location = self.course_key.make_usage_key('chapter', 'Overview')
        self.toy_course = modulestore().get_course(self.course_key)
        self.mock_user = UserFactory()
        self.mock_user.id = 1
        self.request_factory = RequestFactory()

        # Construct a mock module for the modulestore to return
        self.mock_module = MagicMock()
        self.mock_module.id = 1
        self.dispatch = 'score_update'

        # Construct a 'standard' xqueue_callback url
        self.callback_url = reverse('xqueue_callback',
                                    kwargs={
                                        'course_id':
                                        self.course_key.to_deprecated_string(),
                                        'userid':
                                        str(self.mock_user.id),
                                        'mod_id':
                                        self.mock_module.id,
                                        'dispatch':
                                        self.dispatch
                                    })
Ejemplo n.º 8
0
    def test_get_courses_for_wiki(self):
        """
        Test the get_courses_for_wiki method
        """
        for course_number in self.courses:
            course_locations = self.store.get_courses_for_wiki(course_number)
            assert_equals(len(course_locations), 1)
            assert_equals(Location('edX', course_number, '2012_Fall', 'course', '2012_Fall'), course_locations[0])

        course_locations = self.store.get_courses_for_wiki('no_such_wiki')
        assert_equals(len(course_locations), 0)

        # set toy course to share the wiki with simple course
        toy_course = self.store.get_course(SlashSeparatedCourseKey('edX', 'toy', '2012_Fall'))
        toy_course.wiki_slug = 'simple'
        self.store.update_item(toy_course)

        # now toy_course should not be retrievable with old wiki_slug
        course_locations = self.store.get_courses_for_wiki('toy')
        assert_equals(len(course_locations), 0)

        # but there should be two courses with wiki_slug 'simple'
        course_locations = self.store.get_courses_for_wiki('simple')
        assert_equals(len(course_locations), 2)
        for course_number in ['toy', 'simple']:
            assert_in(Location('edX', course_number, '2012_Fall', 'course', '2012_Fall'), course_locations)

        # configure simple course to use unique wiki_slug.
        simple_course = self.store.get_course(SlashSeparatedCourseKey('edX', 'simple', '2012_Fall'))
        simple_course.wiki_slug = 'edX.simple.2012_Fall'
        self.store.update_item(simple_course)
        # it should be retrievable with its new wiki_slug
        course_locations = self.store.get_courses_for_wiki('edX.simple.2012_Fall')
        assert_equals(len(course_locations), 1)
        assert_in(Location('edX', 'simple', '2012_Fall', 'course', '2012_Fall'), course_locations)
Ejemplo n.º 9
0
    def test_errored_course_regular_access(self):
        """
        Test the course list for regular staff when get_course returns an ErrorDescriptor
        """
        GlobalStaff().remove_users(self.user)
        CourseStaffRole(SlashSeparatedCourseKey('Non', 'Existent',
                                                'Course')).add_users(self.user)

        course_key = SlashSeparatedCourseKey('Org1', 'Course1', 'Run1')
        self._create_course_with_access_groups(course_key, self.user)

        with patch('xmodule.modulestore.mongo.base.MongoKeyValueStore',
                   Mock(side_effect=Exception)):
            self.assertIsInstance(modulestore().get_course(course_key),
                                  ErrorDescriptor)

            # get courses through iterating all courses
            courses_list = _accessible_courses_list(self.request)
            self.assertEqual(courses_list, [])

            # get courses by reversing group name formats
            courses_list_by_groups = _accessible_courses_list_from_groups(
                self.request)
            self.assertEqual(courses_list_by_groups, [])
            self.assertEqual(courses_list, courses_list_by_groups)
Ejemplo n.º 10
0
 def setUp(self):
     self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
     self.course_loc = self.course_key.make_usage_key('course', '2012_Fall')
     self.anonymous_user = AnonymousUserFactory()
     self.student = UserFactory()
     self.global_staff = UserFactory(is_staff=True)
     self.course_staff = StaffFactory(course_key=self.course_key)
     self.course_instructor = InstructorFactory(course_key=self.course_key)
Ejemplo n.º 11
0
 def setUp(self):
     self.course = CourseFactory.create()
     self.page = ItemFactory.create(category="static_tab",
                                    parent_location=self.course.location,
                                    data="OOGIE BLOOGIE",
                                    display_name="new_tab")
     self.toy_course_key = SlashSeparatedCourseKey('edX', 'toy',
                                                   '2012_Fall')
Ejemplo n.º 12
0
 def setUp(self):
     course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
     self.course = course_key.make_usage_key('course', course_key.run)
     self.anonymous_user = AnonymousUserFactory()
     self.student = UserFactory()
     self.global_staff = UserFactory(is_staff=True)
     self.course_staff = StaffFactory(course_key=self.course.course_key)
     self.course_instructor = InstructorFactory(course_key=self.course.course_key)
Ejemplo n.º 13
0
 def test_invalid_chars_ssck(self):
     """
     Test that the ssck constructor fails if given invalid chars
     """
     valid_base = SlashSeparatedCourseKey(u'org.dept-1%2', u'course.sub-2%3', u'run.faster-4%5')
     for key in SlashSeparatedCourseKey.KEY_FIELDS:
         with self.assertRaises(InvalidKeyError):
             # this ends up calling the constructor where the legality check should occur
             valid_base.replace(**{key: u'funny thing'})
Ejemplo n.º 14
0
    def setUp(self):
        self.user = UserFactory.create(username="******", password="******")
        self.client.login(username="******", password="******")
        self.course_key = SlashSeparatedCourseKey("Robot", "999", "Test_Course")
        CourseFactory.create(org='Robot', number='999', display_name='Test Course')

        patcher = patch('student.models.tracker')
        self.mock_tracker = patcher.start()
        self.addCleanup(patcher.stop)
Ejemplo n.º 15
0
def i_click_on_error_dialog(step):
    world.click_link_by_text('Correct failed component')
    assert_true(world.css_html("span.inline-error").startswith("Problem i4x://MITx/999/problem"))
    course_key = SlashSeparatedCourseKey("MITx", "999", "Robot_Super_Course")
    # we don't know the actual ID of the vertical. So just check that we did go to a
    # vertical page in the course (there should only be one).
    vertical_usage_key = course_key.make_usage_key("vertical", "")
    vertical_url = reverse_usage_url('unit_handler', vertical_usage_key)
    assert_equal(1, world.browser.url.count(vertical_url))
Ejemplo n.º 16
0
 def setUp(self):
     self.data = {'foo': 'foo_value'}
     self.course_id = SlashSeparatedCourseKey('org', 'course', 'run')
     self.children = [
         self.course_id.make_usage_key('child', 'a'),
         self.course_id.make_usage_key('child', 'b')
     ]
     self.metadata = {'meta': 'meta_val'}
     self.kvs = MongoKeyValueStore(self.data, self.children, self.metadata)
Ejemplo n.º 17
0
    def test_get_course_list_with_same_course_id(self):
        """
        Test getting courses with same id but with different name case. Then try to delete one of them and
        check that it is properly deleted and other one is accessible
        """
        # create and log in a non-staff user
        self.user = UserFactory()
        request = self.factory.get('/course')
        request.user = self.user
        self.client.login(username=self.user.username, password='******')

        course_location_caps = SlashSeparatedCourseKey('Org', 'COURSE', 'Run')
        self._create_course_with_access_groups(course_location_caps, self.user)

        # get courses through iterating all courses
        courses_list = _accessible_courses_list(request)
        self.assertEqual(len(courses_list), 1)

        # get courses by reversing group name formats
        courses_list_by_groups = _accessible_courses_list_from_groups(request)
        self.assertEqual(len(courses_list_by_groups), 1)
        # check both course lists have same courses
        self.assertEqual(courses_list, courses_list_by_groups)

        # now create another course with same course_id but different name case
        course_location_camel = SlashSeparatedCourseKey('Org', 'Course', 'Run')
        self._create_course_with_access_groups(course_location_camel, self.user)

        # test that get courses through iterating all courses returns both courses
        courses_list = _accessible_courses_list(request)
        self.assertEqual(len(courses_list), 2)

        # test that get courses by reversing group name formats returns both courses
        courses_list_by_groups = _accessible_courses_list_from_groups(request)
        self.assertEqual(len(courses_list_by_groups), 2)

        # now delete first course (course_location_caps) and check that it is no longer accessible
        delete_course_and_groups(course_location_caps, commit=True)

        # test that get courses through iterating all courses now returns one course
        courses_list = _accessible_courses_list(request)
        self.assertEqual(len(courses_list), 1)

        # test that get courses by reversing group name formats also returns one course
        courses_list_by_groups = _accessible_courses_list_from_groups(request)
        self.assertEqual(len(courses_list_by_groups), 1)

        # now check that deleted course is not accessible
        outline_url = reverse_course_url('course_handler', course_location_caps)
        response = self.client.get(outline_url, HTTP_ACCEPT='application/json')
        self.assertEqual(response.status_code, 403)

        # now check that other course is accessible
        outline_url = reverse_course_url('course_handler', course_location_camel)
        response = self.client.get(outline_url, HTTP_ACCEPT='application/json')
        self.assertEqual(response.status_code, 200)
Ejemplo n.º 18
0
class TestInstructorEnrollmentStudentModule(TestCase):
    """ Test student module manipulations. """
    def setUp(self):
        self.course_key = SlashSeparatedCourseKey('fake', 'course', 'id')

    def test_reset_student_attempts(self):
        user = UserFactory()
        msk = self.course_key.make_usage_key('dummy', 'module')
        original_state = json.dumps({'attempts': 32, 'otherstuff': 'alsorobots'})
        StudentModule.objects.create(student=user, course_id=self.course_key, module_state_key=msk, state=original_state)
        # lambda to reload the module state from the database
        module = lambda: StudentModule.objects.get(student=user, course_id=self.course_key, module_state_key=msk)
        self.assertEqual(json.loads(module().state)['attempts'], 32)
        reset_student_attempts(self.course_key, user, msk)
        self.assertEqual(json.loads(module().state)['attempts'], 0)

    def test_delete_student_attempts(self):
        user = UserFactory()
        msk = self.course_key.make_usage_key('dummy', 'module')
        original_state = json.dumps({'attempts': 32, 'otherstuff': 'alsorobots'})
        StudentModule.objects.create(student=user, course_id=self.course_key, module_state_key=msk, state=original_state)
        self.assertEqual(StudentModule.objects.filter(student=user, course_id=self.course_key, module_state_key=msk).count(), 1)
        reset_student_attempts(self.course_key, user, msk, delete_module=True)
        self.assertEqual(StudentModule.objects.filter(student=user, course_id=self.course_key, module_state_key=msk).count(), 0)

    def test_delete_submission_scores(self):
        user = UserFactory()
        problem_location = self.course_key.make_usage_key('dummy', 'module')

        # Create a student module for the user
        StudentModule.objects.create(
            student=user,
            course_id=self.course_key,
            module_state_key=problem_location,
            state=json.dumps({})
        )

        # Create a submission and score for the student using the submissions API
        student_item = {
            'student_id': anonymous_id_for_user(user, self.course_key),
            'course_id': self.course_key.to_deprecated_string(),
            'item_id': problem_location.to_deprecated_string(),
            'item_type': 'openassessment'
        }
        submission = sub_api.create_submission(student_item, 'test answer')
        sub_api.set_score(submission['uuid'], 1, 2)

        # Delete student state using the instructor dash
        reset_student_attempts(
            self.course_key, user, problem_location,
            delete_module=True
        )

        # Verify that the student's scores have been reset in the submissions API
        score = sub_api.get_score(student_item)
        self.assertIs(score, None)
Ejemplo n.º 19
0
 def test_sandbox_inclusion(self):
     """
     Test to make sure that a match works across course runs
     """
     self.assertTrue(
         can_execute_unsafe_code(
             SlashSeparatedCourseKey('edX', 'full', '2012_Fall')))
     self.assertTrue(
         can_execute_unsafe_code(
             SlashSeparatedCourseKey('edX', 'full', '2013_Spring')))
Ejemplo n.º 20
0
 def test_courses_with_unsafe_code_default(self):
     """
     Test that the default setting for COURSES_WITH_UNSAFE_CODE is an empty setting, e.g. we don't use @override_settings in these tests
     """
     self.assertFalse(
         can_execute_unsafe_code(
             SlashSeparatedCourseKey('edX', 'full', '2012_Fall')))
     self.assertFalse(
         can_execute_unsafe_code(
             SlashSeparatedCourseKey('edX', 'full', '2013_Spring')))
Ejemplo n.º 21
0
    def setup_xml_course(self):
        """
        Define the XML backed course to use.
        Toy courses are already loaded in XML and mixed modulestores.
        """
        course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
        location = course_key.make_usage_key('chapter', 'Overview')
        descriptor = modulestore().get_item(location)

        self.module = self._get_module(course_key, descriptor, location)
Ejemplo n.º 22
0
 def setUp(self):
     course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
     self.course = course_key.make_usage_key('course', course_key.run)
     self.anonymous_user = AnonymousUserFactory()
     self.student = UserFactory()
     self.global_staff = UserFactory(is_staff=True)
     # TODO please change the StaffFactory and InstructorFactory parameters ASAP!
     self.course_staff = StaffFactory(course=self.course.course_key)
     self.course_instructor = InstructorFactory(
         course=self.course.course_key)
    def setup_xml_course(self):
        """
        Define the XML backed course to use.
        Toy courses are already loaded in XML and mixed modulestores.
        """
        course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
        location = course_key.make_usage_key('chapter', 'Overview')
        descriptor = modulestore().get_item(location)

        self.module = self._get_module(course_key, descriptor, location)
Ejemplo n.º 24
0
 def setUp(self):
     system = get_test_descriptor_system()
     course_key = SlashSeparatedCourseKey('org', 'course', 'run')
     usage_key = course_key.make_usage_key('video', 'name')
     self.descriptor = system.construct_xblock_from_class(
         VideoDescriptor,
         scope_ids=ScopeIds(None, None, usage_key, usage_key),
         field_data=DictFieldData({}),
     )
     self.descriptor.runtime.handler_url = MagicMock()
Ejemplo n.º 25
0
 def test_invalid_chars_location(self):
     """
     Test that the location constructor fails if given invalid chars
     """
     course_key = SlashSeparatedCourseKey(u'org.dept-1%2', u'course.sub-2%3', u'run.faster-4%5')
     valid_base = course_key.make_usage_key('tomato-again%9', 'block-head:sub-4%9')
     for key in SlashSeparatedCourseKey.KEY_FIELDS:
         with self.assertRaises(InvalidKeyError):
             # this ends up calling the constructor where the legality check should occur
             valid_base.replace(**{key: u'funny thing'})
 def setUp(self):
     system = get_test_descriptor_system()
     course_key = SlashSeparatedCourseKey('org', 'course', 'run')
     usage_key = course_key.make_usage_key('video', 'name')
     self.descriptor = system.construct_xblock_from_class(
         VideoDescriptor,
         scope_ids=ScopeIds(None, None, usage_key, usage_key),
         field_data=DictFieldData({}),
     )
     self.descriptor.runtime.handler_url = MagicMock()
Ejemplo n.º 27
0
 def test_rewrite_reference_list(self):
     module_store = modulestore('direct')
     target_course_id = SlashSeparatedCourseKey('testX', 'conditional_copy', 'copy_run')
     import_from_xml(
         module_store,
         'common/test/data/',
         ['conditional'],
         target_course_id=target_course_id
     )
     conditional_module = module_store.get_item(
         target_course_id.make_usage_key('conditional', 'condone')
     )
     self.assertIsNotNone(conditional_module)
     different_course_id = SlashSeparatedCourseKey('edX', 'different_course', 'copy_run')
     self.assertListEqual(
         [
             target_course_id.make_usage_key('problem', 'choiceprob'),
             different_course_id.make_usage_key('html', 'for_testing_import_rewrites')
         ],
         conditional_module.sources_list
     )
     self.assertListEqual(
         [
             target_course_id.make_usage_key('html', 'congrats'),
             target_course_id.make_usage_key('html', 'secret_page')
         ],
         conditional_module.show_tag_list
     )
Ejemplo n.º 28
0
    def create_course(self,
                      org,
                      offering,
                      user_id=None,
                      fields=None,
                      **kwargs):
        """
        Creates and returns the course.

        Args:
            org (str): the organization that owns the course
            offering (str): the name of the course offering
            user_id: id of the user creating the course
            fields (dict): Fields to set on the course at initialization
            kwargs: Any optional arguments understood by a subset of modulestores to customize instantiation

        Returns: a CourseDescriptor

        Raises:
            InvalidLocationError: If a course with the same org and offering already exists
        """

        course, _, run = offering.partition('/')
        course_id = SlashSeparatedCourseKey(org, course, run)

        # Check if a course with this org/course has been defined before (case-insensitive)
        course_search_location = SON([
            ('_id.tag', 'i4x'),
            ('_id.org', re.compile(u'^{}$'.format(course_id.org),
                                   re.IGNORECASE)),
            ('_id.course',
             re.compile(u'^{}$'.format(course_id.course), re.IGNORECASE)),
            ('_id.category', 'course'),
        ])
        courses = self.collection.find(course_search_location, fields=('_id'))
        if courses.count() > 0:
            raise InvalidLocationError(
                "There are already courses with the given org and course id: {}"
                .format([course['_id'] for course in courses]))

        location = course_id.make_usage_key('course', course_id.run)
        course = self.create_and_save_xmodule(location,
                                              fields=fields,
                                              **kwargs)

        # clone a default 'about' overview module as well
        about_location = location.replace(category='about', name='overview')
        overview_template = AboutDescriptor.get_template('overview.yaml')
        self.create_and_save_xmodule(
            about_location,
            system=course.system,
            definition_data=overview_template.get('data'))

        return course
Ejemplo n.º 29
0
def i_click_on_error_dialog(step):
    world.click_link_by_text('Correct failed component')
    assert_true(
        world.css_html("span.inline-error").startswith(
            "Problem i4x://MITx/999/problem"))
    course_key = SlashSeparatedCourseKey("MITx", "999", "Robot_Super_Course")
    # we don't know the actual ID of the vertical. So just check that we did go to a
    # vertical page in the course (there should only be one).
    vertical_usage_key = course_key.make_usage_key("vertical", "")
    vertical_url = reverse_usage_url('unit_handler', vertical_usage_key)
    assert_equal(1, world.browser.url.count(vertical_url))
    def setUp(self):
        """
        Add a user and a course
        """
        super(TestUsersDefaultRole, self).setUp()
        # create and log in a staff user.
        self.user = UserFactory(is_staff=True)  # pylint: disable=no-member
        self.client = AjaxEnabledTestClient()
        self.client.login(username=self.user.username, password='******')

        # create a course via the view handler to create course
        self.course_key = SlashSeparatedCourseKey('Org_1', 'Course_1', 'Run_1')
        self._create_course_with_given_location(self.course_key)
Ejemplo n.º 31
0
    def setUp(self):
        self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')

        # Create instructor account
        instructor = AdminFactory.create()
        self.client.login(username=instructor.username, password="******")

        # URL for instructor dash
        self.url = reverse(
            'instructor_dashboard',
            kwargs={'course_id': self.course_key.to_deprecated_string()})
        # URL for email view
        self.email_link = '<a href="" data-section="send_email">Email</a>'
Ejemplo n.º 32
0
    def create_course(self, org, offering, user_id=None, fields=None, **kwargs):
        """
        Creates and returns the course.

        Args:
            org (str): the organization that owns the course
            offering (str): the name of the course offering
            user_id: id of the user creating the course
            fields (dict): Fields to set on the course at initialization
            kwargs: Any optional arguments understood by a subset of modulestores to customize instantiation

        Returns: a CourseDescriptor

        Raises:
            InvalidLocationError: If a course with the same org and offering already exists
        """

        course, _, run = offering.partition('/')
        course_id = SlashSeparatedCourseKey(org, course, run)

        # Check if a course with this org/course has been defined before (case-insensitive)
        course_search_location = SON([
            ('_id.tag', 'i4x'),
            ('_id.org', re.compile(u'^{}$'.format(course_id.org), re.IGNORECASE)),
            ('_id.course', re.compile(u'^{}$'.format(course_id.course), re.IGNORECASE)),
            ('_id.category', 'course'),
        ])
        courses = self.collection.find(course_search_location, fields=('_id'))
        if courses.count() > 0:
            raise InvalidLocationError(
                "There are already courses with the given org and course id: {}".format([
                    course['_id'] for course in courses
                ]))

        location = course_id.make_usage_key('course', course_id.run)
        course = self.create_and_save_xmodule(location, fields=fields, **kwargs)

        # clone a default 'about' overview module as well
        about_location = location.replace(
            category='about',
            name='overview'
        )
        overview_template = AboutDescriptor.get_template('overview.yaml')
        self.create_and_save_xmodule(
            about_location,
            system=course.system,
            definition_data=overview_template.get('data')
        )

        return course
Ejemplo n.º 33
0
    def handle(self, *args, **options):
        source_key = SlashSeparatedCourseKey.from_deprecated_string(options['source_course'])
        dest_key = SlashSeparatedCourseKey.from_deprecated_string(options['dest_course'])

        source_students = User.objects.filter(
            courseenrollment__course_id=source_key
        )

        for user in source_students:
            if CourseEnrollment.is_enrolled(user, dest_key):
                # Un Enroll from source course but don't mess
                # with the enrollment in the destination course.
                CourseEnrollment.unenroll(user, source_key)
                print("Unenrolled {} from {}".format(user.username, source_key.to_deprecated_string()))
                msg = "Skipping {}, already enrolled in destination course {}"
                print(msg.format(user.username, dest_key.to_deprecated_string()))
                continue

            print("Moving {}.".format(user.username))
            # Find the old enrollment.
            enrollment = CourseEnrollment.objects.get(
                user=user,
                course_id=source_key
            )

            # Move the Student between the classes.
            mode = enrollment.mode
            old_is_active = enrollment.is_active
            CourseEnrollment.unenroll(user, source_key)
            new_enrollment = CourseEnrollment.enroll(user, dest_key, mode=mode)

            # Unenroll from the new coures if the user had unenrolled
            # form the old course.
            if not old_is_active:
                new_enrollment.update_enrollment(is_active=False)

            if mode == 'verified':
                try:
                    certificate_item = CertificateItem.objects.get(
                        course_id=source_key,
                        course_enrollment=enrollment
                    )
                except CertificateItem.DoesNotExist:
                    print("No certificate for {}".format(user))
                    continue

                certificate_item.course_id = dest_key
                certificate_item.course_enrollment = new_enrollment
                certificate_item.save()
Ejemplo n.º 34
0
    def test_graphicslidertool_import(self):
        '''
        Check to see if definition_from_xml in gst_module.py
        works properly.  Pulls data from the graphic_slider_tool directory
        in the test data directory.
        '''
        modulestore = XMLModuleStore(DATA_DIR, course_dirs=['graphic_slider_tool'])

        sa_id = SlashSeparatedCourseKey("edX", "gst_test", "2012_Fall")
        location = sa_id.make_usage_key("graphical_slider_tool", "sample_gst")
        gst_sample = modulestore.get_item(location)
        render_string_from_sample_gst_xml = """
        <slider var="a" style="width:400px;float:left;"/>\
<plot style="margin-top:15px;margin-bottom:15px;"/>""".strip()
        self.assertIn(render_string_from_sample_gst_xml, gst_sample.data)
Ejemplo n.º 35
0
    def lms_link_test(self):
        """ Tests get_lms_link_for_item. """
        course_key = SlashSeparatedCourseKey("mitX", "101", "test")
        location = course_key.make_usage_key("vertical", "contacting_us")
        link = utils.get_lms_link_for_item(location, False)
        self.assertEquals(link, "//localhost:8000/courses/mitX/101/test/jump_to/i4x://mitX/101/vertical/contacting_us")

        # test preview
        link = utils.get_lms_link_for_item(location, True)
        self.assertEquals(link, "//preview/courses/mitX/101/test/jump_to/i4x://mitX/101/vertical/contacting_us")

        # now test with the course' location
        location = course_key.make_usage_key("course", "test")
        link = utils.get_lms_link_for_item(location)
        self.assertEquals(link, "//localhost:8000/courses/mitX/101/test/jump_to/i4x://mitX/101/course/test")
Ejemplo n.º 36
0
    def test_enrollment(self):
        user = User.objects.create_user("joe", "*****@*****.**", "password")
        course_id = SlashSeparatedCourseKey("edX", "Test101", "2013")
        course_id_partial = SlashSeparatedCourseKey("edX", "Test101", None)

        # Test basic enrollment
        self.assertFalse(CourseEnrollment.is_enrolled(user, course_id))
        self.assertFalse(
            CourseEnrollment.is_enrolled_by_partial(user, course_id_partial))
        CourseEnrollment.enroll(user, course_id)
        self.assertTrue(CourseEnrollment.is_enrolled(user, course_id))
        self.assertTrue(
            CourseEnrollment.is_enrolled_by_partial(user, course_id_partial))
        self.assert_enrollment_event_was_emitted(user, course_id)

        # Enrolling them again should be harmless
        CourseEnrollment.enroll(user, course_id)
        self.assertTrue(CourseEnrollment.is_enrolled(user, course_id))
        self.assertTrue(
            CourseEnrollment.is_enrolled_by_partial(user, course_id_partial))
        self.assert_no_events_were_emitted()

        # Now unenroll the user
        CourseEnrollment.unenroll(user, course_id)
        self.assertFalse(CourseEnrollment.is_enrolled(user, course_id))
        self.assertFalse(
            CourseEnrollment.is_enrolled_by_partial(user, course_id_partial))
        self.assert_unenrollment_event_was_emitted(user, course_id)

        # Unenrolling them again should also be harmless
        CourseEnrollment.unenroll(user, course_id)
        self.assertFalse(CourseEnrollment.is_enrolled(user, course_id))
        self.assertFalse(
            CourseEnrollment.is_enrolled_by_partial(user, course_id_partial))
        self.assert_no_events_were_emitted()

        # The enrollment record should still exist, just be inactive
        enrollment_record = CourseEnrollment.objects.get(user=user,
                                                         course_id=course_id)
        self.assertFalse(enrollment_record.is_active)

        # Make sure mode is updated properly if user unenrolls & re-enrolls
        enrollment = CourseEnrollment.enroll(user, course_id, "verified")
        self.assertEquals(enrollment.mode, "verified")
        CourseEnrollment.unenroll(user, course_id)
        enrollment = CourseEnrollment.enroll(user, course_id, "audit")
        self.assertTrue(CourseEnrollment.is_enrolled(user, course_id))
        self.assertEquals(enrollment.mode, "audit")
Ejemplo n.º 37
0
    def test_graphicslidertool_import(self):
        '''
        Check to see if definition_from_xml in gst_module.py
        works properly.  Pulls data from the graphic_slider_tool directory
        in the test data directory.
        '''
        modulestore = XMLModuleStore(DATA_DIR,
                                     course_dirs=['graphic_slider_tool'])

        sa_id = SlashSeparatedCourseKey("edX", "gst_test", "2012_Fall")
        location = sa_id.make_usage_key("graphical_slider_tool", "sample_gst")
        gst_sample = modulestore.get_item(location)
        render_string_from_sample_gst_xml = """
        <slider var="a" style="width:400px;float:left;"/>\
<plot style="margin-top:15px;margin-bottom:15px;"/>""".strip()
        self.assertIn(render_string_from_sample_gst_xml, gst_sample.data)
Ejemplo n.º 38
0
    def clean_course_id(self):
        """Validate the course id"""
        cleaned_id = self.cleaned_data["course_id"]
        try:
            course_key = CourseKey.from_string(cleaned_id)
        except InvalidKeyError:
            try:
                course_key = SlashSeparatedCourseKey.from_deprecated_string(cleaned_id)
            except InvalidKeyError:
                msg = u'Course id invalid.' 
                msg += u' --- Entered course id was: "{0}". '.format(cleaned_id)
                msg += 'Please recheck that you have supplied a valid course id.'
                raise forms.ValidationError(msg)

        if not modulestore().has_course(course_key):
            msg = u'COURSE NOT FOUND'
            msg += u' --- Entered course id was: "{0}". '.format(course_key.to_deprecated_string())
            msg += 'Please recheck that you have supplied a valid course id.'
            raise forms.ValidationError(msg)

        # Now, try and discern if it is a Studio course - HTML editor doesn't work with XML courses
        is_studio_course = modulestore().get_modulestore_type(course_key) != XML_MODULESTORE_TYPE
        if not is_studio_course:
            msg = "Course Email feature is only available for courses authored in Studio. "
            msg += '"{0}" appears to be an XML backed course.'.format(course_key.to_deprecated_string())
            raise forms.ValidationError(msg)

        return course_key
Ejemplo n.º 39
0
def remove_user_from_cohort(request, course_key, cohort_id):
    """
    Expects 'username': username in POST data.

    Return json dict of:

    {'success': True} or
    {'success': False,
     'msg': error_msg}
    """
    # this is a string when we get it here
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_key)
    get_course_with_access(request.user, 'staff', course_key)

    username = request.POST.get('username')
    if username is None:
        return json_http_response({'success': False,
                                   'msg': 'No username specified'})

    cohort = cohorts.get_cohort_by_id(course_key, cohort_id)
    try:
        user = User.objects.get(username=username)
        cohort.users.remove(user)
        return json_http_response({'success': True})
    except User.DoesNotExist:
        log.debug('no user')
        return json_http_response({'success': False,
                                   'msg': "No user '{0}'".format(username)})
Ejemplo n.º 40
0
    def handle(self, *args, **options):

        dry_run = options['dry_run']
        task_number = options['task_number']

        if len(args) == 4:
            course_id = SlashSeparatedCourseKey.from_deprecated_string(args[0])
            location = course_id.make_usage_key_from_deprecated_string(args[1])
            students_ids = [line.strip() for line in open(args[2])]
            hostname = args[3]
        else:
            print self.help
            return

        try:
            course = get_course(course_id)
        except ValueError as err:
            print err
            return

        descriptor = modulestore().get_item(location, depth=0)
        if descriptor is None:
            print "Location not found in course"
            return

        if dry_run:
            print "Doing a dry run."

        students = User.objects.filter(id__in=students_ids).order_by('username')
        print "Number of students: {0}".format(students.count())

        for student in students:
            post_submission_for_student(student, course, location, task_number, dry_run=dry_run, hostname=hostname)
Ejemplo n.º 41
0
def course_info(request, course_id):
    """
    Display the course's info.html, or 404 if there is no such course.

    Assumes the course_id is in a valid format.
    """
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course = get_course_with_access(request.user, 'load', course_key)
    staff_access = has_access(request.user, 'staff', course)
    masq = setup_masquerade(request, staff_access)    # allow staff to toggle masquerade on info page
    reverifications = fetch_reverify_banner_info(request, course_key)
    studio_url = get_studio_url(course_key, 'course_info')

    context = {
        'request': request,
        'course_id': course_key.to_deprecated_string(),
        'cache': None,
        'course': course,
        'staff_access': staff_access,
        'masquerade': masq,
        'studio_url': studio_url,
        'reverifications': reverifications,
    }

    return render_to_response('courseware/info.html', context)
Ejemplo n.º 42
0
    def handle(self, *args, **options):
        if not options['course_id']:
            raise CommandError("You must specify a course id for this command")
        if not options['from_mode'] or not options['to_mode']:
            raise CommandError('You must specify a "to" and "from" mode as parameters')

        try:
            course_key = CourseKey.from_string(options['course_id'])
        except InvalidKeyError:
            course_key = SlashSeparatedCourseKey.from_deprecated_string(options['course_id'])

        filter_args = dict(
            course_id=course_key,
            mode=options['from_mode']
        )
        if options['user']:
            if '@' in options['user']:
                user = User.objects.get(email=options['user'])
            else:
                user = User.objects.get(username=options['user'])
            filter_args['user'] = user
        enrollments = CourseEnrollment.objects.filter(**filter_args)
        if options['noop']:
            print "Would have changed {num_enrollments} students from {from_mode} to {to_mode}".format(
                num_enrollments=enrollments.count(),
                from_mode=options['from_mode'],
                to_mode=options['to_mode']
            )
        else:
            for enrollment in enrollments:
                enrollment.update_enrollment(mode=options['to_mode'])
                enrollment.save()
    def handle(self, *args, **options):
        if len(args) != 1:
            raise CommandError("course_id not specified")

        # Get the modulestore

        try:
            name = options["modulestore"]
            store = modulestore(name)
        except KeyError:
            raise CommandError("Unknown modulestore {}".format(name))

        # Get the course data

        try:
            course_id = SlashSeparatedCourseKey.from_deprecated_string(args[0])
        except InvalidKeyError:
            raise CommandError("Invalid course_id")

        course = store.get_course(course_id)
        if course is None:
            raise CommandError("Invalid course_id")

        # precompute inherited metadata at the course level, if needed:
        if options["inherited"]:
            compute_inherited_metadata(course)

        # Convert course data to dictionary and dump it as JSON to stdout

        info = dump_module(course, inherited=options["inherited"], defaults=options["inherited_defaults"])

        return json.dumps(info, indent=2, sort_keys=True)
    def test_per_course_anonymized_id(self, descriptor_class):
        self.assertEquals(
            # This value is set by observation, so that later changes to the student
            # id computation don't break old data
            'e3b0b940318df9c14be59acb08e78af5',
            self._get_anonymous_id(
                SlashSeparatedCourseKey('MITx', '6.00x', '2012_Fall'),
                descriptor_class))

        self.assertEquals(
            # This value is set by observation, so that later changes to the student
            # id computation don't break old data
            'f82b5416c9f54b5ce33989511bb5ef2e',
            self._get_anonymous_id(
                SlashSeparatedCourseKey('MITx', '6.00x', '2013_Spring'),
                descriptor_class))
Ejemplo n.º 45
0
def show_unit_extensions(request, course_id):
    """
    Shows all of the students which have due date extensions for the given unit.
    """
    course = get_course_by_id(SlashSeparatedCourseKey.from_deprecated_string(course_id))
    unit = find_unit(course, request.GET.get('url'))
    return JsonResponse(dump_module_extensions(course, unit))
Ejemplo n.º 46
0
    def test_xml_course_authorization(self):
        course_id = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
        # Assert this is an XML course
        self.assertEqual(modulestore().get_modulestore_type(course_id), XML_MODULESTORE_TYPE)

        form_data = {'course_id': course_id.to_deprecated_string(), 'email_enabled': True}
        form = CourseAuthorizationAdminForm(data=form_data)
        # Validation shouldn't work
        self.assertFalse(form.is_valid())

        msg = u"Course Email feature is only available for courses authored in Studio. "
        msg += u'"{0}" appears to be an XML backed course.'.format(course_id.to_deprecated_string())
        self.assertEquals(msg, form._errors['course_id'][0])  # pylint: disable=protected-access

        with self.assertRaisesRegexp(ValueError, "The CourseAuthorization could not be created because the data didn't validate."):
            form.save()
Ejemplo n.º 47
0
    def test_form_typo(self):
        # Munge course id
        bad_id = SlashSeparatedCourseKey(u'Broken{}'.format(self.course.id.org), '', self.course.id.run + '_typo')

        form_data = {'course_id': bad_id.to_deprecated_string(), 'email_enabled': True}
        form = CourseAuthorizationAdminForm(data=form_data)
        # Validation shouldn't work
        self.assertFalse(form.is_valid())

        msg = u'COURSE NOT FOUND'
        msg += u' --- Entered course id was: "{0}". '.format(bad_id.to_deprecated_string())
        msg += 'Please recheck that you have supplied a valid course id.'
        self.assertEquals(msg, form._errors['course_id'][0])  # pylint: disable=protected-access

        with self.assertRaisesRegexp(ValueError, "The CourseAuthorization could not be created because the data didn't validate."):
            form.save()
Ejemplo n.º 48
0
def jump_to(request, course_id, location):
    """
    Show the page that contains a specific location.

    If the location is invalid or not in any class, return a 404.

    Otherwise, delegates to the index view to figure out whether this user
    has access, and what they should see.
    """
    try:
        course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
        usage_key = course_key.make_usage_key_from_deprecated_string(location)
    except InvalidKeyError:
        raise Http404(u"Invalid course_key or usage_key")
    try:
        (course_key, chapter, section, position) = path_to_location(modulestore(), usage_key)
    except ItemNotFoundError:
        raise Http404(u"No data at this location: {0}".format(usage_key))
    except NoPathToItem:
        raise Http404(u"This location is not in any class: {0}".format(usage_key))

    # choose the appropriate view (and provide the necessary args) based on the
    # args provided by the redirect.
    # Rely on index to do all error handling and access control.
    if chapter is None:
        return redirect('courseware', course_id=course_key.to_deprecated_string())
    elif section is None:
        return redirect('courseware_chapter', course_id=course_key.to_deprecated_string(), chapter=chapter)
    elif position is None:
        return redirect('courseware_section', course_id=course_key.to_deprecated_string(), chapter=chapter, section=section)
    else:
        return redirect('courseware_position', course_id=course_key.to_deprecated_string(), chapter=chapter, section=section, position=position)
Ejemplo n.º 49
0
    def clean_course_id(self):
        """Validate the course id"""
        cleaned_id = self.cleaned_data["course_id"]
        try:
            course_key = CourseKey.from_string(cleaned_id)
        except InvalidKeyError:
            try:
                course_key = SlashSeparatedCourseKey.from_deprecated_string(
                    cleaned_id)
            except InvalidKeyError:
                msg = u'Course id invalid.'
                msg += u' --- Entered course id was: "{0}". '.format(
                    cleaned_id)
                msg += 'Please recheck that you have supplied a valid course id.'
                raise forms.ValidationError(msg)

        if not modulestore().has_course(course_key):
            msg = u'COURSE NOT FOUND'
            msg += u' --- Entered course id was: "{0}". '.format(
                course_key.to_deprecated_string())
            msg += 'Please recheck that you have supplied a valid course id.'
            raise forms.ValidationError(msg)

        # Now, try and discern if it is a Studio course - HTML editor doesn't work with XML courses
        is_studio_course = modulestore().get_modulestore_type(
            course_key) != XML_MODULESTORE_TYPE
        if not is_studio_course:
            msg = "Course Email feature is only available for courses authored in Studio. "
            msg += '"{0}" appears to be an XML backed course.'.format(
                course_key.to_deprecated_string())
            raise forms.ValidationError(msg)

        return course_key
Ejemplo n.º 50
0
    def setUp(self):
        self.client = Client()

        # Mocks
        api.api_enabled = self.mock_api_enabled(True)

        # Create two accounts
        self.password = '******'
        self.student = User.objects.create_user('student', '*****@*****.**', self.password)
        self.student2 = User.objects.create_user('student2', '*****@*****.**', self.password)
        self.instructor = User.objects.create_user('instructor', '*****@*****.**', self.password)
        self.course_key = SlashSeparatedCourseKey('HarvardX', 'CB22x', 'The_Ancient_Greek_Hero')
        self.note = {
            'user': self.student,
            'course_id': self.course_key,
            'uri': '/',
            'text': 'foo',
            'quote': 'bar',
            'range_start': 0,
            'range_start_offset': 0,
            'range_end': 100,
            'range_end_offset': 0,
            'tags': 'a,b,c'
        }

        # Make sure no note with this ID ever exists for testing purposes
        self.NOTE_ID_DOES_NOT_EXIST = 99999
Ejemplo n.º 51
0
def static_tab(request, course_id, tab_slug):
    """
    Display the courses tab with the given name.

    Assumes the course_id is in a valid format.
    """
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course = get_course_with_access(request.user, 'load', course_key)

    tab = CourseTabList.get_tab_by_slug(course.tabs, tab_slug)
    if tab is None:
        raise Http404

    contents = get_static_tab_contents(
        request,
        course,
        tab
    )
    if contents is None:
        raise Http404

    return render_to_response('courseware/static_tab.html', {
        'course': course,
        'tab': tab,
        'tab_contents': contents,
    })
Ejemplo n.º 52
0
def add_cohort(request, course_key):
    """
    Return json of dict:
    {'success': True,
     'cohort': {'id': id,
                'name': name}}

                or

    {'success': False,
     'msg': error_msg} if there's an error
    """
    # this is a string when we get it here
    course_key = SlashSeparatedCourseKey.from_deprecated_string(course_key)

    get_course_with_access(request.user, 'staff', course_key)

    name = request.POST.get("name")
    if not name:
        return json_http_response({'success': False,
                                'msg': "No name specified"})

    try:
        cohort = cohorts.add_cohort(course_key, name)
    except ValueError as err:
        return json_http_response({'success': False,
                                'msg': str(err)})

    return json_http_response({'success': 'True',
                            'cohort': {
                                'id': cohort.id,
                                'name': cohort.name
                                }})
Ejemplo n.º 53
0
def send_email(request, course_id):
    """
    Send an email to self, staff, or everyone involved in a course.
    Query Parameters:
    - 'send_to' specifies what group the email should be sent to
       Options are defined by the CourseEmail model in
       lms/djangoapps/bulk_email/models.py
    - 'subject' specifies email's subject
    - 'message' specifies email's content
    """
    course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)

    if not bulk_email_is_enabled_for_course(course_id):
        return HttpResponseForbidden("Email is not enabled for this course.")

    send_to = request.POST.get("send_to")
    subject = request.POST.get("subject")
    message = request.POST.get("message")

    # Create the CourseEmail object.  This is saved immediately, so that
    # any transaction that has been pending up to this point will also be
    # committed.
    email = CourseEmail.create(course_id, request.user, send_to, subject, message)

    # Submit the task, so that the correct InstructorTask object gets created (for monitoring purposes)
    instructor_task.api.submit_bulk_course_email(request, course_id, email.id)  # pylint: disable=E1101

    response_payload = {
        'course_id': course_id.to_deprecated_string(),
        'success': True,
    }
    return JsonResponse(response_payload)
Ejemplo n.º 54
0
def progress(request, course_id, student_id=None):
    """
    Wraps "_progress" with the manual_transaction context manager just in case
    there are unanticipated errors.
    """
    with grades.manual_transaction():
        return _progress(request, SlashSeparatedCourseKey.from_deprecated_string(course_id), student_id)
Ejemplo n.º 55
0
def get_anon_ids(request, course_id):  # pylint: disable=W0613
    """
    Respond with 2-column CSV output of user-id, anonymized-user-id
    """
    # TODO: the User.objects query and CSV generation here could be
    # centralized into analytics. Currently analytics has similar functionality
    # but not quite what's needed.
    course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    def csv_response(filename, header, rows):
        """Returns a CSV http response for the given header and rows (excel/utf-8)."""
        response = HttpResponse(mimetype='text/csv')
        response['Content-Disposition'] = 'attachment; filename={0}'.format(filename)
        writer = csv.writer(response, dialect='excel', quotechar='"', quoting=csv.QUOTE_ALL)
        # In practice, there should not be non-ascii data in this query,
        # but trying to do the right thing anyway.
        encoded = [unicode(s).encode('utf-8') for s in header]
        writer.writerow(encoded)
        for row in rows:
            encoded = [unicode(s).encode('utf-8') for s in row]
            writer.writerow(encoded)
        return response

    students = User.objects.filter(
        courseenrollment__course_id=course_id,
    ).order_by('id')
    header = ['User ID', 'Anonymized user ID', 'Course Specific Anonymized user ID']
    rows = [[s.id, unique_id_for_user(s), anonymous_id_for_user(s, course_id)] for s in students]
    return csv_response(course_id.to_deprecated_string().replace('/', '-') + '-anon-ids.csv', header, rows)
Ejemplo n.º 56
0
class TestNewInstructorDashboardEmailViewXMLBacked(ModuleStoreTestCase):
    """
    Check for email view on the new instructor dashboard
    """
    def setUp(self):
        self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')

        # Create instructor account
        instructor = AdminFactory.create()
        self.client.login(username=instructor.username, password="******")

        # URL for instructor dash
        self.url = reverse('instructor_dashboard', kwargs={'course_id': self.course_key.to_deprecated_string()})
        # URL for email view
        self.email_link = '<a href="" data-section="send_email">Email</a>'

    # The flag is enabled, and since REQUIRE_COURSE_EMAIL_AUTH is False, all courses should
    # be authorized to use email. But the course is not Mongo-backed (should not work)
    @patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True, 'REQUIRE_COURSE_EMAIL_AUTH': False})
    def test_email_flag_true_mongo_false(self):
        response = self.client.get(self.url)
        self.assertFalse(self.email_link in response.content)

    # The flag is disabled and the course is not Mongo-backed (should not work)
    @patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': False, 'REQUIRE_COURSE_EMAIL_AUTH': False})
    def test_email_flag_false_mongo_false(self):
        response = self.client.get(self.url)
        self.assertFalse(self.email_link in response.content)