def test_has_changes(self):
        """
        Tests that has_changes() only returns true when changes are present
        """
        draft_course = CourseLocator(
            org='testx', course='GreekHero', run='run', branch=ModuleStoreEnum.BranchName.draft
        )
        head = draft_course.make_usage_key('course', 'head12345')
        dummy_user = ModuleStoreEnum.UserID.test

        # Not yet published, so changes are present
        self.assertTrue(self.module_store.has_changes(head))

        # Publish and verify that there are no unpublished changes
        self.module_store.publish(head, dummy_user)
        self.assertFalse(self.module_store.has_changes(head))

        # Change the course, then check that there now are changes
        course = self.module_store.get_item(head)
        course.show_calculator = not course.show_calculator
        self.module_store.update_item(course, dummy_user)
        self.assertTrue(self.module_store.has_changes(head))

        # Publish and verify again
        self.module_store.publish(head, dummy_user)
        self.assertFalse(self.module_store.has_changes(head))
Exemplo n.º 2
0
 def test_make_usage_key_from_deprecated_string_roundtrip(self, url):
     course_key = CourseLocator('org', 'course', 'run')
     with self.assertDeprecationWarning(count=1):
         self.assertEqual(
             url,
             text_type(course_key.make_usage_key_from_deprecated_string(url))
         )
Exemplo n.º 3
0
class TestSortedAssetList(unittest.TestCase):
    """
    Tests the SortedAssetList class.
    """
    shard = 1

    def setUp(self):
        super(TestSortedAssetList, self).setUp()
        asset_list = [dict(zip(AssetStoreTestData.asset_fields, asset)) for asset in AssetStoreTestData.all_asset_data]
        self.sorted_asset_list_by_filename = SortedAssetList(iterable=asset_list)
        self.sorted_asset_list_by_last_edit = SortedAssetList(iterable=asset_list, key=lambda x: x['edited_on'])
        self.course_key = CourseLocator('org', 'course', 'run')

    def test_exception_on_bad_sort(self):
        asset_key = self.course_key.make_asset_key('asset', 'pic1.jpg')
        with self.assertRaises(IncorrectlySortedList):
            __ = self.sorted_asset_list_by_last_edit.find(asset_key)

    def test_find(self):
        asset_key = self.course_key.make_asset_key('asset', 'asset.txt')
        self.assertEquals(self.sorted_asset_list_by_filename.find(asset_key), 0)
        asset_key_last = self.course_key.make_asset_key('asset', 'weather_patterns.bmp')
        self.assertEquals(
            self.sorted_asset_list_by_filename.find(asset_key_last), len(AssetStoreTestData.all_asset_data) - 1
        )
Exemplo n.º 4
0
 def test_course_constructor_package_id_no_branch(self):
     org = 'mit.eecs'
     offering = '6002x'
     testurn = '{}+{}'.format(org, offering)
     testobj = CourseLocator(org=org, offering=offering)
     self.check_course_locn_fields(testobj, org=org, offering=offering)
     self.assertEqual(testobj._to_string(), testurn)
 def initialize_ids(self):
     """Define set of id values for use in tests."""
     course_key = CourseLocator(org='FooX', course='1.23x', run='2013_Spring')
     self.course_id = unicode(course_key)
     self.org_id = course_key.org
     block_id = "9cee77a606ea4c1aa5440e0ea5d0f618"
     self.problem_id = unicode(course_key.make_usage_key("problem", block_id))
     self.answer_id = "{block_id}_2_1".format(block_id=block_id)
     self.second_answer_id = "{block_id}_3_1".format(block_id=block_id)
Exemplo n.º 6
0
 def test_course_constructor_package_id_no_branch(self):
     org = 'mit.eecs'
     course = '6002x'
     run = '2014_T2'
     testurn = '{}+{}+{}'.format(org, course, run)
     testobj = CourseLocator(org=org, course=course, run=run)
     self.check_course_locn_fields(testobj, org=org, course=course, run=run)
     # Allow access to _to_string
     # pylint: disable=protected-access
     self.assertEqual(testobj._to_string(), testurn)
Exemplo n.º 7
0
def instantiate_descriptor(**field_data):
    """
    Instantiate descriptor with most properties.
    """
    system = get_test_descriptor_system()
    course_key = CourseLocator('org', 'course', 'run')
    usage_key = course_key.make_usage_key('video', 'SampleProblem')
    return system.construct_xblock_from_class(
        VideoDescriptor,
        scope_ids=ScopeIds(None, None, usage_key, usage_key),
        field_data=DictFieldData(field_data),
    )
Exemplo n.º 8
0
def survey_ajax(request):
    """Ajax call to submit a survey."""
    MAX_CHARACTER_LENGTH = 1000

    course_id = request.POST.get('course_id')
    unit_id = request.POST.get('unit_id')
    survey_name = request.POST.get('survey_name')
    survey_answer = request.POST.get('survey_answer')

    if not course_id or not unit_id:
        log.warning("Illegal parameter. course_id=%s, unit_id=%s" % (course_id, unit_id))
        raise Http404
    if not survey_name:
        log.warning("Illegal parameter. survey_name=%s" % survey_name)
        raise Http404
    if not survey_answer:
        log.warning("Illegal parameter. survey_answer=%s" % survey_answer)
        raise Http404
    try:
        obj = json.loads(survey_answer)
    except:
        log.warning("Illegal parameter. survey_answer=%s" % survey_answer)
        raise Http404
    for k, v in obj.iteritems():
        if len(v) > MAX_CHARACTER_LENGTH:
            log.warning("%s cannot be more than %d characters long." % (k, MAX_CHARACTER_LENGTH))
            raise Http404

    try:
        submission = SurveySubmission.objects.filter(
            course_id=CourseLocator.from_string(course_id),
            unit_id=unit_id,
            user=request.user
        ).order_by('created')[0:1].get()
    except SurveySubmission.DoesNotExist:
        pass
    else:
        return JsonResponse({
            'success': False,
            'survey_answer': submission.get_survey_answer(),
        })

    submission = SurveySubmission(
        course_id=CourseLocator.from_string(course_id),
        unit_id=unit_id,
        user=request.user,
        survey_name=survey_name,
        survey_answer=survey_answer,
    )
    submission.save()
    return JsonResponse({'success': True})
Exemplo n.º 9
0
 def test_course_constructor_package_id_separate_branch(self):
     org = 'mit.eecs'
     offering = '6002x'
     test_branch = 'published'
     expected_urn = '{}+{}+{}+{}'.format(org, offering, CourseLocator.BRANCH_PREFIX, test_branch)
     testobj = CourseLocator(org=org, offering=offering, branch=test_branch)
     self.check_course_locn_fields(
         testobj,
         org=org,
         offering=offering,
         branch=test_branch,
     )
     self.assertEqual(testobj.branch, test_branch)
     self.assertEqual(testobj._to_string(), expected_urn)
Exemplo n.º 10
0
    def handle(self, *args, **options):
        create_report = options['create']
        delete_report = options['delete']

        if len(args) != 1:
            raise CommandError('"course_id" is not specified')
        elif create_report and delete_report:
            raise CommandError(
                'Cannot specify "-c" option and "-d" option at the same time.')

        course_id = args[0]
        try:
            course_id = CourseLocator.from_string(course_id)
        except InvalidKeyError:
            raise CommandError("'{}' is an invalid course_id".format(course_id))
        if not modulestore().get_course(course_id):
            raise CommandError("The specified course does not exist.")

        if delete_report:
            try:
                delete_pgreport_csv(course_id)
            except NotFoundError:
                raise CommandError("CSV not found.")
            call_command('create_report_task', *['clear_cache'], **{'course_id': course_id.to_deprecated_string()})
        elif create_report:
            call_command('create_report_task', *['create'], **{'course_id': course_id.to_deprecated_string()})
        else:
            try: 
                get_pgreport_csv(course_id)
            except NotFoundError:
                raise CommandError("CSV not found.")
