def test_static_tabs_import(self): """Make sure that the static tabs are imported correctly""" modulestore = XMLModuleStore(DATA_DIR, source_dirs=['toy']) location_tab_syllabus = BlockUsageLocator(CourseLocator("edX", "toy", "2012_Fall", deprecated=True), "static_tab", "syllabus", deprecated=True) toy_tab_syllabus = modulestore.get_item(location_tab_syllabus) assert toy_tab_syllabus.display_name == 'Syllabus' assert toy_tab_syllabus.course_staff_only is False location_tab_resources = BlockUsageLocator(CourseLocator("edX", "toy", "2012_Fall", deprecated=True), "static_tab", "resources", deprecated=True) toy_tab_resources = modulestore.get_item(location_tab_resources) assert toy_tab_resources.display_name == 'Resources' assert toy_tab_resources.course_staff_only is True
def get_transcript(course_id, block_id, lang=None, output_format=Transcript.SRT, is_bumper=False): """ Get video transcript from edx-val or content store. Arguments: course_id (CourseLocator): course identifier block_id (unicode): a unique identifier for an item in modulestore lang (unicode): transcript language output_format (unicode): transcript output format is_bumper (bool): indicates bumper video Returns: tuple containing content, filename, mimetype """ usage_key = BlockUsageLocator(course_id, block_type='video', block_id=block_id) video_descriptor = modulestore().get_item(usage_key) try: return get_transcript_from_val(video_descriptor.edx_video_id, lang, output_format) except NotFoundError: return get_transcript_from_contentstore(video_descriptor, lang, output_format=output_format, is_bumper=is_bumper)
def track_user_event(user, event_name, data, page): """ Marks a user's progress in the user's CompletionProfile whenever an appropriate event is logged, and sends the report to the third-party API. Args: user (User): the user object. event_name (str): the name of the logged event. Event names are specific to edX events and are mapped to the events tracked for the completion report. data (str or dict): in the event of a watched video, edX logs data in a dict where the `id` key contains the video block ID. For problems this argument contains the usage key. page (str): URL where the event was triggered. Used to extract the course ID. """ if event_name in EVENT_BLOCK_MAP: block_type = EVENT_BLOCK_MAP[event_name] course_id = extract_course_id_from_url(page) course_key = CourseKey.from_string(course_id) if block_type == 'problem': data_id = extract_problem_id(data) elif block_type == 'video': data_id = data['id'] usage_key = BlockUsageLocator(course_key, block_type, data_id) CompletionProfile.mark_progress(user, course_key, usage_key.block_id)
def setUp(self): super().setUp() system = get_test_descriptor_system(render_template=Mock()) self.tabs = [{ 'name': "Test_css", 'template': "tabs/codemirror-edit.html", 'current': True, 'css': { 'scss': [ resource_string( __name__, 'test_files/test_tabseditingdescriptor.scss') ], 'css': [ resource_string( __name__, 'test_files/test_tabseditingdescriptor.css') ] } }, { 'name': "Subtitles", 'template': "video/subtitles.html", }, { 'name': "Settings", 'template': "tabs/video-metadata-edit-tab.html" }] TabsEditingDescriptor.tabs = self.tabs self.descriptor = system.construct_xblock_from_class( TabsEditingDescriptor, scope_ids=ScopeIds( None, None, None, BlockUsageLocator( CourseLocator('org', 'course', 'run', branch='revision'), 'category', 'name')), field_data=DictFieldData({}), )
def _calculate_score_for_modules(user_id, course, modules): """ Calculates the cumulative score (percent) of the given modules """ # removing branch and version from exam modules locator # otherwise student module would not return scores since module usage keys would not match modules = [m for m in modules] locations = [ BlockUsageLocator( course_key=course.id, block_type=module.location.block_type, block_id=module.location.block_id ) if isinstance(module.location, BlockUsageLocator) and module.location.version else module.location for module in modules ] scores_client = ScoresClient(course.id, user_id) scores_client.fetch_scores(locations) # Iterate over all of the exam modules to get score percentage of user for each of them module_percentages = [] ignore_categories = ['course', 'chapter', 'sequential', 'vertical', 'randomize', 'library_content'] for index, module in enumerate(modules): if module.category not in ignore_categories and (module.graded or module.has_score): module_score = scores_client.get(locations[index]) if module_score: correct = module_score.correct or 0 total = module_score.total or 1 module_percentages.append(correct / total) return sum(module_percentages) / float(len(module_percentages)) if module_percentages else 0
def test_conditional_module_with_empty_sources_list(self): """ If a ConditionalDescriptor is initialized with an empty sources_list, we assert that the sources_list is set via generating UsageKeys from the values in xml_attributes['sources'] """ dummy_system = Mock() dummy_location = BlockUsageLocator( CourseLocator("edX", "conditional_test", "test_run"), "conditional", "SampleConditional") dummy_scope_ids = ScopeIds(None, None, dummy_location, dummy_location) dummy_field_data = DictFieldData({ 'data': '<conditional/>', 'xml_attributes': { 'sources': 'i4x://HarvardX/ER22x/poll_question/T15_poll' }, 'children': None, }) conditional = ConditionalDescriptor( dummy_system, dummy_field_data, dummy_scope_ids, ) new_run = conditional.location.course_key.run self.assertEqual( conditional.sources_list[0], # Matching what is in ConditionalDescriptor.__init__. BlockUsageLocator.from_string(conditional.xml_attributes['sources'] ).replace(run=new_run))
def create_location(self, block_type, block_id): """ Create a new BlockUsageLocation with the given type and ID. """ return BlockUsageLocator(course_key=self.course_key, block_type=block_type, block_id=block_id)
def setUp(self): super(SignAndSendReplaceResultTest, self).setUp() self.course_key = CourseLocator( org='some_org', course='some_course', run='some_run' ) self.usage_key = BlockUsageLocator( course_key=self.course_key, block_type='problem', block_id='block_id' ) self.user = UserFactory.create() consumer = LtiConsumer( consumer_name='consumer', consumer_key='consumer_key', consumer_secret='secret' ) consumer.save() outcome = OutcomeService( lis_outcome_service_url='http://example.com/service_url', lti_consumer=consumer, ) outcome.save() self.assignment = GradedAssignment( user=self.user, course_key=self.course_key, usage_key=self.usage_key, outcome_service=outcome, lis_result_sourcedid='sourcedid', ) self.assignment.save()
def test_xml_import_export_cycle(self): """ Test the import export cycle. """ runtime = get_test_descriptor_system() runtime.export_fs = MemoryFS() original_xml = ( '<word_cloud display_name="Favorite Fruits" display_student_percents="false" ' 'instructions="What are your favorite fruits?" num_inputs="3" num_top_words="100"/>\n' ) olx_element = etree.fromstring(original_xml) id_generator = Mock() block = WordCloudBlock.parse_xml(olx_element, runtime, None, id_generator) block.location = BlockUsageLocator( CourseLocator('org', 'course', 'run', branch='revision'), 'word_cloud', 'block_id') assert block.display_name == 'Favorite Fruits' assert not block.display_student_percents assert block.instructions == 'What are your favorite fruits?' assert block.num_inputs == 3 assert block.num_top_words == 100 node = etree.Element("unknown_root") # This will export the olx to a separate file. block.add_xml_to_node(node) with runtime.export_fs.open('word_cloud/block_id.xml') as f: exported_xml = f.read() assert exported_xml == original_xml
def get_usage_id(self, block_type, block_id): """ Constructs usage id using 'block_type' and 'block_id' """ return BlockUsageLocator(self.dummy_course_key, block_type=block_type, block_id=block_id)
def test_replacement(self, key): course_key = CourseLocator('org', 'course', 'run', 'rev', deprecated=True) kwargs = {key: 'newvalue'} self.assertEqual( getattr( BlockUsageLocator(course_key, 'c', 'n', deprecated=True).replace(**kwargs), key), 'newvalue') with self.assertRaises(InvalidKeyError): BlockUsageLocator(course_key, 'c', 'n', deprecated=True).replace(block_id=u'name\xae')
def setUp(self): super(PersistentSubsectionGradeTest, self).setUp() self.usage_key = BlockUsageLocator( course_key=self.course_key, block_type='subsection', block_id='subsection_12345', ) self.block_records = BlockRecordList([self.record_a, self.record_b], self.course_key) self.params = { "user_id": 12345, "usage_key": self.usage_key, "course_version": "deadbeef", "subtree_edited_timestamp": "2016-08-01 18:53:24.354741Z", "earned_all": 6.0, "possible_all": 12.0, "earned_graded": 6.0, "possible_graded": 8.0, "visible_blocks": self.block_records, "first_attempted": datetime(2000, 1, 1, 12, 30, 45, tzinfo=pytz.UTC), }
def test_remap_namespace_native_xblock(self): # Set the XBlock's location self.xblock.location = BlockUsageLocator( CourseLocator("org", "import", "run"), "category", "stubxblock") # Explicitly set the content and settings fields self.xblock.test_content_field = "Explicitly set" self.xblock.test_settings_field = "Explicitly set" self.xblock.save() # Move to different runtime w/ different course id target_location_namespace = CourseKey.from_string("org/course/run") new_version = _update_and_import_module( self.xblock, modulestore(), 999, self.xblock.location.course_key, target_location_namespace, do_import_static=False) # Check the XBlock's location assert new_version.location.course_key == target_location_namespace # Check the values of the fields. # The content and settings fields should be preserved assert new_version.test_content_field == 'Explicitly set' assert new_version.test_settings_field == 'Explicitly set' # Expect that these fields are marked explicitly set assert 'test_content_field' in new_version.get_explicitly_set_fields_by_scope( scope=Scope.content) assert 'test_settings_field' in new_version.get_explicitly_set_fields_by_scope( scope=Scope.settings)
def setUp(self): super(BaseOutcomeTest, self).setUp() self.course_key = CourseLocator(org='some_org', course='some_course', run='some_run') self.usage_key = BlockUsageLocator(course_key=self.course_key, block_type='problem', block_id='block_id') self.user = UserFactory.create() self.consumer = LtiConsumer(consumer_name='Lti Consumer Name', consumer_key='consumer_key', consumer_secret='consumer_secret', instance_guid='tool_instance_guid') self.consumer.save() outcome = OutcomeService( lis_outcome_service_url='http://example.com/service_url', lti_consumer=self.consumer) outcome.save() self.assignment = GradedAssignment( user=self.user, course_key=self.course_key, usage_key=self.usage_key, outcome_service=outcome, lis_result_sourcedid='sourcedid', version_number=1, ) self.assignment.save() self.send_score_update_mock = self.setup_patch( 'lti_provider.outcomes.send_score_update', None)
def block_key_factory(self, block_id): """ Returns a block key object for the given block_id. """ return BlockUsageLocator(course_key=self.course_key, block_type='course', block_id=unicode(block_id))
def setUp(self): super(TestResetGrades, self).setUp() self.command = reset_grades.Command() self.user_ids = [user_id for user_id in range(self.num_users)] self.course_keys = [] for course_index in range(self.num_courses): self.course_keys.append( CourseLocator( org='some_org', course='some_course', run=unicode(course_index), )) self.subsection_keys_by_course = {} for course_key in self.course_keys: subsection_keys_in_course = [] for subsection_index in range(self.num_subsections): subsection_keys_in_course.append( BlockUsageLocator( course_key=course_key, block_type='sequential', block_id=unicode(subsection_index), )) self.subsection_keys_by_course[ course_key] = subsection_keys_in_course
def test_migrate_published_info(self): """ Tests that blocks that were storing published_date and published_by through CMSBlockMixin are loaded correctly """ # Insert the test block directly into the module store location = BlockUsageLocator(CourseLocator('edX', 'migration', '2012_Fall', deprecated=True), 'html', 'test_html', deprecated=True) published_date = datetime(1970, 1, 1, tzinfo=UTC) published_by = 123 self.draft_store._update_single_item( as_draft(location), { 'definition.data': {}, 'metadata': { # published_date was previously stored as a list of time components, not a datetime 'published_date': list(published_date.timetuple()), 'published_by': published_by, }, }, allow_not_found=True, ) # Retrieve the block and verify its fields component = self.draft_store.get_item(location) assert component.published_on == published_date assert component.published_by == published_by
def test_usage_key_replace(self, key_class, attr_value): attr, value = attr_value key = key_class( BlockUsageLocator(CourseLocator('org', 'course', 'run'), 'block_type', 'block_id'), 'aside') new_key = key.replace(**{attr: value}) self.assertEqual(getattr(new_key, attr), value)
def test_path_to_location_for_orphan_vertical(self, module_store): r""" Make sure that path_to_location works with a component having multiple vertical parents, from which one of them is orphan. course | chapter | vertical vertical \ / html """ # Get a course with orphan modules course = self.create_course_with_orphans(module_store) # Fetch the required course components. vertical1 = self.store.get_item(BlockUsageLocator(course.id, 'vertical', 'Vertical1')) chapter1 = self.store.get_item(BlockUsageLocator(course.id, 'chapter', 'Chapter1')) orphan_vertical = self.store.get_item(BlockUsageLocator(course.id, 'vertical', 'OrphanVert')) multi_parent_html = self.store.get_item(BlockUsageLocator(course.id, 'html', 'multi_parent_html')) # Verify `OrphanVert` is an orphan self.assertIn(orphan_vertical.location, self.store.get_orphans(course.id)) # Verify `multi_parent_html` is child of both `Vertical1` and `OrphanVert` self.assertIn(multi_parent_html.location, orphan_vertical.children) self.assertIn(multi_parent_html.location, vertical1.children) # HTML component has `vertical1` as its parent. html_parent = self.store.get_parent_location(multi_parent_html.location) self.assertNotEqual(six.text_type(html_parent), six.text_type(orphan_vertical.location)) self.assertEqual(six.text_type(html_parent), six.text_type(vertical1.location)) # Get path of the `multi_parent_html` & verify path_to_location returns a expected path path = path_to_location(self.store, multi_parent_html.location) expected_path = ( course.id, chapter1.location.block_id, vertical1.location.block_id, multi_parent_html.location.block_id, "", path[-1] ) self.assertIsNotNone(path) self.assertEqual(len(path), 6) self.assertEqual(path, expected_path)
def setUp(self): super(TestOrderCreation, self).setUp() # Set enforce_csrf_checks=True here because testing must still # work (webhooks are explicitly exempted from CSRF protection) self.client = Client(enforce_csrf_checks=True) conf = settings.WEBHOOK_SETTINGS['edx_shopify'] # Calculate 3 SHA256 hashes over the payload, which the # webhook handler must verify and accept or reject: a correct # hash, a hash from the wrong (reversed) key, and a corrupted # hash containing an invalid base64 character. correct_hash = hmac.new(conf['api_key'], self.raw_payload, hashlib.sha256) incorrect_hash = hmac.new(conf['api_key'][::-1], self.raw_payload, hashlib.sha256) self.correct_signature = base64.b64encode(correct_hash.digest()) self.incorrect_signature = base64.b64encode(incorrect_hash.digest()) self.corrupt_signature = "-%s" % base64.b64encode( correct_hash.digest())[1:] # noqa: E501 # Set up a mock course course_id_string = 'course-v1:org+course+run1' cl = CourseLocator.from_string(course_id_string) bul = BlockUsageLocator(cl, u'course', u'course') course = Mock() course.id = cl course.system = Mock() course.scope_ids = Mock() course.scope_id.user_id = None course.scope_ids.block_type = u'course' course.scope_ids.def_id = bul course.scope_ids.usage_id = bul course.location = bul course.display_name = u'Course - Run 1' self.course_id_string = course_id_string self.cl = cl self.course = course email_params = { 'registration_url': u'https://localhost:8000/register', # noqa: E501 'course_about_url': u'https://localhost:8000/courses/course-v1:org+course+run1/about', # noqa: E501 'site_name': 'localhost:8000', 'course': course, 'is_shib_course': None, 'display_name': u'Course - Run 1', 'auto_enroll': True, 'course_url': u'https://localhost:8000/courses/course-v1:org+course+run1/' } # noqa: E501 self.email_params = email_params
def test_unicode_loads(self): """ Test that getting items from the test_unicode course works """ assert self.draft_store.get_item( BlockUsageLocator(CourseLocator('edX', 'test_unicode', '2012_Fall', deprecated=True), 'course', '2012_Fall', deprecated=True)) is not None # All items with ascii-only filenames should load properly. assert self.draft_store.get_item( BlockUsageLocator(CourseLocator('edX', 'test_unicode', '2012_Fall', deprecated=True), 'video', 'Welcome', deprecated=True)) is not None assert self.draft_store.get_item( BlockUsageLocator(CourseLocator('edX', 'test_unicode', '2012_Fall', deprecated=True), 'video', 'Welcome', deprecated=True)) is not None assert self.draft_store.get_item( BlockUsageLocator(CourseLocator('edX', 'test_unicode', '2012_Fall', deprecated=True), 'chapter', 'Overview', deprecated=True)) is not None
def setUp(self): super().setUp() self.course_key = CourseLocator('org', 'course', str(uuid4())) self.usage_key = BlockUsageLocator(course_key=self.course_key, block_type='course', block_id='course') self.params = self._create_bsm_params()
def test_find_one(self): assert_not_none( self.draft_store._find_one( BlockUsageLocator(CourseLocator('edX', 'toy', '2012_Fall', deprecated=True), 'course', '2012_Fall', deprecated=True)), ) assert_not_none( self.draft_store._find_one( BlockUsageLocator(CourseLocator('edX', 'simple', '2012_Fall', deprecated=True), 'course', '2012_Fall', deprecated=True)), ) assert_not_none( self.draft_store._find_one(BlockUsageLocator(CourseLocator('edX', 'toy', '2012_Fall', deprecated=True), 'video', 'Welcome', deprecated=True)), )
def to_block_locator(self): """ Returns a BlockUsageLocator for this location. """ return BlockUsageLocator( course_key=self.course_key.to_course_locator(), block_type=self.block_type, block_id=self.block_id)
def _gen_usage_key(self): return BlockUsageLocator( self.course_key, self._gen_block_type(), # We've seen at most 1000 blocks requested in a course, so we'll # generate at most that many different indexes. str(numpy.random.randint(0, 1000)), )
def setUp(self): super(BlockStructureModelTestCase, self).setUp() self.course_key = CourseLocator('org', 'course', unicode(uuid4())) self.usage_key = BlockUsageLocator(course_key=self.course_key, block_type='course', block_id='course') self.params = self._create_bsm_params()
def setUp(self): super().setUp() self.annotatable = AnnotatableBlock( get_test_system(), DictFieldData({'data': self.sample_xml}), ScopeIds( None, None, None, BlockUsageLocator(CourseLocator('org', 'course', 'run'), 'category', 'name')))
def _block(self, block): """Return a UsageKey for the block ``block``.""" course = block // 1000 return BlockUsageLocator( self._course(course), self._block_type(block), 'block{}'.format(block) )
def setUp(self): super().setUp() self.environ = {'wsgi.url_scheme': 'http', 'REQUEST_METHOD': 'POST'} self.request_body_xml_template = textwrap.dedent(""" <?xml version = "1.0" encoding = "UTF-8"?> <imsx_POXEnvelopeRequest xmlns = "{namespace}"> <imsx_POXHeader> <imsx_POXRequestHeaderInfo> <imsx_version>V1.0</imsx_version> <imsx_messageIdentifier>{messageIdentifier}</imsx_messageIdentifier> </imsx_POXRequestHeaderInfo> </imsx_POXHeader> <imsx_POXBody> <{action}> <resultRecord> <sourcedGUID> <sourcedId>{sourcedId}</sourcedId> </sourcedGUID> <result> <resultScore> <language>en-us</language> <textString>{grade}</textString> </resultScore> </result> </resultRecord> </{action}> </imsx_POXBody> </imsx_POXEnvelopeRequest> """) self.system = get_test_system() self.system.get_real_user = Mock() self.system.publish = Mock() self.system.rebind_noauth_module_to_user = Mock() self.user_id = self.system.anonymous_student_id self.xmodule = LTIBlock( self.system, DictFieldData({}), ScopeIds(None, None, None, BlockUsageLocator(self.system.course_id, 'lti', 'name')) ) self.lti_id = self.xmodule.lti_id self.unquoted_resource_link_id = '{}-i4x-2-3-lti-31de800015cf4afb973356dbe81496df'.format( self.xmodule.runtime.hostname ) sourced_id = ':'.join(parse.quote(i) for i in (self.lti_id, self.unquoted_resource_link_id, self.user_id)) # lint-amnesty, pylint: disable=line-too-long self.defaults = { 'namespace': "http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0", 'sourcedId': sourced_id, 'action': 'replaceResultRequest', 'grade': 0.5, 'messageIdentifier': '528243ba5241b', } self.xmodule.due = None self.xmodule.graceperiod = None
def _create_item(self, category, name, data, metadata, parent_category, parent_name, draft=True, split=True): """ Create the item of the given category and block id in split and old mongo, add it to the optional parent. The parent category is only needed because old mongo requires it for the id. Note: if draft = False, it will create the draft and then publish it; so, it will overwrite any existing draft for both the new item and the parent """ location = self.old_course_key.make_usage_key(category, name) self.draft_mongo.create_item(self.user_id, location.course_key, location.block_type, block_id=location.block_id, definition_data=data, metadata=metadata, runtime=self.runtime) if not draft: self.draft_mongo.publish(location, self.user_id) if isinstance(data, basestring): fields = {'data': data} else: fields = data.copy() fields.update(metadata) if parent_name: # add child to parent in mongo parent_location = self.old_course_key.make_usage_key( parent_category, parent_name) parent = self.draft_mongo.get_item(parent_location) parent.children.append(location) self.draft_mongo.update_item(parent, self.user_id) if not draft: self.draft_mongo.publish(parent_location, self.user_id) # create child for split if split: self.split_mongo.create_child( self.user_id, BlockUsageLocator(course_key=self.split_course_key, block_type=parent_category, block_id=parent_name), category, block_id=name, fields=fields) else: if split: self.split_mongo.create_item(self.user_id, self.split_course_key, category, block_id=name, fields=fields)