def test_existing_video(self, course_id): xml = self.make_import_xml( video_dict={ "client_video_id": "new_client_video_id", "duration": 0, }, encoded_video_dicts=[ constants.ENCODED_VIDEO_DICT_FISH_DESKTOP, { "url": "http://example.com/new_url", "file_size": 2733256, "bitrate": 1597804, "profile": "mobile", }, ]) api.import_from_xml(xml, constants.VIDEO_DICT_FISH["edx_video_id"], course_id) video = Video.objects.get( edx_video_id=constants.VIDEO_DICT_FISH["edx_video_id"]) self.assert_video_matches_dict(video, constants.VIDEO_DICT_FISH) self.assert_encoded_video_matches_dict( video.encoded_videos.get( profile__profile_name=constants.PROFILE_MOBILE), constants.ENCODED_VIDEO_DICT_MOBILE) self.assertFalse( video.encoded_videos.filter( profile__profile_name=constants.PROFILE_DESKTOP).exists()) self.assertTrue(video.courses.filter(course_id=course_id).exists())
def test_existing_video_with_invalid_course_id(self): xml = self.make_import_xml(video_dict=constants.VIDEO_DICT_FISH) with self.assertRaises(ValCannotCreateError): api.import_from_xml( xml, edx_video_id=constants.VIDEO_DICT_FISH["edx_video_id"], course_id="x" * 300)
def test_new_video_minimal(self): edx_video_id = "test_edx_video_id" xml = self.make_import_xml(video_dict={"client_video_id": "dummy", "duration": "0"}) api.import_from_xml(xml, edx_video_id) video = Video.objects.get(edx_video_id=edx_video_id) self.assertFalse(video.encoded_videos.all().exists()) self.assertFalse(video.courses.all().exists())
def test_new_video_minimal(self): edx_video_id = "test_edx_video_id" xml = self.make_import_xml(video_dict={ "client_video_id": "dummy", "duration": "0", }) api.import_from_xml(xml, edx_video_id) video = Video.objects.get(edx_video_id=edx_video_id) self.assertFalse(video.encoded_videos.all().exists()) self.assertFalse(video.courses.all().exists())
def test_new_video_full(self): new_course_id = "new_course_id" xml = self.make_import_xml( video_dict=constants.VIDEO_DICT_STAR, encoded_video_dicts=[constants.ENCODED_VIDEO_DICT_STAR] ) api.import_from_xml(xml, constants.VIDEO_DICT_STAR["edx_video_id"], new_course_id) video = Video.objects.get(edx_video_id=constants.VIDEO_DICT_STAR["edx_video_id"]) self.assert_video_matches_dict(video, constants.VIDEO_DICT_STAR) self.assert_encoded_video_matches_dict( video.encoded_videos.get(profile__profile_name=constants.PROFILE_MOBILE), constants.ENCODED_VIDEO_DICT_STAR ) video.courses.get(course_id=new_course_id)
def test_existing_video(self, course_id): xml = self.make_import_xml( video_dict={"client_video_id": "new_client_video_id", "duration": 0}, encoded_video_dicts=[ constants.ENCODED_VIDEO_DICT_FISH_DESKTOP, {"url": "http://example.com/new_url", "file_size": 2733256, "bitrate": 1597804, "profile": "mobile"}, ], ) api.import_from_xml(xml, constants.VIDEO_DICT_FISH["edx_video_id"], course_id) video = Video.objects.get(edx_video_id=constants.VIDEO_DICT_FISH["edx_video_id"]) self.assert_video_matches_dict(video, constants.VIDEO_DICT_FISH) self.assert_encoded_video_matches_dict( video.encoded_videos.get(profile__profile_name=constants.PROFILE_MOBILE), constants.ENCODED_VIDEO_DICT_MOBILE, ) self.assertFalse(video.encoded_videos.filter(profile__profile_name=constants.PROFILE_DESKTOP).exists()) self.assertTrue(video.courses.filter(course_id=course_id).exists())
def test_unknown_profile(self): profile = "unknown_profile" xml = self.make_import_xml( video_dict=constants.VIDEO_DICT_STAR, encoded_video_dicts=[ constants.ENCODED_VIDEO_DICT_STAR, { "url": "http://example.com/dummy", "file_size": -1, # Invalid data in an unknown profile is ignored "bitrate": 0, "profile": profile, } ] ) api.import_from_xml(xml, constants.VIDEO_DICT_STAR["edx_video_id"]) video = Video.objects.get(edx_video_id=constants.VIDEO_DICT_STAR["edx_video_id"]) self.assertFalse(video.encoded_videos.filter(profile__profile_name=profile).exists())
def test_unknown_profile(self): profile = "unknown_profile" xml = self.make_import_xml( video_dict=constants.VIDEO_DICT_STAR, encoded_video_dicts=[ constants.ENCODED_VIDEO_DICT_STAR, { "url": "http://example.com/dummy", "file_size": -1, # Invalid data in an unknown profile is ignored "bitrate": 0, "profile": profile, }, ], ) api.import_from_xml(xml, constants.VIDEO_DICT_STAR["edx_video_id"]) video = Video.objects.get(edx_video_id=constants.VIDEO_DICT_STAR["edx_video_id"]) self.assertFalse(video.encoded_videos.filter(profile__profile_name=profile).exists())
def import_video_info_into_val(self, xml, course_id): """ Import parsed video info from `xml` into edxval. Arguments: xml (lxml object): xml representation of video to be imported course_id (str): course id """ if self.edx_video_id is not None: edx_video_id = self.edx_video_id.strip() video_asset_elem = xml.find('video_asset') if edxval_api and video_asset_elem is not None: # Always pass the edx_video_id, Whether the video is internal or external # In case of external, we only need to import transcripts and for that # purpose video id is already present in the xml edxval_api.import_from_xml(video_asset_elem, edx_video_id, course_id=course_id)
def import_video_info_into_val(self, xml, course_id): """ Import parsed video info from `xml` into edxval. Arguments: xml (lxml object): xml representation of video to be imported course_id (str): course id """ if self.edx_video_id is not None: edx_video_id = self.edx_video_id.strip() video_asset_elem = xml.find('video_asset') if edxval_api and video_asset_elem is not None: # Always pass the edx_video_id, Whether the video is internal or external # In case of external, we only need to import transcripts and for that # purpose video id is already present in the xml edxval_api.import_from_xml( video_asset_elem, edx_video_id, course_id=course_id )
def import_video_info_into_val(self, xml, resource_fs, course_id): """ Import parsed video info from `xml` into edxval. Arguments: xml (lxml object): xml representation of video to be imported. resource_fs (OSFS): Import file system. course_id (str): course id """ edx_video_id = clean_video_id(self.edx_video_id) # Create video_asset is not already present. video_asset_elem = xml.find('video_asset') if video_asset_elem is None: video_asset_elem = etree.Element('video_asset') # This will be a dict containing the list of names of the external transcripts. # Example: # { # 'en': ['The_Flash.srt', 'Harry_Potter.srt'], # 'es': ['Green_Arrow.srt'] # } external_transcripts = defaultdict(list) # Add trancript from self.sub and self.youtube_id_1_0 fields. external_transcripts['en'] = [ subs_filename(transcript, 'en') for transcript in [self.sub, self.youtube_id_1_0] if transcript ] for language_code, transcript in self.transcripts.items(): external_transcripts[language_code].append(transcript) if edxval_api: edx_video_id = edxval_api.import_from_xml( video_asset_elem, edx_video_id, resource_fs, EXPORT_IMPORT_STATIC_DIR, external_transcripts, course_id=course_id ) return edx_video_id
def _parse_video_xml(cls, xml, id_generator=None): """ Parse video fields out of xml_data. The fields are set if they are present in the XML. Arguments: id_generator is used to generate course-specific urls and identifiers """ field_data = {} # Convert between key types for certain attributes -- # necessary for backwards compatibility. conversions = { # example: 'start_time': cls._example_convert_start_time } # Convert between key names for certain attributes -- # necessary for backwards compatibility. compat_keys = { 'from': 'start_time', 'to': 'end_time' } sources = xml.findall('source') if sources: field_data['html5_sources'] = [ele.get('src') for ele in sources] track = xml.find('track') if track is not None: field_data['track'] = track.get('src') handout = xml.find('handout') if handout is not None: field_data['handout'] = handout.get('src') transcripts = xml.findall('transcript') if transcripts: field_data['transcripts'] = {tr.get('language'): tr.get('src') for tr in transcripts} for attr, value in xml.items(): if attr in compat_keys: attr = compat_keys[attr] if attr in cls.metadata_to_strip + ('url_name', 'name'): continue if attr == 'youtube': speeds = cls._parse_youtube(value) for speed, youtube_id in speeds.items(): # should have made these youtube_id_1_00 for # cleanliness, but hindsight doesn't need glasses normalized_speed = speed[:-1] if speed.endswith('0') else speed # If the user has specified html5 sources, make sure we don't use the default video if youtube_id != '' or 'html5_sources' in field_data: field_data['youtube_id_{0}'.format(normalized_speed.replace('.', '_'))] = youtube_id elif attr in conversions: field_data[attr] = conversions[attr](value) elif attr not in cls.fields: field_data.setdefault('xml_attributes', {})[attr] = value else: # We export values with json.dumps (well, except for Strings, but # for about a month we did it for Strings also). field_data[attr] = deserialize_field(cls.fields[attr], value) course_id = getattr(id_generator, 'target_course_id', None) # Update the handout location with current course_id if 'handout' in field_data.keys() and course_id: handout_location = StaticContent.get_location_from_path(field_data['handout']) if isinstance(handout_location, AssetLocator): handout_new_location = StaticContent.compute_location(course_id, handout_location.path) field_data['handout'] = StaticContent.serialize_asset_key_with_slash(handout_new_location) # For backwards compatibility: Add `source` if XML doesn't have `download_video` # attribute. if 'download_video' not in field_data and sources: field_data['source'] = field_data['html5_sources'][0] # For backwards compatibility: if XML doesn't have `download_track` attribute, # it means that it is an old format. So, if `track` has some value, # `download_track` needs to have value `True`. if 'download_track' not in field_data and track is not None: field_data['download_track'] = True video_asset_elem = xml.find('video_asset') if ( edxval_api and video_asset_elem is not None and 'edx_video_id' in field_data ): # Allow ValCannotCreateError to escape edxval_api.import_from_xml( video_asset_elem, field_data['edx_video_id'], course_id=course_id ) # load license if it exists field_data = LicenseMixin.parse_license_from_xml(field_data, xml) return field_data
def _parse_video_xml(cls, xml, id_generator=None): """ Parse video fields out of xml_data. The fields are set if they are present in the XML. Arguments: id_generator is used to generate course-specific urls and identifiers """ field_data = {} # Convert between key types for certain attributes -- # necessary for backwards compatibility. conversions = { # example: 'start_time': cls._example_convert_start_time } # Convert between key names for certain attributes -- # necessary for backwards compatibility. compat_keys = {"from": "start_time", "to": "end_time"} sources = xml.findall("source") if sources: field_data["html5_sources"] = [ele.get("src") for ele in sources] track = xml.find("track") if track is not None: field_data["track"] = track.get("src") handout = xml.find("handout") if handout is not None: field_data["handout"] = handout.get("src") transcripts = xml.findall("transcript") if transcripts: field_data["transcripts"] = {tr.get("language"): tr.get("src") for tr in transcripts} for attr, value in xml.items(): if attr in compat_keys: attr = compat_keys[attr] if attr in cls.metadata_to_strip + ("url_name", "name"): continue if attr == "youtube": speeds = cls._parse_youtube(value) for speed, youtube_id in speeds.items(): # should have made these youtube_id_1_00 for # cleanliness, but hindsight doesn't need glasses normalized_speed = speed[:-1] if speed.endswith("0") else speed # If the user has specified html5 sources, make sure we don't use the default video if youtube_id != "" or "html5_sources" in field_data: field_data["youtube_id_{0}".format(normalized_speed.replace(".", "_"))] = youtube_id elif attr in conversions: field_data[attr] = conversions[attr](value) elif attr not in cls.fields: field_data.setdefault("xml_attributes", {})[attr] = value else: # We export values with json.dumps (well, except for Strings, but # for about a month we did it for Strings also). field_data[attr] = deserialize_field(cls.fields[attr], value) # For backwards compatibility: Add `source` if XML doesn't have `download_video` # attribute. if "download_video" not in field_data and sources: field_data["source"] = field_data["html5_sources"][0] # For backwards compatibility: if XML doesn't have `download_track` attribute, # it means that it is an old format. So, if `track` has some value, # `download_track` needs to have value `True`. if "download_track" not in field_data and track is not None: field_data["download_track"] = True video_asset_elem = xml.find("video_asset") if edxval_api and video_asset_elem is not None and "edx_video_id" in field_data: # Allow ValCannotCreateError to escape edxval_api.import_from_xml( video_asset_elem, field_data["edx_video_id"], course_id=getattr(id_generator, "target_course_id", None) ) # load license if it exists field_data = LicenseMixin.parse_license_from_xml(field_data, xml) return field_data
def test_existing_video_with_invalid_course_id(self): xml = self.make_import_xml(video_dict=constants.VIDEO_DICT_FISH) with self.assertRaises(ValCannotCreateError): api.import_from_xml(xml, edx_video_id=constants.VIDEO_DICT_FISH["edx_video_id"], course_id="x" * 300)
def assert_invalid_import(self, xml, course_id=None): edx_video_id = "test_edx_video_id" with self.assertRaises(ValCannotCreateError): api.import_from_xml(xml, edx_video_id, course_id) self.assertFalse( Video.objects.filter(edx_video_id=edx_video_id).exists())
def assert_invalid_import(self, xml, course_id=None): edx_video_id = "test_edx_video_id" with self.assertRaises(ValCannotCreateError): api.import_from_xml(xml, edx_video_id, course_id) self.assertFalse(Video.objects.filter(edx_video_id=edx_video_id).exists())