Exemplo n.º 11
0
    def import_test_course(self, solution_attribute=None, solution_element=None):
        """
        Import the test course with the sga unit
        """
        # adapted from edx-platform/cms/djangoapps/contentstore/management/commands/tests/test_cleanup_assets.py
        root = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
        input_dir = os.path.join(root, "test_data")

        temp_dir = tempfile.mkdtemp()
        self.addCleanup(lambda: shutil.rmtree(temp_dir))

        xml_dir = os.path.join(temp_dir, "xml")
        shutil.copytree(input_dir, xml_dir)

        with open(os.path.join(xml_dir, "2017_SGA", "vertical", "vertical.xml"), "w") as f:
            f.write(self.make_test_vertical(solution_attribute, solution_element))

        store = modulestore()
        import_course_from_xml(
            store,
            'sga_user',
            xml_dir,
        )

        return store.get_course(CourseLocator.from_string('SGAU/SGA101/course'))
Exemplo n.º 12
0
def recalculate_subsection_grade_v2(**kwargs):
    """
    Updates a saved subsection grade.

    Arguments:
        user_id (int): id of applicable User object
        course_id (string): identifying the course
        usage_id (string): identifying the course block
        only_if_higher (boolean): indicating whether grades should
            be updated only if the new raw_earned is higher than the
            previous value.
        expected_modified_time (serialized timestamp): indicates when the task
            was queued so that we can verify the underlying data update.
        score_deleted (boolean): indicating whether the grade change is
            a result of the problem's score being deleted.
        event_transaction_id(string): uuid identifying the current
            event transaction.
        event_transaction_type(string): human-readable type of the
            event at the root of the current event transaction.
    """
    try:
        course_key = CourseLocator.from_string(kwargs['course_id'])
        if not PersistentGradesEnabledFlag.feature_enabled(course_key):
            return

        score_deleted = kwargs['score_deleted']
        scored_block_usage_key = UsageKey.from_string(kwargs['usage_id']).replace(course_key=course_key)
        expected_modified_time = from_timestamp(kwargs['expected_modified_time'])

        # The request cache is not maintained on celery workers,
        # where this code runs. So we take the values from the
        # main request cache and store them in the local request
        # cache. This correlates model-level grading events with
        # higher-level ones.
        set_event_transaction_id(kwargs.pop('event_transaction_id', None))
        set_event_transaction_type(kwargs.pop('event_transaction_type', None))

        # Verify the database has been updated with the scores when the task was
        # created. This race condition occurs if the transaction in the task
        # creator's process hasn't committed before the task initiates in the worker
        # process.
        if not _has_database_updated_with_new_score(
                kwargs['user_id'], scored_block_usage_key, expected_modified_time, score_deleted,
        ):
            raise _retry_recalculate_subsection_grade(**kwargs)

        _update_subsection_grades(
            course_key,
            scored_block_usage_key,
            kwargs['only_if_higher'],
            kwargs['user_id'],
        )

    except Exception as exc:   # pylint: disable=broad-except
        if not isinstance(exc, KNOWN_RETRY_ERRORS):
            log.info("tnl-6244 grades unexpected failure: {}. kwargs={}".format(
                repr(exc),
                kwargs
            ))
        raise _retry_recalculate_subsection_grade(exc=exc, **kwargs)
Exemplo n.º 13
0
    def set_up_assets(self, deprecated):
        """
        Setup contentstore w/ proper overriding of deprecated.
        """
        # since MongoModuleStore and MongoContentStore are basically assumed to be together, create this class
        # as well
        self.contentstore = MongoContentStore(HOST, DB, port=PORT)
        self.addCleanup(self.contentstore._drop_database)  # pylint: disable=protected-access

        AssetLocator.deprecated = deprecated
        CourseLocator.deprecated = deprecated

        self.course1_key = CourseLocator('test', 'asset_test', '2014_07')
        self.course2_key = CourseLocator('test', 'asset_test2', '2014_07')

        self.course1_files = ['contains.sh', 'picture1.jpg', 'picture2.jpg']
        self.course2_files = ['picture1.jpg', 'picture3.jpg', 'door_2.ogg']

        def load_assets(course_key, files):
            locked = False
            for filename in files:
                asset_key = course_key.make_asset_key('asset', filename)
                self.save_asset(filename, asset_key, filename, locked)
                locked = not locked

        load_assets(self.course1_key, self.course1_files)
        load_assets(self.course2_key, self.course2_files)
Exemplo n.º 14
0
 def get(self, request, course_id):
     """Implements the GET method as described in the class docstring."""
     course_key = CourseLocator.from_string(course_id)
     course = get_course_with_access(request.user, 'load_forum', course_key)
     if not any([isinstance(tab, DiscussionTab) for tab in course.tabs]):
         raise Http404
     return Response(get_course_topics(course, request.user))
Exemplo n.º 15
0
 def clean_course_id(self):
     """Validate course_id"""
     value = self.cleaned_data["course_id"]
     try:
         return CourseLocator.from_string(value)
     except InvalidKeyError:
         raise ValidationError("'{}' is not a valid course id".format(value))
Exemplo n.º 16
0
    def test_gitlog_pagination_out_of_range_invalid(self):
        """
        Make sure the pagination behaves properly when the requested page is out
        of range.
        """

        self._setstaff_login()

        mongoengine.connect(TEST_MONGODB_LOG['db'])

        for _ in xrange(15):
            CourseImportLog(
                course_id=CourseLocator.from_string("test/test/test"),
                location="location",
                import_log="import_log",
                git_log="git_log",
                repo_dir="repo_dir",
                created=datetime.now()
            ).save()

        for page, expected in [(-1, 1), (1, 1), (2, 2), (30, 2), ('abc', 1)]:
            response = self.client.get(
                '{}?page={}'.format(
                    reverse('gitlogs'),
                    page
                )
            )
            self.assertIn(
                u'Page {} of 2'.format(expected),
                response.content.decode(response.charset)
            )

        CourseImportLog.objects.delete()
Exemplo n.º 17
0
    def initdb(self, default):
        """
        Initialize the database and create one test course in it
        """
        # set the default modulestore
        store_configs = self.options['stores']
        for index in range(len(store_configs)):
            if store_configs[index]['NAME'] == default:
                if index > 0:
                    store_configs[index], store_configs[0] = store_configs[0], store_configs[index]
                break
        self.store = MixedModuleStore(None, create_modulestore_instance=create_modulestore_instance, **self.options)
        self.addCleanup(self.store.close_all_connections)

        # convert to CourseKeys
        self.course_locations = {
            course_id: CourseLocator.from_string(course_id)
            for course_id in [self.MONGO_COURSEID, self.XML_COURSEID1, self.XML_COURSEID2]
        }
        # and then to the root UsageKey
        self.course_locations = {
            course_id: course_key.make_usage_key('course', course_key.run)
            for course_id, course_key in self.course_locations.iteritems()  # pylint: disable=maybe-no-member
        }
        if default == 'split':
            self.fake_location = CourseLocator(
                'foo', 'bar', 'slowly', branch=ModuleStoreEnum.BranchName.draft
            ).make_usage_key('vertical', 'baz')
        else:
            self.fake_location = Location('foo', 'bar', 'slowly', 'vertical', 'baz')
        self.xml_chapter_location = self.course_locations[self.XML_COURSEID1].replace(
            category='chapter', name='Overview'
        )
        self._create_course(default, self.course_locations[self.MONGO_COURSEID].course_key)
Exemplo n.º 18
0
def recalculate_subsection_grade_handler(sender, **kwargs):  # pylint: disable=unused-argument
    """
    Consume the SCORE_CHANGED signal and trigger an update.
    This method expects that the kwargs dictionary will contain the following
    entries (See the definition of SCORE_CHANGED):
       - points_possible: Maximum score available for the exercise
       - points_earned: Score obtained by the user
       - user: User object
       - course_id: Unicode string representing the course
       - usage_id: Unicode string indicating the courseware instance
    """
    try:
        course_id = kwargs.get('course_id', None)
        usage_id = kwargs.get('usage_id', None)
        student = kwargs.get('user', None)

        course_key = CourseLocator.from_string(course_id)
        if not PersistentGradesEnabledFlag.feature_enabled(course_key):
            return

        usage_key = UsageKey.from_string(usage_id).replace(course_key=course_key)

        from lms.djangoapps.grades.new.subsection_grade import SubsectionGradeFactory
        SubsectionGradeFactory(student).update(usage_key, course_key)
    except Exception as ex:  # pylint: disable=broad-except
        log.exception(
            u"Failed to process SCORE_CHANGED signal. "
            "user: %s, course_id: %s, "
            "usage_id: %s. Exception: %s", unicode(student), course_id, usage_id, ex.message
        )
Exemplo n.º 19
0
 def setUp(self):
     super(SetupTestErrorModules, self).setUp()
     self.system = get_test_system()
     self.course_id = CourseLocator('org', 'course', 'run')
     self.location = self.course_id.make_usage_key('foo', 'bar')
     self.valid_xml = u"<problem>ABC \N{SNOWMAN}</problem>"
     self.error_msg = "Error"
Exemplo n.º 20
0
    def setUp(self):
        super(ApiTest, self).setUp()
        self.client = Client()

        # Mocks
        patcher = patch.object(api, 'api_enabled', Mock(return_value=True))
        patcher.start()
        self.addCleanup(patcher.stop)

        # 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 = CourseLocator('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
Exemplo n.º 21
0
 def setUp(self):
     super(TestSortedAssetList, self).setUp()
     asset_list = [dict(zip(AssetStoreTestData.asset_fields, asset)) for asset in AssetStoreTestData.all_asset_data]
     self.sorted_asset_list_by_filename = SortedAssetList(iterable=asset_list)
     self.sorted_asset_list_by_last_edit = SortedAssetList(iterable=asset_list, key=lambda x: x['edited_on'])
     self.course_key = CourseLocator('org', 'course', 'run')
Exemplo n.º 22
0
def generate_course_key(org, number, run):
    """
    Makes a CourseLocator from org, number and run
    """
    default_store = os.environ.get('DEFAULT_STORE', 'draft')
    return CourseLocator(org, number, run, deprecated=(default_store == 'draft'))
Exemplo n.º 23
0
class CertificatesBrandingTest(TestCase):
    """Test certificates branding. """

    COURSE_KEY = CourseLocator(org='test', course='test', run='test')
    configuration = {
        'logo_image_url': 'test_site/images/header-logo.png',
        'SITE_NAME': 'test_site.localhost',
        'urls': {
            'ABOUT': 'test-site/about',
            'PRIVACY': 'test-site/privacy',
            'TOS_AND_HONOR': 'test-site/tos-and-honor',
        },
    }

    @with_site_configuration(configuration=configuration)
    def test_certificate_header_data(self):
        """
        Test that get_certificate_header_context from lms.djangoapps.certificates api
        returns data customized according to site branding.
        """
        # Generate certificates for the course
        CourseModeFactory.create(course_id=self.COURSE_KEY, mode_slug=CourseMode.HONOR)
        data = certs_api.get_certificate_header_context(is_secure=True)

        # Make sure there are not unexpected keys in dict returned by 'get_certificate_header_context'
        self.assertItemsEqual(
            list(data.keys()),
            ['logo_src', 'logo_url']
        )
        self.assertIn(
            self.configuration['logo_image_url'],
            data['logo_src']
        )

        self.assertIn(
            self.configuration['SITE_NAME'],
            data['logo_url']
        )

    @with_site_configuration(configuration=configuration)
    def test_certificate_footer_data(self):
        """
        Test that get_certificate_footer_context from lms.djangoapps.certificates api returns
        data customized according to site branding.
        """
        # Generate certificates for the course
        CourseModeFactory.create(course_id=self.COURSE_KEY, mode_slug=CourseMode.HONOR)
        data = certs_api.get_certificate_footer_context()

        # Make sure there are not unexpected keys in dict returned by 'get_certificate_footer_context'
        self.assertItemsEqual(
            list(data.keys()),
            ['company_about_url', 'company_privacy_url', 'company_tos_url']
        )
        self.assertIn(
            self.configuration['urls']['ABOUT'],
            data['company_about_url']
        )
        self.assertIn(
            self.configuration['urls']['PRIVACY'],
            data['company_privacy_url']
        )
        self.assertIn(
            self.configuration['urls']['TOS_AND_HONOR'],
            data['company_tos_url']
        )
Exemplo n.º 24
0
 def setUp(self):
     super(ReportStoreTestMixin, self).setUp()
     self.course_id = CourseLocator(org="testx",
                                    course="coursex",
                                    run="runx")
Exemplo n.º 25
0
 def test_non_existent_usage_key(self):
     self.form_data['usage_key'] = self.store.make_course_usage_key(
         CourseLocator('non', 'existent', 'course'))
     self.assert_raises_permission_denied()
Exemplo n.º 26
0
 def setUp(self):
     super(CourseModeModelTest, self).setUp()
     self.course_key = CourseLocator('Test', 'TestCourse', 'TestCourseRun')
     CourseMode.objects.all().delete()
Exemplo n.º 27
0
    'oauth_signature': 'OAuth Signature',
    'oauth_signature_method': 'HMAC-SHA1',
    'oauth_timestamp': 'OAuth Timestamp',
    'oauth_nonce': 'OAuth Nonce',
    'user_id': 'LTI_User',
}

LTI_OPTIONAL_PARAMS = {
    'context_title': 'context title',
    'context_label': 'context label',
    'lis_result_sourcedid': 'result sourcedid',
    'lis_outcome_service_url': 'outcome service URL',
    'tool_consumer_instance_guid': 'consumer instance guid'
}

COURSE_KEY = CourseLocator(org='some_org', course='some_course', run='some_run')
USAGE_KEY = BlockUsageLocator(course_key=COURSE_KEY, block_type='problem', block_id='block_id')

COURSE_PARAMS = {
    'course_key': COURSE_KEY,
    'usage_key': USAGE_KEY
}


ALL_PARAMS = dict(list(LTI_DEFAULT_PARAMS.items()) + list(COURSE_PARAMS.items()))


def build_launch_request(extra_post_data=None, param_to_delete=None):
    """
    Helper method to create a new request object for the LTI launch.
    """
Exemplo n.º 28
0
 def test_non_existent_course(self):
     usage_key = self.store.make_course_usage_key(
         CourseLocator('non', 'existent', 'course'))
     url = reverse('blocks_in_block_tree',
                   kwargs={'usage_key_string': str(usage_key)})
     self.verify_response(403, url=url)
Exemplo n.º 29
0
class GenerateExampleCertificatesTest(TestCase):
    """Test generation of example certificates. """

    COURSE_KEY = CourseLocator(org='test', course='test', run='test')

    def setUp(self):
        super(GenerateExampleCertificatesTest, self).setUp()

    def test_generate_example_certs(self):
        # Generate certificates for the course
        with self._mock_xqueue() as mock_queue:
            certs_api.generate_example_certificates(self.COURSE_KEY)

        # Verify that the appropriate certs were added to the queue
        self._assert_certs_in_queue(mock_queue, 1)

        # Verify that the certificate status is "started"
        self._assert_cert_status({'description': 'honor', 'status': 'started'})

    def test_generate_example_certs_with_verified_mode(self):
        # Create verified and honor modes for the course
        CourseModeFactory(course_id=self.COURSE_KEY, mode_slug='honor')
        CourseModeFactory(course_id=self.COURSE_KEY, mode_slug='verified')

        # Generate certificates for the course
        with self._mock_xqueue() as mock_queue:
            certs_api.generate_example_certificates(self.COURSE_KEY)

        # Verify that the appropriate certs were added to the queue
        self._assert_certs_in_queue(mock_queue, 2)

        # Verify that the certificate status is "started"
        self._assert_cert_status(
            {
                'description': 'verified',
                'status': 'started'
            }, {
                'description': 'honor',
                'status': 'started'
            })

    @contextmanager
    def _mock_xqueue(self):
        """Mock the XQueue method for adding a task to the queue. """
        with patch.object(XQueueCertInterface,
                          'add_example_cert') as mock_queue:
            yield mock_queue

    def _assert_certs_in_queue(self, mock_queue, expected_num):
        """Check that the certificate generation task was added to the queue. """
        certs_in_queue = [
            call_args[0] for (call_args, __) in mock_queue.call_args_list
        ]
        self.assertEqual(len(certs_in_queue), expected_num)
        for cert in certs_in_queue:
            self.assertTrue(isinstance(cert, ExampleCertificate))

    def _assert_cert_status(self, *expected_statuses):
        """Check the example certificate status. """
        actual_status = certs_api.example_certificates_status(self.COURSE_KEY)
        self.assertEqual(list(expected_statuses), actual_status)
Exemplo n.º 30
0
 def id(self):
     return CourseLocator(self.org, 'toy', self.run)
Exemplo n.º 31
0
 def test_possibly_scored(self):
     course_key = CourseLocator(u'org', u'course', u'run')
     for block_type in self.possibly_scored_block_types:
         usage_key = BlockUsageLocator(course_key, block_type, 'mock_block_id')
         self.assertTrue(scores.possibly_scored(usage_key))
Exemplo n.º 32
0
    def create(system, source_is_error_module=False, source_visible_to_staff_only=False):
        """
        return a dict of modules: the conditional with a single source and a single child.
        Keys are 'cond_module', 'source_module', and 'child_module'.

        if the source_is_error_module flag is set, create a real ErrorBlock for the source.
        """
        descriptor_system = get_test_descriptor_system()

        # construct source descriptor and module:
        source_location = BlockUsageLocator(CourseLocator("edX", "conditional_test", "test_run", deprecated=True),
                                            "problem", "SampleProblem", deprecated=True)
        if source_is_error_module:
            # Make an error descriptor and module
            source_descriptor = NonStaffErrorBlock.from_xml(
                'some random xml data',
                system,
                id_generator=CourseLocationManager(source_location.course_key),
                error_msg='random error message'
            )
        else:
            source_descriptor = Mock(name='source_descriptor')
            source_descriptor.location = source_location

        source_descriptor.visible_to_staff_only = source_visible_to_staff_only
        source_descriptor.runtime = descriptor_system
        source_descriptor.render = lambda view, context=None: descriptor_system.render(source_descriptor, view, context)

        # construct other descriptors:
        child_descriptor = Mock(name='child_descriptor')
        child_descriptor.visible_to_staff_only = False
        child_descriptor._xmodule.student_view.return_value = Fragment(content=u'<p>This is a secret</p>')  # lint-amnesty, pylint: disable=protected-access
        child_descriptor.student_view = child_descriptor._xmodule.student_view  # lint-amnesty, pylint: disable=protected-access
        child_descriptor.displayable_items.return_value = [child_descriptor]
        child_descriptor.runtime = descriptor_system
        child_descriptor.xmodule_runtime = get_test_system()
        child_descriptor.render = lambda view, context=None: descriptor_system.render(child_descriptor, view, context)
        child_descriptor.location = source_location.replace(category='html', name='child')

        def visible_to_nonstaff_users(desc):
            """
            Returns if the object is visible to nonstaff users.
            """
            return not desc.visible_to_staff_only

        def load_item(usage_id, for_parent=None):  # pylint: disable=unused-argument
            """Test-only implementation of load_item that simply returns static xblocks."""
            return {
                child_descriptor.location: child_descriptor,
                source_location: source_descriptor
            }.get(usage_id)

        descriptor_system.load_item = load_item

        system.descriptor_runtime = descriptor_system

        # construct conditional module:
        cond_location = BlockUsageLocator(CourseLocator("edX", "conditional_test", "test_run", deprecated=True),
                                          "conditional", "SampleConditional", deprecated=True)
        field_data = DictFieldData({
            'data': '<conditional/>',
            'conditional_attr': 'attempted',
            'conditional_value': 'true',
            'xml_attributes': {'attempted': 'true'},
            'children': [child_descriptor.location],
        })

        cond_descriptor = ConditionalBlock(
            descriptor_system,
            field_data,
            ScopeIds(None, None, cond_location, cond_location)
        )
        cond_descriptor.xmodule_runtime = system
        system.get_module = lambda desc: desc if visible_to_nonstaff_users(desc) else None
        cond_descriptor.get_required_blocks = [
            system.get_module(source_descriptor),
        ]

        # return dict:
        return {'cond_module': cond_descriptor,
                'source_module': source_descriptor,
                'child_module': child_descriptor}
Exemplo n.º 33
0
class TestAssetXml(unittest.TestCase):
    """
    Tests for storing/querying course asset metadata.
    """
    shard = 1

    def setUp(self):
        super(TestAssetXml, self).setUp()

        xsd_filename = "assets.xsd"

        self.course_id = CourseLocator('org1', 'course1', 'run1')

        self.course_assets = []
        for asset in AssetStoreTestData.all_asset_data:
            asset_dict = dict(
                zip(AssetStoreTestData.asset_fields[1:], asset[1:]))
            asset_md = AssetMetadata(
                self.course_id.make_asset_key('asset', asset[0]), **asset_dict)
            self.course_assets.append(asset_md)

        # Read in the XML schema definition and make a validator.
        xsd_path = path(__file__).realpath().parent / xsd_filename
        with open(xsd_path, 'r') as f:
            schema_root = etree.XML(f.read())
        schema = etree.XMLSchema(schema_root)
        self.xmlparser = etree.XMLParser(schema=schema)

    def test_export_single_asset_to_from_xml(self):
        """
        Export a single AssetMetadata to XML and verify the structure and fields.
        """
        asset_md = self.course_assets[0]
        root = etree.Element("assets")
        asset = etree.SubElement(root, "asset")
        asset_md.to_xml(asset)
        # If this line does *not* raise, the XML is valid.
        etree.fromstring(etree.tostring(root), self.xmlparser)
        new_asset_key = self.course_id.make_asset_key('tmp', 'tmp')
        new_asset_md = AssetMetadata(new_asset_key)
        new_asset_md.from_xml(asset)
        # Compare asset_md to new_asset_md.
        for attr in AssetMetadata.XML_ATTRS:
            if attr in AssetMetadata.XML_ONLY_ATTRS:
                continue
            orig_value = getattr(asset_md, attr)
            new_value = getattr(new_asset_md, attr)
            self.assertEqual(orig_value, new_value)

    def test_export_with_None_value(self):
        """
        Export and import a single AssetMetadata to XML with a None created_by field, without causing an exception.
        """
        asset_md = AssetMetadata(
            self.course_id.make_asset_key('asset', 'none_value'),
            created_by=None,
        )
        asset = etree.Element("asset")
        asset_md.to_xml(asset)
        asset_md.from_xml(asset)

    def test_export_all_assets_to_xml(self):
        """
        Export all AssetMetadatas to XML and verify the structure and fields.
        """
        root = etree.Element("assets")
        AssetMetadata.add_all_assets_as_xml(root, self.course_assets)
        # If this line does *not* raise, the XML is valid.
        etree.fromstring(etree.tostring(root), self.xmlparser)

    def test_wrong_node_type_all(self):
        """
        Ensure full asset sections with the wrong tag are detected.
        """
        root = etree.Element("glassets")
        with self.assertRaises(ContractNotRespected):
            AssetMetadata.add_all_assets_as_xml(root, self.course_assets)

    def test_wrong_node_type_single(self):
        """
        Ensure single asset blocks with the wrong tag are detected.
        """
        asset_md = self.course_assets[0]
        root = etree.Element("assets")
        asset = etree.SubElement(root, "smashset")
        with self.assertRaises(ContractNotRespected):
            asset_md.to_xml(asset)
Exemplo n.º 34
0
 def get_key(self):
     return CourseLocator(
         self.courselike_key.org, self.courselike_key.course, self.courselike_key.run, deprecated=True
     )
Exemplo n.º 35
0
class XBlockCacheModelTest(ModuleStoreTestCase):
    """
    Test the XBlockCache model.
    """
    COURSE_KEY = CourseLocator(org='test', course='test', run='test')
    CHAPTER1_USAGE_KEY = BlockUsageLocator(COURSE_KEY,
                                           block_type='chapter',
                                           block_id='chapter1')
    SECTION1_USAGE_KEY = BlockUsageLocator(COURSE_KEY,
                                           block_type='section',
                                           block_id='section1')
    SECTION2_USAGE_KEY = BlockUsageLocator(COURSE_KEY,
                                           block_type='section',
                                           block_id='section1')
    VERTICAL1_USAGE_KEY = BlockUsageLocator(COURSE_KEY,
                                            block_type='vertical',
                                            block_id='sequential1')
    PATH1 = [
        [unicode(CHAPTER1_USAGE_KEY), 'Chapter 1'],
        [unicode(SECTION1_USAGE_KEY), 'Section 1'],
    ]
    PATH2 = [
        [unicode(CHAPTER1_USAGE_KEY), 'Chapter 1'],
        [unicode(SECTION2_USAGE_KEY), 'Section 2'],
    ]

    def assert_xblock_cache_data(self, xblock_cache, data):
        """
        Assert that the XBlockCache object values match.
        """
        self.assertEqual(xblock_cache.usage_key, data['usage_key'])
        self.assertEqual(xblock_cache.course_key, data['usage_key'].course_key)
        self.assertEqual(xblock_cache.display_name, data['display_name'])
        self.assertEqual(xblock_cache._paths, data['_paths'])  # pylint: disable=protected-access
        self.assertEqual(xblock_cache.paths,
                         [parse_path_data(path) for path in data['_paths']])

    @ddt.data(
        (
            [
                {
                    'usage_key': VERTICAL1_USAGE_KEY,
                },
                {
                    'display_name': '',
                    '_paths': [],
                },
            ],
            [
                {
                    'usage_key': VERTICAL1_USAGE_KEY,
                    'display_name': 'Vertical 5',
                    '_paths': [PATH2]
                },
                {
                    '_paths': []
                },
            ],
        ),
        (
            [
                {
                    'usage_key': VERTICAL1_USAGE_KEY,
                    'display_name': 'Vertical 4',
                    '_paths': [PATH1]
                },
                {},
            ],
            [
                {
                    'usage_key': VERTICAL1_USAGE_KEY,
                    'display_name': 'Vertical 5',
                    '_paths': [PATH2]
                },
                {
                    '_paths': [PATH1]
                },
            ],
        ),
    )
    def test_create(self, data):
        """
        Test XBlockCache.create() constructs and updates objects correctly.
        """
        for create_data, additional_data_to_expect in data:
            xblock_cache = XBlockCache.create(create_data)
            create_data.update(additional_data_to_expect)
            self.assert_xblock_cache_data(xblock_cache, create_data)

    @ddt.data(
        ([], [PATH1]),
        ([PATH1, PATH2], [PATH1]),
        ([PATH1], []),
    )
    @ddt.unpack
    def test_paths(self, original_paths, updated_paths):
        xblock_cache = XBlockCache.create({
            'usage_key': self.VERTICAL1_USAGE_KEY,
            'display_name': 'The end.',
            '_paths': original_paths,
        })
        self.assertEqual(xblock_cache.paths,
                         [parse_path_data(path) for path in original_paths])

        xblock_cache.paths = [parse_path_data(path) for path in updated_paths]
        xblock_cache.save()

        xblock_cache = XBlockCache.objects.get(id=xblock_cache.id)
        self.assertEqual(xblock_cache._paths, updated_paths)  # pylint: disable=protected-access
        self.assertEqual(xblock_cache.paths,
                         [parse_path_data(path) for path in updated_paths])
Exemplo n.º 36
0
    def test_contentstore_attrs(self):
        """
        Test getting, setting, and defaulting the locked attr and arbitrary attrs.
        """
        location = BlockUsageLocator(CourseLocator('edX',
                                                   'toy',
                                                   '2012_Fall',
                                                   deprecated=True),
                                     'course',
                                     '2012_Fall',
                                     deprecated=True)
        course_content, __ = self.content_store.get_all_content_for_course(
            location.course_key)
        assert_true(len(course_content) > 0)
        filter_params = _build_requested_filter('Images')
        filtered_course_content, __ = self.content_store.get_all_content_for_course(
            location.course_key, filter_params=filter_params)
        assert_true(len(filtered_course_content) < len(course_content))
        # a bit overkill, could just do for content[0]
        for content in course_content:
            assert not content.get('locked', False)
            asset_key = AssetLocator._from_deprecated_son(
                content.get('content_son', content['_id']), location.run)
            assert not self.content_store.get_attr(asset_key, 'locked', False)
            attrs = self.content_store.get_attrs(asset_key)
            assert_in('uploadDate', attrs)
            assert not attrs.get('locked', False)
            self.content_store.set_attr(asset_key, 'locked', True)
            assert self.content_store.get_attr(asset_key, 'locked', False)
            attrs = self.content_store.get_attrs(asset_key)
            assert_in('locked', attrs)
            assert attrs['locked'] is True
            self.content_store.set_attrs(asset_key, {'miscel': 99})
            assert_equals(self.content_store.get_attr(asset_key, 'miscel'), 99)

        asset_key = AssetLocator._from_deprecated_son(
            course_content[0].get('content_son', course_content[0]['_id']),
            location.run)
        assert_raises(AttributeError, self.content_store.set_attr, asset_key,
                      'md5', 'ff1532598830e3feac91c2449eaa60d6')
        assert_raises(AttributeError, self.content_store.set_attrs, asset_key,
                      {
                          'foo': 9,
                          'md5': 'ff1532598830e3feac91c2449eaa60d6'
                      })
        assert_raises(
            NotFoundError, self.content_store.get_attr,
            BlockUsageLocator(CourseLocator('bogus', 'bogus', 'bogus'),
                              'asset', 'bogus'), 'displayname')
        assert_raises(
            NotFoundError, self.content_store.set_attr,
            BlockUsageLocator(CourseLocator('bogus', 'bogus', 'bogus'),
                              'asset', 'bogus'), 'displayname', 'hello')
        assert_raises(
            NotFoundError, self.content_store.get_attrs,
            BlockUsageLocator(CourseLocator('bogus', 'bogus', 'bogus'),
                              'asset', 'bogus'))
        assert_raises(
            NotFoundError, self.content_store.set_attrs,
            BlockUsageLocator(CourseLocator('bogus', 'bogus', 'bogus'),
                              'asset', 'bogus'), {'displayname': 'hello'})
        assert_raises(
            NotFoundError, self.content_store.set_attrs,
            BlockUsageLocator(CourseLocator('bogus',
                                            'bogus',
                                            'bogus',
                                            deprecated=True),
                              'asset',
                              None,
                              deprecated=True), {'displayname': 'hello'})
Exemplo n.º 37
0
    def test_all_current_course_configs(self):
        # Set up test objects
        for global_setting in (True, False, None):
            CourseDurationLimitConfig.objects.create(enabled=global_setting,
                                                     enabled_as_of=datetime(
                                                         2018, 1, 1))
            for site_setting in (True, False, None):
                test_site_cfg = SiteConfigurationFactory.create(
                    site_values={'course_org_filter': []})
                CourseDurationLimitConfig.objects.create(
                    site=test_site_cfg.site,
                    enabled=site_setting,
                    enabled_as_of=datetime(2018, 1, 1))

                for org_setting in (True, False, None):
                    test_org = "{}-{}".format(test_site_cfg.id, org_setting)
                    test_site_cfg.site_values['course_org_filter'].append(
                        test_org)
                    test_site_cfg.save()

                    CourseDurationLimitConfig.objects.create(
                        org=test_org,
                        enabled=org_setting,
                        enabled_as_of=datetime(2018, 1, 1))

                    for course_setting in (True, False, None):
                        test_course = CourseOverviewFactory.create(
                            org=test_org,
                            id=CourseLocator(test_org, 'test_course',
                                             'run-{}'.format(course_setting)))
                        CourseDurationLimitConfig.objects.create(
                            course=test_course,
                            enabled=course_setting,
                            enabled_as_of=datetime(2018, 1, 1))

            with self.assertNumQueries(4):
                all_configs = CourseDurationLimitConfig.all_current_course_configs(
                )

        # Deliberatly using the last all_configs that was checked after the 3rd pass through the global_settings loop
        # We should be creating 3^4 courses (3 global values * 3 site values * 3 org values * 3 course values)
        # Plus 1 for the edX/toy/2012_Fall course
        self.assertEqual(len(all_configs), 3**4 + 1)

        # Point-test some of the final configurations
        self.assertEqual(
            all_configs[CourseLocator('7-True', 'test_course', 'run-None')], {
                'enabled': (True, Provenance.org),
                'enabled_as_of':
                (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC), Provenance.run),
            })
        self.assertEqual(
            all_configs[CourseLocator('7-True', 'test_course', 'run-False')], {
                'enabled': (False, Provenance.run),
                'enabled_as_of':
                (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC), Provenance.run),
            })
        self.assertEqual(
            all_configs[CourseLocator('7-None', 'test_course', 'run-None')], {
                'enabled': (True, Provenance.site),
                'enabled_as_of':
                (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC), Provenance.run),
            })
Exemplo n.º 38
0
 def setUp(self):
     super(PersistentGradesFeatureFlagTests, self).setUp()
     self.course_id_1 = CourseLocator(org="edx", course="course", run="run")
     self.course_id_2 = CourseLocator(org="edx",
                                      course="course2",
                                      run="run")
Exemplo n.º 39
0
    def _create_test_tree(self, name, user_id=None):
        """
        Creates and returns a tree with the following structure:
        Grandparent
            Parent Sibling
            Parent
                Child
                Child Sibling

        """
        if user_id is None:
            user_id = self.dummy_user

        org = 'edX'
        course = 'tree{}'.format(name)
        run = name

        if not self.draft_store.has_course(
                CourseKey.from_string('/'.join[org, course, run])):
            self.draft_store.create_course(org, course, run, user_id)

            locations = {
                'grandparent':
                BlockUsageLocator(CourseLocator(org,
                                                course,
                                                run,
                                                deprecated=True),
                                  'chapter',
                                  'grandparent',
                                  deprecated=True),
                'parent_sibling':
                BlockUsageLocator(CourseLocator(org,
                                                course,
                                                run,
                                                deprecated=True),
                                  'sequential',
                                  'parent_sibling',
                                  deprecated=True),
                'parent':
                BlockUsageLocator(CourseLocator(org,
                                                course,
                                                run,
                                                deprecated=True),
                                  'sequential',
                                  'parent',
                                  deprecated=True),
                'child_sibling':
                BlockUsageLocator(CourseLocator(org,
                                                course,
                                                run,
                                                deprecated=True),
                                  'vertical',
                                  'child_sibling',
                                  deprecated=True),
                'child':
                BlockUsageLocator(CourseLocator(org,
                                                course,
                                                run,
                                                deprecated=True),
                                  'vertical',
                                  'child',
                                  deprecated=True),
            }

            for key in locations:
                self.draft_store.create_item(user_id,
                                             locations[key].course_key,
                                             locations[key].block_type,
                                             block_id=locations[key].block_id)

            grandparent = self.draft_store.get_item(locations['grandparent'])
            grandparent.children += [
                locations['parent_sibling'], locations['parent']
            ]
            self.draft_store.update_item(grandparent, user_id=user_id)

            parent = self.draft_store.get_item(locations['parent'])
            parent.children += [locations['child_sibling'], locations['child']]
            self.draft_store.update_item(parent, user_id=user_id)

            self.draft_store.publish(locations['parent'], user_id)
            self.draft_store.publish(locations['parent_sibling'], user_id)

        return locations
Exemplo n.º 40
0
class CertificateGenerationEnabledTest(EventTestMixin, TestCase):
    """Test enabling/disabling self-generated certificates for a course. """

    COURSE_KEY = CourseLocator(org='test', course='test', run='test')

    def setUp(self):  # pylint: disable=arguments-differ
        super(CertificateGenerationEnabledTest, self).setUp('lms.djangoapps.certificates.api.tracker')

        # Since model-based configuration is cached, we need
        # to clear the cache before each test.
        cache.clear()

    @ddt.data(
        (None, None, False),
        (False, None, False),
        (False, True, False),
        (True, None, False),
        (True, False, False),
        (True, True, True)
    )
    @ddt.unpack
    def test_cert_generation_enabled(self, is_feature_enabled, is_course_enabled, expect_enabled):
        if is_feature_enabled is not None:
            CertificateGenerationConfiguration.objects.create(enabled=is_feature_enabled)

        if is_course_enabled is not None:
            certs_api.set_cert_generation_enabled(self.COURSE_KEY, is_course_enabled)
            cert_event_type = 'enabled' if is_course_enabled else 'disabled'
            event_name = '.'.join(['edx', 'certificate', 'generation', cert_event_type])
            self.assert_event_emitted(
                event_name,
                course_id=six.text_type(self.COURSE_KEY),
            )

        self._assert_enabled_for_course(self.COURSE_KEY, expect_enabled)

    def test_latest_setting_used(self):
        # Enable the feature
        CertificateGenerationConfiguration.objects.create(enabled=True)

        # Enable for the course
        certs_api.set_cert_generation_enabled(self.COURSE_KEY, True)
        self._assert_enabled_for_course(self.COURSE_KEY, True)

        # Disable for the course
        certs_api.set_cert_generation_enabled(self.COURSE_KEY, False)
        self._assert_enabled_for_course(self.COURSE_KEY, False)

    def test_setting_is_course_specific(self):
        # Enable the feature
        CertificateGenerationConfiguration.objects.create(enabled=True)

        # Enable for one course
        certs_api.set_cert_generation_enabled(self.COURSE_KEY, True)
        self._assert_enabled_for_course(self.COURSE_KEY, True)

        # Should be disabled for another course
        other_course = CourseLocator(org='other', course='other', run='other')
        self._assert_enabled_for_course(other_course, False)

    def _assert_enabled_for_course(self, course_key, expect_enabled):
        """Check that self-generated certificates are enabled or disabled for the course. """
        actual_enabled = certs_api.cert_generation_enabled(course_key)
        self.assertEqual(expect_enabled, actual_enabled)
 def setUp(self):
     super(TestLTIConsumerHideFieldsFlag, self).setUp()
     self.course_id = CourseLocator(org="edx", course="course", run="run")
Exemplo n.º 42
0
 def get_course_locator(self, course_key):
     from opaque_keys.edx.locator import CourseLocator
     return CourseLocator.from_string(course_key)
Exemplo n.º 43
0
 def test_make_course_usage_key(self):
     """Test that we get back the appropriate usage key for the root of a course key."""
     course_key = CourseLocator(org="edX", course="101", run="2015")
     root_block_key = self.draft_store.make_course_usage_key(course_key)
     self.assertEqual(root_block_key.block_type, "course")
     self.assertEqual(root_block_key.block_id, "2015")
Exemplo n.º 44
0
 def setUp(self):
     super(TestCohortSignals, self).setUp()
     self.course_key = CourseLocator("dummy", "dummy", "dummy")
Exemplo n.º 45
0
class ExampleCertificateTest(TestCase):
    """Tests for the ExampleCertificate model. """

    COURSE_KEY = CourseLocator(org='test', course='test', run='test')

    DESCRIPTION = 'test'
    TEMPLATE = 'test.pdf'
    DOWNLOAD_URL = 'http://www.example.com'
    ERROR_REASON = 'Kaboom!'

    def setUp(self):
        super(ExampleCertificateTest, self).setUp()
        self.cert_set = ExampleCertificateSet.objects.create(course_key=self.COURSE_KEY)
        self.cert = ExampleCertificate.objects.create(
            example_cert_set=self.cert_set,
            description=self.DESCRIPTION,
            template=self.TEMPLATE
        )

    def test_update_status_success(self):
        self.cert.update_status(
            ExampleCertificate.STATUS_SUCCESS,
            download_url=self.DOWNLOAD_URL
        )
        self.assertEqual(
            self.cert.status_dict,
            {
                'description': self.DESCRIPTION,
                'status': ExampleCertificate.STATUS_SUCCESS,
                'download_url': self.DOWNLOAD_URL
            }
        )

    def test_update_status_error(self):
        self.cert.update_status(
            ExampleCertificate.STATUS_ERROR,
            error_reason=self.ERROR_REASON
        )
        self.assertEqual(
            self.cert.status_dict,
            {
                'description': self.DESCRIPTION,
                'status': ExampleCertificate.STATUS_ERROR,
                'error_reason': self.ERROR_REASON
            }
        )

    def test_update_status_invalid(self):
        with self.assertRaisesRegexp(ValueError, 'status'):
            self.cert.update_status('invalid')

    def test_latest_status_unavailable(self):
        # Delete any existing statuses
        ExampleCertificateSet.objects.all().delete()

        # Verify that the "latest" status is None
        result = ExampleCertificateSet.latest_status(self.COURSE_KEY)
        self.assertIs(result, None)

    def test_latest_status_is_course_specific(self):
        other_course = CourseLocator(org='other', course='other', run='other')
        result = ExampleCertificateSet.latest_status(other_course)
        self.assertIs(result, None)
 def location(self):
     return BlockUsageLocator(CourseLocator('org', 'course', 'run'),
                              'category', self.url_name)
Exemplo n.º 47
0
class UpdateExampleCertificateViewTest(CacheIsolationTestCase):
    """Tests for the XQueue callback that updates example certificates. """

    COURSE_KEY = CourseLocator(org='test', course='test', run='test')

    DESCRIPTION = 'test'
    TEMPLATE = 'test.pdf'
    DOWNLOAD_URL = 'http://www.example.com'
    ERROR_REASON = 'Kaboom!'

    ENABLED_CACHES = ['default']

    def setUp(self):
        super(UpdateExampleCertificateViewTest, self).setUp()
        self.cert_set = ExampleCertificateSet.objects.create(
            course_key=self.COURSE_KEY)
        self.cert = ExampleCertificate.objects.create(
            example_cert_set=self.cert_set,
            description=self.DESCRIPTION,
            template=self.TEMPLATE,
        )
        self.url = reverse('update_example_certificate')

        # Since rate limit counts are cached, we need to clear
        # this before each test.
        cache.clear()

    def test_update_example_certificate_success(self):
        response = self._post_to_view(self.cert,
                                      download_url=self.DOWNLOAD_URL)
        self._assert_response(response)

        self.cert = ExampleCertificate.objects.get()
        self.assertEqual(self.cert.status, ExampleCertificate.STATUS_SUCCESS)
        self.assertEqual(self.cert.download_url, self.DOWNLOAD_URL)

    def test_update_example_certificate_invalid_key(self):
        payload = {
            'xqueue_header':
            json.dumps({'lms_key': 'invalid'}),
            'xqueue_body':
            json.dumps({
                'username': self.cert.uuid,
                'url': self.DOWNLOAD_URL
            })
        }
        response = self.client.post(self.url, data=payload)
        self.assertEqual(response.status_code, 404)

    def test_update_example_certificate_error(self):
        response = self._post_to_view(self.cert,
                                      error_reason=self.ERROR_REASON)
        self._assert_response(response)

        self.cert = ExampleCertificate.objects.get()
        self.assertEqual(self.cert.status, ExampleCertificate.STATUS_ERROR)
        self.assertEqual(self.cert.error_reason, self.ERROR_REASON)

    @ddt.data('xqueue_header', 'xqueue_body')
    def test_update_example_certificate_invalid_params(self, missing_param):
        payload = {
            'xqueue_header':
            json.dumps({'lms_key': self.cert.access_key}),
            'xqueue_body':
            json.dumps({
                'username': self.cert.uuid,
                'url': self.DOWNLOAD_URL
            })
        }
        del payload[missing_param]

        response = self.client.post(self.url, data=payload)
        self.assertEqual(response.status_code, 400)

    def test_update_example_certificate_missing_download_url(self):
        payload = {
            'xqueue_header': json.dumps({'lms_key': self.cert.access_key}),
            'xqueue_body': json.dumps({'username': self.cert.uuid})
        }
        response = self.client.post(self.url, data=payload)
        self.assertEqual(response.status_code, 400)

    def test_update_example_cetificate_non_json_param(self):
        payload = {'xqueue_header': '{/invalid', 'xqueue_body': '{/invalid'}
        response = self.client.post(self.url, data=payload)
        self.assertEqual(response.status_code, 400)

    def test_unsupported_http_method(self):
        response = self.client.get(self.url)
        self.assertEqual(response.status_code, 405)

    def test_bad_request_rate_limiting(self):
        payload = {
            'xqueue_header':
            json.dumps({'lms_key': 'invalid'}),
            'xqueue_body':
            json.dumps({
                'username': self.cert.uuid,
                'url': self.DOWNLOAD_URL
            })
        }

        # Exceed the rate limit for invalid requests
        # (simulate a DDOS with invalid keys)
        for _ in range(100):
            response = self.client.post(self.url, data=payload)
            if response.status_code == 403:
                break

        # The final status code should indicate that the rate
        # limit was exceeded.
        self.assertEqual(response.status_code, 403)

    def _post_to_view(self, cert, download_url=None, error_reason=None):
        """Simulate a callback from the XQueue to the example certificate end-point. """
        header = {'lms_key': cert.access_key}
        body = {'username': cert.uuid}

        if download_url is not None:
            body['url'] = download_url

        if error_reason is not None:
            body['error'] = 'error'
            body['error_reason'] = self.ERROR_REASON

        payload = {
            'xqueue_header': json.dumps(header),
            'xqueue_body': json.dumps(body)
        }
        return self.client.post(self.url, data=payload)

    def _assert_response(self, response):
        """Check the response from the callback end-point. """
        content = json.loads(response.content)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(content['return_code'], 0)
Exemplo n.º 48
0
    def test_conditional_module(self, _):
        """Make sure that conditional module works"""

        print("Starting import")
        course = self.get_course('conditional_and_poll')

        print("Course: ", course)
        print("id: ", course.id)

        def inner_get_module(descriptor):
            if isinstance(descriptor, BlockUsageLocator):
                location = descriptor
                descriptor = self.modulestore.get_item(location, depth=None)
            descriptor.xmodule_runtime = get_test_system()
            descriptor.xmodule_runtime.descriptor_runtime = descriptor._runtime  # pylint: disable=protected-access
            descriptor.xmodule_runtime.get_module = inner_get_module
            return descriptor

        # edx - HarvardX
        # cond_test - ER22x
        location = BlockUsageLocator(CourseLocator("HarvardX", "ER22x", "2013_Spring", deprecated=True),
                                     "conditional", "condone", deprecated=True)

        def replace_urls(text, staticfiles_prefix=None, replace_prefix='/static/', course_namespace=None):  # lint-amnesty, pylint: disable=unused-argument
            return text
        self.test_system.replace_urls = replace_urls
        self.test_system.get_module = inner_get_module

        module = inner_get_module(location)
        print("module: ", module)
        print("module children: ", module.get_children())
        print("module display items (children): ", module.get_display_items())

        html = module.render(STUDENT_VIEW).content
        print("html type: ", type(html))
        print("html: ", html)
        html_expect = module.xmodule_runtime.render_template(
            'conditional_ajax.html',
            {
                # Test ajax url is just usage-id / handler_name
                'ajax_url': '{}/xmodule_handler'.format(text_type(location)),
                'element_id': u'i4x-HarvardX-ER22x-conditional-condone',
                'depends': u'i4x-HarvardX-ER22x-problem-choiceprob'
            }
        )
        assert html == html_expect

        gdi = module.get_display_items()
        print("gdi=", gdi)

        ajax = json.loads(module.handle_ajax('', ''))
        module.save()
        print("ajax: ", ajax)
        fragments = ajax['fragments']
        assert not any(('This is a secret' in item['content']) for item in fragments)

        # Now change state of the capa problem to make it completed
        inner_module = inner_get_module(location.replace(category="problem", name='choiceprob'))
        inner_module.attempts = 1
        # Save our modifications to the underlying KeyValueStore so they can be persisted
        inner_module.save()

        ajax = json.loads(module.handle_ajax('', ''))
        module.save()
        print("post-attempt ajax: ", ajax)
        fragments = ajax['fragments']
        assert any(('This is a secret' in item['content']) for item in fragments)
Exemplo n.º 49
0
def _recalculate_subsection_grade(self, **kwargs):
    """
    Updates a saved subsection grade.

    Keyword Arguments:
        user_id (int): id of applicable User object
        anonymous_user_id (int, OPTIONAL): Anonymous ID of the User
        course_id (string): identifying the course
        usage_id (string): identifying the course block
        only_if_higher (boolean): indicating whether grades should
            be updated only if the new raw_earned is higher than the
            previous value.
        expected_modified_time (serialized timestamp): indicates when the task
            was queued so that we can verify the underlying data update.
        score_deleted (boolean): indicating whether the grade change is
            a result of the problem's score being deleted.
        event_transaction_id (string): uuid identifying the current
            event transaction.
        event_transaction_type (string): human-readable type of the
            event at the root of the current event transaction.
        score_db_table (ScoreDatabaseTableEnum): database table that houses
            the changed score. Used in conjunction with expected_modified_time.
    """
    try:
        course_key = CourseLocator.from_string(kwargs['course_id'])
        scored_block_usage_key = UsageKey.from_string(
            kwargs['usage_id']).replace(course_key=course_key)

        set_custom_metrics_for_course_key(course_key)
        set_custom_metric('usage_id', unicode(scored_block_usage_key))

        # The request cache is not maintained on celery workers,
        # where this code runs. So we take the values from the
        # main request cache and store them in the local request
        # cache. This correlates model-level grading events with
        # higher-level ones.
        set_event_transaction_id(kwargs.get('event_transaction_id'))
        set_event_transaction_type(kwargs.get('event_transaction_type'))

        # Verify the database has been updated with the scores when the task was
        # created. This race condition occurs if the transaction in the task
        # creator's process hasn't committed before the task initiates in the worker
        # process.
        has_database_updated = _has_db_updated_with_new_score(
            self, scored_block_usage_key, **kwargs)

        if not has_database_updated:
            raise DatabaseNotReadyError

        _update_subsection_grades(
            course_key,
            scored_block_usage_key,
            kwargs['only_if_higher'],
            kwargs['user_id'],
            kwargs['score_deleted'],
        )
    except Exception as exc:  # pylint: disable=broad-except
        if not isinstance(exc, KNOWN_RETRY_ERRORS):
            log.info(
                "tnl-6244 grades unexpected failure: {}. task id: {}. kwargs={}"
                .format(
                    repr(exc),
                    self.request.id,
                    kwargs,
                ))
        raise self.retry(kwargs=kwargs, exc=exc)
Exemplo n.º 50
0
class XQueueCertInterfaceExampleCertificateTest(TestCase):
    """Tests for the XQueue interface for certificate generation. """

    COURSE_KEY = CourseLocator(org='test', course='test', run='test')

    TEMPLATE = 'test.pdf'
    DESCRIPTION = 'test'
    ERROR_MSG = 'Kaboom!'

    def setUp(self):
        super().setUp()
        self.xqueue = XQueueCertInterface()

    def test_add_example_cert(self):
        cert = self._create_example_cert()
        with self._mock_xqueue() as mock_send:
            self.xqueue.add_example_cert(cert)

        # Verify that the correct payload was sent to the XQueue
        self._assert_queue_task(mock_send, cert)

        # Verify the certificate status
        assert cert.status == ExampleCertificate.STATUS_STARTED

    def test_add_example_cert_error(self):
        cert = self._create_example_cert()
        with self._mock_xqueue(success=False):
            self.xqueue.add_example_cert(cert)

        # Verify the error status of the certificate
        assert cert.status == ExampleCertificate.STATUS_ERROR
        assert self.ERROR_MSG in cert.error_reason

    def _create_example_cert(self):
        """Create an example certificate. """
        cert_set = ExampleCertificateSet.objects.create(
            course_key=self.COURSE_KEY)
        return ExampleCertificate.objects.create(example_cert_set=cert_set,
                                                 description=self.DESCRIPTION,
                                                 template=self.TEMPLATE)

    @contextmanager
    def _mock_xqueue(self, success=True):
        """Mock the XQueue method for sending a task to the queue. """
        with patch.object(XQueueInterface, 'send_to_queue') as mock_send:
            mock_send.return_value = (0, None) if success else (1,
                                                                self.ERROR_MSG)
            yield mock_send

    def _assert_queue_task(self, mock_send, cert):
        """Check that the task was added to the queue. """
        expected_header = {
            'lms_key': cert.access_key,
            'lms_callback_url':
            f'https://edx.org/update_example_certificate?key={cert.uuid}',
            'queue_name': 'certificates'
        }

        expected_body = {
            'action': 'create',
            'username': cert.uuid,
            'name': 'John Doë',
            'course_id': str(self.COURSE_KEY),
            'template_pdf': 'test.pdf',
            'example_certificate': True
        }

        assert mock_send.called

        __, kwargs = mock_send.call_args_list[0]
        actual_header = json.loads(kwargs['header'])
        actual_body = json.loads(kwargs['body'])

        assert expected_header == actual_header
        assert expected_body == actual_body