def test_fetch_video(self): video_value = 'test_video_id' with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.course.id): CourseDetails.update_about_video(self.course, video_value, self.user.id) assert CourseDetails.fetch_youtube_video_id(self.course.id) == video_value video_url = CourseDetails.fetch_video_url(self.course.id) self.assertRegex(video_url, fr'http://.*{video_value}')
def test_fetch_video(self): video_value = 'test_video_id' with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.course.id): CourseDetails.update_about_video(self.course, video_value, self.user.id) self.assertEqual(CourseDetails.fetch_youtube_video_id(self.course.id), video_value) video_url = CourseDetails.fetch_video_url(self.course.id) self.assertRegexpMatches(video_url, r'http://.*{}'.format(video_value))
def test_basic(self): expected_data = { 'course_id': u'edX/toy/2012_Fall', 'name': u'Toy Course', 'number': u'toy', 'org': u'edX', 'short_description': u'A course about toys.', 'media': { 'course_image': { 'uri': u'/c4x/edX/toy/asset/just_a_test.jpg', }, 'course_video': { 'uri': u'http://www.youtube.com/watch?v=test_youtube_id', } }, 'start': u'2015-07-17T12:00:00Z', 'start_type': u'timestamp', 'start_display': u'July 17, 2015', 'end': u'2015-09-19T18:00:00Z', 'enrollment_start': u'2015-06-15T00:00:00Z', 'enrollment_end': u'2015-07-15T00:00:00Z', 'blocks_url': u'http://testserver/api/courses/v1/blocks/?course_id=edX%2Ftoy%2F2012_Fall', 'effort': u'6 hours', } course = self.create_course() CourseDetails.update_about_video(course, 'test_youtube_id', self.staff_user.id) # pylint: disable=no-member result = self._get_result(course) self.assertDictEqual(result, expected_data)
def test_basic(self): course = self.create_course() CourseDetails.update_about_video(course, 'test_youtube_id', self.staff_user.id) with check_mongo_calls(self.expected_mongo_calls): result = self._get_result(course) self.assertDictEqual(result, self.expected_data)
def test_fetch_about_attribute(self, attribute_name): attribute_value = 'test_value' with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.course.id): CourseDetails.update_about_item(self.course, attribute_name, attribute_value, self.user.id) assert CourseDetails.fetch_about_attribute( self.course.id, attribute_name) == attribute_value
def test_fetch_about_attribute_error(self): attribute_name = 'not_an_about_attribute' with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.course.id): CourseDetails.update_about_item(self.course, attribute_name, 'test_value', self.user.id) with pytest.raises(ValueError): CourseDetails.fetch_about_attribute(self.course.id, attribute_name)
def test_toggle_pacing_during_course_run(self): SelfPacedConfiguration(enabled=True).save() self.course.start = datetime.datetime.now() self.store.update_item(self.course, self.user.id) details = CourseDetails.fetch(self.course.id) with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.course.id): updated_details = CourseDetails.update_from_json( self.course.id, dict(details.__dict__, self_paced=True), self.user ) self.assertFalse(updated_details.self_paced)
def test_toggle_pacing_during_course_run(self): self.course.start = datetime.datetime.now() self.store.update_item(self.course, self.user.id) details = CourseDetails.fetch(self.course.id) with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.course.id): updated_details = CourseDetails.update_from_json( self.course.id, dict(details.__dict__, self_paced=True), self.user) assert not updated_details.self_paced
def _create_new_course(subject, org, number, run, fields): """ Create a new course. Raises DuplicateCourseError if the course already exists """ org_data = get_organization_by_short_name(org) if not org_data and organizations_enabled(): return store_for_new_course = modulestore().default_modulestore.get_modulestore_type() try: user = User.objects.get(username='******') except User.DoesNotExist: user = User.objects.create( username='******', email='*****@*****.**', first_name='coursecreator', last_name='coursecreator', is_active=True, is_staff=True ) user.set_password('coursecreator') user.save() try: new_course = create_new_course_in_store(store_for_new_course, user, org, number, run, fields) except DuplicateCourseError: existing_course_key = SlashSeparatedCourseKey.from_deprecated_string('course-v1:'+org+'+'+number+'+'+run) new_course = get_course_by_id(existing_course_key) add_organization_course(org_data, new_course.id) # Set description and images for the course course_image_name, course_image_asset_path = store_jacket_image( new_course.id, settings.VODECLIC_COURSE_IMAGE_LOCATION, subject.get("id") + ".png" ) additional_info = { 'display_name': subject.get('title'), 'language': subject.get('language', 'fr'), 'short_description': subject.get('description', ''), 'intro_video': None, 'course_image_name': course_image_name, 'course_image_asset_path': course_image_asset_path, 'start_date': new_course.start, 'end_date': new_course.end, 'enrollment_start': new_course.start, 'enrollment_end': new_course.end } CourseDetails.update_from_json(new_course.id, additional_info, user)
def get_course_description(self, bot, chat_id, course_name=None, course_key=None, enroll=None, enroll_keyboard=False): """ Get description of particular courses by Title or by course_id """ bot.sendChatAction(chat_id=chat_id, action=ChatAction.TYPING) if enroll: result = CourseEnrollment.objects.get(id=enroll) results = [modulestore().get_course(result.course_id)] course_name = results[0].display_name_with_default elif course_key: results = [modulestore().get_course(course_key)] course_name = modulestore().get_course( course_key).display_name_with_default else: results = modulestore().get_courses() results = [ course for course in results if course.scope_ids.block_type == 'course' ] for each in results: if each.display_name_with_default == course_name: message = truncate_course_info( CourseDetails.fetch_about_attribute(each.id, 'overview')) if message == 'null': bot.sendMessage( chat_id=chat_id, text="I'm sorry, but this course has no description") else: bot.sendMessage(chat_id=chat_id, text="*Short course description*", parse_mode=telegram.ParseMode.MARKDOWN) course_key = each.id current_site = Site.objects.get_current() course_title = modulestore().get_course( course_key).display_name_with_default course_url = '[%s](%scourses/%s/)' % ( course_title, current_site, each.id) bot.sendMessage(chat_id=chat_id, text=course_url, parse_mode=telegram.ParseMode.MARKDOWN) if enroll_keyboard: reply_markup = InlineKeyboardMarkup([[ InlineKeyboardButton( text='I like it and I want to enroll', callback_data=json.dumps( {'key': str(course_key)})) ]]) bot.sendMessage(chat_id=chat_id, text=message, reply_markup=reply_markup) else: bot.sendMessage(chat_id=chat_id, text=message) break bot.sendMessage(chat_id=chat_id, reply_markup=ReplyKeyboardHide())
def test_virgin_fetch(self): details = CourseDetails.fetch(self.course.id) self.assertEqual(details.org, self.course.location.org, "Org not copied into") self.assertEqual(details.course_id, self.course.location.course, "Course_id not copied into") self.assertEqual(details.run, self.course.location.name, "Course name not copied into") self.assertEqual(details.course_image_name, self.course.course_image) self.assertIsNotNone(details.start_date.tzinfo) self.assertIsNone( details.end_date, "end date somehow initialized " + str(details.end_date)) self.assertIsNone( details.enrollment_start, "enrollment_start date somehow initialized " + str(details.enrollment_start)) self.assertIsNone( details.enrollment_end, "enrollment_end date somehow initialized " + str(details.enrollment_end)) self.assertIsNone( details.syllabus, "syllabus somehow initialized" + str(details.syllabus)) self.assertIsNone( details.intro_video, "intro_video somehow initialized" + str(details.intro_video)) self.assertIsNone(details.effort, "effort somehow initialized" + str(details.effort)) self.assertIsNone( details.language, "language somehow initialized" + str(details.language)) self.assertFalse(details.self_paced)
def get_overview(self, course_overview): """ Get the representation for SerializerMethodField `overview` """ # Note: This makes a call to the modulestore, unlike the other # fields from CourseSerializer, which get their data # from the CourseOverview object in SQL. return CourseDetails.fetch_about_attribute(course_overview.id, 'overview')
def test_changes_on_webview(self): # Prepare attributes to check for CourseDetails.update_about_item(self.course, 'short_description', 'Edraak Test Description', self.user.id) # Creating a certificate self._add_course_certificates(count=1, signatory_count=2) self._create_edraak_test_template() test_url = get_certificate_url(user_id=self.user.id, course_id=unicode(self.course.id)) # Getting certificate as HTML response = self.client.get(test_url) # Verifying contents self.assertContains(response, 'Edraak Template') self.assertContains(response, 'course_description: Edraak Test Description')
def get_minimum_age(self, course_overview): """ Get the representation for SerializerMethodField `minimum_age` Represents the minimum enrollment age for the course """ # Note: This makes a call to the modulestore, unlike the other # fields from CourseSerializer, which get their data # from the CourseOverview object in SQL. courseDetails = CourseDetails.fetch(course_overview.id) return courseDetails.minimum_age
def test_updates_values_in_mongo_should_be_updated_in_sql(self): """ Test that a already existing course is correctly updated by update_course management command. """ mongo_course = CourseFactory.create(short_description='test') # short_description field is required by FUN Course model CourseDetails.update_about_item( mongo_course, 'short_description', u"Short description", None) self.assertFalse(FUNCourse.objects.all().exists()) call_command('update_courses', course_id=unicode(mongo_course.id)) self.assertTrue(u"Short description", FUNCourse.objects.get(key=mongo_course.id).short_description) CourseDetails.update_about_item( mongo_course, 'short_description', u"Short description changed", None) call_command('update_courses', course_id=unicode(mongo_course.id)) self.assertEqual( u"Short description changed", FUNCourse.objects.get(key=mongo_course.id).short_description)
def get_course_description( self, bot, chat_id, course_name=None, course_key=None, enroll=None, enroll_keyboard=False ): """ Get description of particular courses by Title or by course_id """ bot.sendChatAction(chat_id=chat_id, action=ChatAction.TYPING) if enroll: result = CourseEnrollment.objects.get(id=enroll) results = [modulestore().get_course(result.course_id)] course_name = results[0].display_name_with_default elif course_key: results = [modulestore().get_course(course_key)] course_name = modulestore().get_course(course_key).display_name_with_default else: results = modulestore().get_courses() results = [course for course in results if course.scope_ids.block_type == "course"] for each in results: if each.display_name_with_default == course_name: message = truncate_course_info(CourseDetails.fetch_about_attribute(each.id, "overview")) if message == "null": bot.sendMessage(chat_id=chat_id, text="I'm sorry, but this course has no description") else: bot.sendMessage( chat_id=chat_id, text="*Short course description*", parse_mode=telegram.ParseMode.MARKDOWN ) course_key = each.id current_site = Site.objects.get_current() course_title = modulestore().get_course(course_key).display_name_with_default course_url = "[%s](%scourses/%s/)" % (course_title, current_site, each.id) bot.sendMessage(chat_id=chat_id, text=course_url, parse_mode=telegram.ParseMode.MARKDOWN) if enroll_keyboard: reply_markup = InlineKeyboardMarkup( [ [ InlineKeyboardButton( text="I like it and I want to enroll", callback_data=json.dumps({"key": str(course_key)}), ) ] ] ) bot.sendMessage(chat_id=chat_id, text=message, reply_markup=reply_markup) else: bot.sendMessage(chat_id=chat_id, text=message) break bot.sendMessage(chat_id=chat_id, reply_markup=ReplyKeyboardHide())
def get_coursed_and_create_matrix(): results = [course for course in modulestore().get_courses() if course.scope_ids.block_type == "course"] new_matrix = TfidMatrixAllCourses.objects.all().first() or TfidMatrixAllCourses() print new_matrix.matrix.shape[0] != len(results) if new_matrix.matrix.shape[0] != len(results): all_courses = [re.sub("<[^>]*>", "", CourseDetails.fetch_about_attribute(x.id, "overview")) for x in results] MatrixEdxCoursesId.objects.all().delete() map(lambda x: MatrixEdxCoursesId.objects.create(course_key=x.id, course_index=results.index(x)), results) stemmer = snowballstemmer.stemmer("english") courses_stem = [" ".join(stemmer.stemWords(x.split())) for x in all_courses] vect = TfidfVectorizer(stop_words=get_stop_words(), lowercase=True, dtype=np.float32) matrix = vect.fit_transform(courses_stem) new_matrix.matrix = matrix new_matrix.save()
def get_coursed_and_create_matrix(): results = [course for course in modulestore().get_courses() if course.scope_ids.block_type == 'course'] new_matrix = TfidMatrixAllCourses.objects.all().first() or TfidMatrixAllCourses() print new_matrix.matrix.shape[0] != len(results) if new_matrix.matrix.shape[0] != len(results): all_courses = [re.sub('<[^>]*>', '', CourseDetails.fetch_about_attribute(x.id, 'overview')) for x in results] MatrixEdxCoursesId.objects.all().delete() map(lambda x: MatrixEdxCoursesId.objects.create(course_key=x.id, course_index=results.index(x)), results) stemmer = snowballstemmer.stemmer('english') courses_stem = [' '.join(stemmer.stemWords(x.split())) for x in all_courses] vect = TfidfVectorizer(stop_words=get_stop_words(), lowercase=True, dtype=np.float32) matrix = vect.fit_transform(courses_stem) new_matrix.matrix = matrix new_matrix.save()
def update_course(self, mongo_course, assign_universities=False): ''' For the given course, we create or update the corresponding course in SQL Course table. ''' course_handler = CourseHandler(mongo_course) key = course_handler.key self.stdout.write('Updating data for course {}\n'.format(key)) course, was_created = Course.objects.get_or_create(key=key) if was_created or assign_universities: university = course_handler.assign_university(course) if university: self.stdout.write( '\t University assigned to "{}"\n'.format(key)) else: self.stdout.write( '\t No university assigned to "{}"\n'.format(key)) course.is_active = True course.university_display_name = course_handler.university_name course.title = course_handler.title course.image_url = course_handler.image_url thumbnails_info = {} for thumbnail_alias, thumbnails_options in \ courses_settings.FUN_THUMBNAIL_OPTIONS.items(): thumbnail = course_handler.make_thumbnail(thumbnails_options) if thumbnail: thumbnails_info[thumbnail_alias] = thumbnail.url course.thumbnails_info = thumbnails_info course.start_date = mongo_course.start course.enrollment_start_date = mongo_course.enrollment_start course.enrollment_end_date = mongo_course.enrollment_end course.end_date = mongo_course.end course_key = CourseKey.from_string(key) course.short_description = CourseDetails.fetch_about_attribute( course_key, 'short_description', ) course.save() del course self.stdout.write('Updated course {}\n'.format(key)) return None
def test_virgin_fetch(self): details = CourseDetails.fetch(self.course.id) self.assertEqual(details.org, self.course.location.org, "Org not copied into") self.assertEqual(details.course_id, self.course.location.course, "Course_id not copied into") self.assertEqual(details.run, self.course.location.name, "Course name not copied into") self.assertEqual(details.course_image_name, self.course.course_image) self.assertIsNotNone(details.start_date.tzinfo) self.assertIsNone(details.end_date, "end date somehow initialized " + str(details.end_date)) self.assertIsNone( details.enrollment_start, "enrollment_start date somehow initialized " + str(details.enrollment_start) ) self.assertIsNone( details.enrollment_end, "enrollment_end date somehow initialized " + str(details.enrollment_end) ) self.assertIsNone(details.syllabus, "syllabus somehow initialized" + str(details.syllabus)) self.assertIsNone(details.intro_video, "intro_video somehow initialized" + str(details.intro_video)) self.assertIsNone(details.effort, "effort somehow initialized" + str(details.effort)) self.assertIsNone(details.language, "language somehow initialized" + str(details.language)) self.assertFalse(details.self_paced)
def test_virgin_fetch(self): details = CourseDetails.fetch(self.course.id) assert details.org == self.course.location.org, 'Org not copied into' assert details.course_id == self.course.location.course, 'Course_id not copied into' assert details.run == self.course.location.block_id, 'Course name not copied into' assert details.course_image_name == self.course.course_image assert details.start_date.tzinfo is not None assert details.end_date is None, ('end date somehow initialized ' + str(details.end_date)) assert details.enrollment_start is None,\ ('enrollment_start date somehow initialized ' + str(details.enrollment_start)) assert details.enrollment_end is None,\ ('enrollment_end date somehow initialized ' + str(details.enrollment_end)) assert details.certificate_available_date is None,\ ('certificate_available_date date somehow initialized ' + str(details.certificate_available_date)) assert details.syllabus is None, ('syllabus somehow initialized' + str(details.syllabus)) assert details.intro_video is None, ('intro_video somehow initialized' + str(details.intro_video)) assert details.effort is None, ('effort somehow initialized' + str(details.effort)) assert details.language is None, ('language somehow initialized' + str(details.language)) assert not details.self_paced
def update_course(self, mongo_course, assign_universities=False): ''' For the given course, we create or update the corresponding course in SQL Course table. ''' course_handler = CourseHandler(mongo_course) key = course_handler.key self.stdout.write('Updating data for course {}\n'.format(key)) course, was_created = Course.objects.get_or_create(key=key) if was_created or assign_universities: university = course_handler.assign_university(course) if university: self.stdout.write('\t University assigned to "{}"\n'.format(key)) else: self.stdout.write('\t No university assigned to "{}"\n'.format(key)) course.is_active = True course.university_display_name = course_handler.university_name course.title = course_handler.title course.image_url = course_handler.image_url thumbnails_info = {} for thumbnail_alias, thumbnails_options in \ courses_settings.FUN_THUMBNAIL_OPTIONS.items(): thumbnail = course_handler.make_thumbnail(thumbnails_options) if thumbnail: thumbnails_info[thumbnail_alias] = thumbnail.url course.thumbnails_info = thumbnails_info course.start_date = mongo_course.start course.enrollment_start_date = mongo_course.enrollment_start course.enrollment_end_date = mongo_course.enrollment_end course.end_date = mongo_course.end course_key = CourseKey.from_string(key) course.short_description = CourseDetails.fetch_about_attribute( course_key, 'short_description', ) course.save() del course self.stdout.write('Updated course {}\n'.format(key)) return None
def setUpClass(cls): super().setUpClass() cls.course = CourseFactory.create() cls.course_without_about = CourseFactory.create( catalog_visibility=CATALOG_VISIBILITY_NONE) cls.course_with_about = CourseFactory.create( catalog_visibility=CATALOG_VISIBILITY_ABOUT) cls.purchase_course = CourseFactory.create( org='MITx', number='buyme', display_name='Course To Buy') CourseDetails.update_about_item(cls.course, 'overview', 'OOGIE BLOOGIE', None) CourseDetails.update_about_item(cls.course_without_about, 'overview', 'WITHOUT ABOUT', None) CourseDetails.update_about_item(cls.course_with_about, 'overview', 'WITH ABOUT', None)
def test_update_and_fetch(self): jsondetails = CourseDetails.fetch(self.course.id) jsondetails.syllabus = "<a href='foo'>bar</a>" # encode - decode to convert date fields and other data which changes form with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.course.id): self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).syllabus, jsondetails.syllabus, "After set syllabus") jsondetails.short_description = "Short Description" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).short_description, jsondetails.short_description, "After set short_description") jsondetails.overview = "Overview" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).overview, jsondetails.overview, "After set overview") jsondetails.intro_video = "intro_video" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).intro_video, jsondetails.intro_video, "After set intro_video") jsondetails.effort = "effort" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).effort, jsondetails.effort, "After set effort") jsondetails.self_paced = True self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).self_paced, jsondetails.self_paced) jsondetails.start_date = datetime.datetime(2010, 10, 1, 0, tzinfo=UTC) self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).start_date, jsondetails.start_date) jsondetails.end_date = datetime.datetime(2011, 10, 1, 0, tzinfo=UTC) self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).end_date, jsondetails.end_date) jsondetails.certificate_available_date = datetime.datetime( 2010, 10, 1, 0, tzinfo=UTC) self.assertEqual( CourseDetails.update_from_json( self.course.id, jsondetails.__dict__, self.user).certificate_available_date, jsondetails.certificate_available_date) jsondetails.course_image_name = "an_image.jpg" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).course_image_name, jsondetails.course_image_name) jsondetails.banner_image_name = "an_image.jpg" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).banner_image_name, jsondetails.banner_image_name) jsondetails.video_thumbnail_image_name = "an_image.jpg" self.assertEqual( CourseDetails.update_from_json( self.course.id, jsondetails.__dict__, self.user).video_thumbnail_image_name, jsondetails.video_thumbnail_image_name) jsondetails.language = "hr" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).language, jsondetails.language) jsondetails.learning_info = ["test", "test"] self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).learning_info, jsondetails.learning_info) jsondetails.instructor_info = { "instructors": [{ "name": "test", "title": "test", "organization": "test", "image": "test", "bio": "test" }] } self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).instructor_info, jsondetails.instructor_info)
def test_update_and_fetch(self): SelfPacedConfiguration(enabled=True).save() jsondetails = CourseDetails.fetch(self.course.id) jsondetails.syllabus = "<a href='foo'>bar</a>" # encode - decode to convert date fields and other data which changes form with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.course.id): self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).syllabus, jsondetails.syllabus, "After set syllabus") jsondetails.short_description = "Short Description" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).short_description, jsondetails.short_description, "After set short_description") jsondetails.overview = "Overview" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).overview, jsondetails.overview, "After set overview") jsondetails.intro_video = "intro_video" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).intro_video, jsondetails.intro_video, "After set intro_video") jsondetails.effort = "effort" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).effort, jsondetails.effort, "After set effort") jsondetails.self_paced = True self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).self_paced, jsondetails.self_paced) jsondetails.start_date = datetime.datetime(2010, 10, 1, 0, tzinfo=UTC()) self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).start_date, jsondetails.start_date) jsondetails.end_date = datetime.datetime(2011, 10, 1, 0, tzinfo=UTC()) self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).end_date, jsondetails.end_date) jsondetails.course_image_name = "an_image.jpg" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).course_image_name, jsondetails.course_image_name) jsondetails.language = "hr" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).language, jsondetails.language)
def get_effort(self, course): """ Get the representation for SerializerMethodField `effort` """ return CourseDetails.fetch_effort(course.id)
def _create_or_update(cls, course): """ Creates or updates a CourseOverview object from a CourseDescriptor. Does not touch the database, simply constructs and returns an overview from the given course. Arguments: course (CourseDescriptor): any course descriptor object Returns: CourseOverview: created or updated overview extracted from the given course """ from lms.djangoapps.certificates.api import get_active_web_certificate from openedx.core.lib.courses import course_image_url # Workaround for a problem discovered in https://openedx.atlassian.net/browse/TNL-2806. # If the course has a malformed grading policy such that # course._grading_policy['GRADE_CUTOFFS'] = {}, then # course.lowest_passing_grade will raise a ValueError. # Work around this for now by defaulting to None. try: lowest_passing_grade = course.lowest_passing_grade except ValueError: lowest_passing_grade = None display_name = course.display_name start = course.start end = course.end max_student_enrollments_allowed = course.max_student_enrollments_allowed if isinstance(course.id, CCXLocator): from lms.djangoapps.ccx.utils import get_ccx_from_ccx_locator ccx = get_ccx_from_ccx_locator(course.id) display_name = ccx.display_name start = ccx.start end = ccx.due max_student_enrollments_allowed = ccx.max_student_enrollments_allowed course_overview = cls.objects.filter(id=course.id) if course_overview.exists(): log.info(u'Updating course overview for %s.', six.text_type(course.id)) course_overview = course_overview.first() else: log.info(u'Creating course overview for %s.', six.text_type(course.id)) course_overview = cls() course_overview.version = cls.VERSION course_overview.id = course.id course_overview._location = course.location course_overview.org = course.location.org course_overview.display_name = display_name course_overview.display_number_with_default = course.display_number_with_default course_overview.display_org_with_default = course.display_org_with_default course_overview.start = start course_overview.end = end course_overview.advertised_start = course.advertised_start course_overview.announcement = course.announcement course_overview.course_image_url = course_image_url(course) course_overview.social_sharing_url = course.social_sharing_url course_overview.certificates_display_behavior = course.certificates_display_behavior course_overview.certificates_show_before_end = course.certificates_show_before_end course_overview.cert_html_view_enabled = course.cert_html_view_enabled course_overview.has_any_active_web_certificate = ( get_active_web_certificate(course) is not None) course_overview.cert_name_short = course.cert_name_short course_overview.cert_name_long = course.cert_name_long course_overview.certificate_available_date = course.certificate_available_date course_overview.lowest_passing_grade = lowest_passing_grade course_overview.end_of_course_survey_url = course.end_of_course_survey_url course_overview.days_early_for_beta = course.days_early_for_beta course_overview.mobile_available = course.mobile_available course_overview.visible_to_staff_only = course.visible_to_staff_only course_overview._pre_requisite_courses_json = json.dumps( course.pre_requisite_courses) course_overview.enrollment_start = course.enrollment_start course_overview.enrollment_end = course.enrollment_end course_overview.enrollment_domain = course.enrollment_domain course_overview.invitation_only = course.invitation_only course_overview.max_student_enrollments_allowed = max_student_enrollments_allowed course_overview.catalog_visibility = course.catalog_visibility course_overview.short_description = CourseDetails.fetch_about_attribute( course.id, 'short_description') course_overview.effort = CourseDetails.fetch_about_attribute( course.id, 'effort') course_overview.course_video_url = CourseDetails.fetch_video_url( course.id) course_overview.self_paced = course.self_paced if not CatalogIntegration.is_enabled(): course_overview.language = course.language return course_overview
def test_validate_certificate_settings_v1(self, stored_date, stored_behavior, expected_date, expected_behavior): """Test that method just returns passed in arguments if v2 is off""" assert CourseDetails.validate_certificate_settings( stored_date, stored_behavior) == (expected_date, expected_behavior)
def test_fetch_about_attribute_error(self): attribute_name = 'not_an_about_attribute' with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.course.id): CourseDetails.update_about_item(self.course, attribute_name, 'test_value', self.user.id) with self.assertRaises(ValueError): CourseDetails.fetch_about_attribute(self.course.id, attribute_name)
def test_fetch_about_attribute(self, attribute_name): attribute_value = 'test_value' with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.course.id): CourseDetails.update_about_item(self.course, attribute_name, attribute_value, self.user.id) self.assertEqual(CourseDetails.fetch_about_attribute(self.course.id, attribute_name), attribute_value)
def _create_from_course(cls, course): """ Creates a CourseOverview object from a CourseDescriptor. Does not touch the database, simply constructs and returns an overview from the given course. Arguments: course (CourseDescriptor): any course descriptor object Returns: CourseOverview: overview extracted from the given course """ from lms.djangoapps.certificates.api import get_active_web_certificate from openedx.core.lib.courses import course_image_url log.info('Creating course overview for %s.', unicode(course.id)) # Workaround for a problem discovered in https://openedx.atlassian.net/browse/TNL-2806. # If the course has a malformed grading policy such that # course._grading_policy['GRADE_CUTOFFS'] = {}, then # course.lowest_passing_grade will raise a ValueError. # Work around this for now by defaulting to None. try: lowest_passing_grade = course.lowest_passing_grade except ValueError: lowest_passing_grade = None display_name = course.display_name start = course.start end = course.end max_student_enrollments_allowed = course.max_student_enrollments_allowed if isinstance(course.id, CCXLocator): from lms.djangoapps.ccx.utils import get_ccx_from_ccx_locator ccx = get_ccx_from_ccx_locator(course.id) display_name = ccx.display_name start = ccx.start end = ccx.due max_student_enrollments_allowed = ccx.max_student_enrollments_allowed return cls( version=cls.VERSION, id=course.id, _location=course.location, org=course.location.org, display_name=display_name, display_number_with_default=course.display_number_with_default, display_org_with_default=course.display_org_with_default, start=start, end=end, advertised_start=course.advertised_start, announcement=course.announcement, course_image_url=course_image_url(course), social_sharing_url=course.social_sharing_url, certificates_display_behavior=course.certificates_display_behavior, certificates_show_before_end=course.certificates_show_before_end, cert_html_view_enabled=course.cert_html_view_enabled, has_any_active_web_certificate=(get_active_web_certificate(course) is not None), cert_name_short=course.cert_name_short, cert_name_long=course.cert_name_long, lowest_passing_grade=lowest_passing_grade, end_of_course_survey_url=course.end_of_course_survey_url, days_early_for_beta=course.days_early_for_beta, mobile_available=course.mobile_available, visible_to_staff_only=course.visible_to_staff_only, _pre_requisite_courses_json=json.dumps(course.pre_requisite_courses), enrollment_start=course.enrollment_start, enrollment_end=course.enrollment_end, enrollment_domain=course.enrollment_domain, invitation_only=course.invitation_only, max_student_enrollments_allowed=max_student_enrollments_allowed, catalog_visibility=course.catalog_visibility, short_description=CourseDetails.fetch_about_attribute(course.id, 'short_description'), effort=CourseDetails.fetch_about_attribute(course.id, 'effort'), course_video_url=CourseDetails.fetch_video_url(course.id), self_paced=course.self_paced, )
def _create_from_course(cls, course): """ Creates a CourseOverview object from a CourseDescriptor. Does not touch the database, simply constructs and returns an overview from the given course. Arguments: course (CourseDescriptor): any course descriptor object Returns: CourseOverview: overview extracted from the given course """ from lms.djangoapps.certificates.api import get_active_web_certificate from openedx.core.lib.courses import course_image_url log.info('Creating course overview for %s.', unicode(course.id)) # Workaround for a problem discovered in https://openedx.atlassian.net/browse/TNL-2806. # If the course has a malformed grading policy such that # course._grading_policy['GRADE_CUTOFFS'] = {}, then # course.lowest_passing_grade will raise a ValueError. # Work around this for now by defaulting to None. try: lowest_passing_grade = course.lowest_passing_grade except ValueError: lowest_passing_grade = None display_name = course.display_name start = course.start end = course.end max_student_enrollments_allowed = course.max_student_enrollments_allowed if isinstance(course.id, CCXLocator): from lms.djangoapps.ccx.utils import get_ccx_from_ccx_locator ccx = get_ccx_from_ccx_locator(course.id) display_name = ccx.display_name start = ccx.start end = ccx.due max_student_enrollments_allowed = ccx.max_student_enrollments_allowed return cls( version=cls.VERSION, id=course.id, _location=course.location, org=course.location.org, display_name=display_name, display_number_with_default=course.display_number_with_default, display_org_with_default=course.display_org_with_default, start=start, end=end, advertised_start=course.advertised_start, announcement=course.announcement, course_image_url=course_image_url(course), social_sharing_url=course.social_sharing_url, certificates_display_behavior=course.certificates_display_behavior, certificates_show_before_end=course.certificates_show_before_end, cert_html_view_enabled=course.cert_html_view_enabled, has_any_active_web_certificate=(get_active_web_certificate(course) is not None), cert_name_short=course.cert_name_short, cert_name_long=course.cert_name_long, lowest_passing_grade=lowest_passing_grade, end_of_course_survey_url=course.end_of_course_survey_url, days_early_for_beta=course.days_early_for_beta, mobile_available=course.mobile_available, visible_to_staff_only=course.visible_to_staff_only, _pre_requisite_courses_json=json.dumps( course.pre_requisite_courses), enrollment_start=course.enrollment_start, enrollment_end=course.enrollment_end, enrollment_domain=course.enrollment_domain, invitation_only=course.invitation_only, max_student_enrollments_allowed=max_student_enrollments_allowed, catalog_visibility=course.catalog_visibility, short_description=CourseDetails.fetch_about_attribute( course.id, 'short_description'), effort=CourseDetails.fetch_about_attribute(course.id, 'effort'), course_video_url=CourseDetails.fetch_video_url(course.id), self_paced=course.self_paced, )
def _create_or_update(cls, course): # lint-amnesty, pylint: disable=too-many-statements """ Creates or updates a CourseOverview object from a CourseBlock. Does not touch the database, simply constructs and returns an overview from the given course. Arguments: course (CourseBlock): any course descriptor object Returns: CourseOverview: created or updated overview extracted from the given course """ from lms.djangoapps.certificates.api import get_active_web_certificate from openedx.core.lib.courses import course_image_url # Workaround for a problem discovered in https://openedx.atlassian.net/browse/TNL-2806. # If the course has a malformed grading policy such that # course._grading_policy['GRADE_CUTOFFS'] = {}, then # course.lowest_passing_grade will raise a ValueError. # Work around this for now by defaulting to None. try: lowest_passing_grade = course.lowest_passing_grade except ValueError: lowest_passing_grade = None display_name = course.display_name start = course.start end = course.end max_student_enrollments_allowed = course.max_student_enrollments_allowed if isinstance(course.id, CCXLocator): from lms.djangoapps.ccx.utils import get_ccx_from_ccx_locator ccx = get_ccx_from_ccx_locator(course.id) display_name = ccx.display_name start = ccx.start end = ccx.due max_student_enrollments_allowed = ccx.max_student_enrollments_allowed course_overview = cls.objects.filter(id=course.id) if course_overview.exists(): log.info('Updating course overview for %s.', str(course.id)) course_overview = course_overview.first() # MySQL ignores casing, but CourseKey doesn't. To prevent multiple # courses with different cased keys from overriding each other, we'll # check for equality here in python. if course_overview.id != course.id: raise CourseOverviewCaseMismatchException( course_overview.id, course.id) else: log.info('Creating course overview for %s.', str(course.id)) course_overview = cls() course_overview.version = cls.VERSION course_overview.id = course.id course_overview._location = course.location # lint-amnesty, pylint: disable=protected-access course_overview.org = course.location.org course_overview.display_name = display_name course_overview.display_number_with_default = course.display_number_with_default course_overview.display_org_with_default = course.display_org_with_default course_overview.start = start course_overview.end = end course_overview.advertised_start = course.advertised_start course_overview.announcement = course.announcement course_overview.banner_image_url = course_image_url( course, 'banner_image') course_overview.course_image_url = course_image_url(course) course_overview.social_sharing_url = course.social_sharing_url updated_available_date, updated_display_behavior = CourseDetails.validate_certificate_settings( course.certificate_available_date, course.certificates_display_behavior) course_overview.certificates_display_behavior = updated_display_behavior course_overview.certificate_available_date = updated_available_date course_overview.certificates_show_before_end = course.certificates_show_before_end course_overview.cert_html_view_enabled = course.cert_html_view_enabled course_overview.has_any_active_web_certificate = ( get_active_web_certificate(course) is not None) course_overview.cert_name_short = course.cert_name_short course_overview.cert_name_long = course.cert_name_long course_overview.lowest_passing_grade = lowest_passing_grade course_overview.end_of_course_survey_url = course.end_of_course_survey_url course_overview.days_early_for_beta = course.days_early_for_beta course_overview.mobile_available = course.mobile_available course_overview.visible_to_staff_only = course.visible_to_staff_only course_overview._pre_requisite_courses_json = json.dumps( course.pre_requisite_courses) # lint-amnesty, pylint: disable=protected-access course_overview.enrollment_start = course.enrollment_start course_overview.enrollment_end = course.enrollment_end course_overview.enrollment_domain = course.enrollment_domain course_overview.invitation_only = course.invitation_only course_overview.max_student_enrollments_allowed = max_student_enrollments_allowed course_overview.catalog_visibility = course.catalog_visibility course_overview.short_description = CourseDetails.fetch_about_attribute( course.id, 'short_description') course_overview.effort = CourseDetails.fetch_about_attribute( course.id, 'effort') course_overview.course_video_url = CourseDetails.fetch_video_url( course.id) course_overview.self_paced = course.self_paced course_overview.has_highlights = cls._get_course_has_highlights(course) course_overview.enable_proctored_exams = course.enable_proctored_exams course_overview.proctoring_provider = course.proctoring_provider course_overview.proctoring_escalation_email = course.proctoring_escalation_email course_overview.allow_proctoring_opt_out = course.allow_proctoring_opt_out course_overview.entrance_exam_enabled = course.entrance_exam_enabled # entrance_exam_id defaults to None in the course object, but '' is more reasonable for a string field course_overview.entrance_exam_id = course.entrance_exam_id or '' # Despite it being a float, the course object defaults to an int. So we will detect that case and update # it to be a float like everything else. if isinstance(course.entrance_exam_minimum_score_pct, int): course_overview.entrance_exam_minimum_score_pct = course.entrance_exam_minimum_score_pct / 100 else: course_overview.entrance_exam_minimum_score_pct = course.entrance_exam_minimum_score_pct if not CatalogIntegration.is_enabled(): course_overview.language = course.language return course_overview
def test_validate_certificate_settings_v2(self, stored_date, stored_behavior, expected_date, expected_behavior): assert CourseDetails.validate_certificate_settings( stored_date, stored_behavior) == (expected_date, expected_behavior)
def recommend(self, bot, chat_id, telegram_user): telegram_id = telegram_user.telegram_id if not LearningPredictionForUser.objects.filter(telegram_user=telegram_user): bot.sendMessage( chat_id=chat_id, text="It seems like I see you for the first time," " please answer a few questions, so I'll be know more about you", ) prediction.get_test_courses(telegram_id) test_courses = LearningPredictionForUser.objects.get(telegram_user=telegram_user).get_list() if len(test_courses) > 0: course_id = MatrixEdxCoursesId.objects.filter(course_index=test_courses[0]).first().course_key course_key = CourseKey.from_string(course_id) reply_markup = InlineKeyboardMarkup( [ [ InlineKeyboardButton( text="I like it!", callback_data=json.dumps({"method": "learning", "kwargs": {}}) ) ], [ InlineKeyboardButton( text="Hmmm. I don't like at all!", callback_data=json.dumps({"method": "learning", "kwargs": {"is_positive": False}}), ) ], ] ) else: predicted_course_id = prediction.prediction(telegram_id) if predicted_course_id == -1: bot.sendMessage(chat_id=chat_id, text="It seems like you have enrolled to all courses we have for now") return predicted_course_key = ( MatrixEdxCoursesId.objects.filter(course_index=predicted_course_id).first().course_key ) bot.sendMessage(chat_id=chat_id, text="Now I'm going to recommend you some great courses") course_key = CourseKey.from_string(predicted_course_key) course_for_user = PredictionForUser.objects.get_or_create(telegram_user=telegram_user)[0] course_for_user.prediction_course = predicted_course_key course_for_user.save() reply_markup = InlineKeyboardMarkup( [ [ InlineKeyboardButton( text="I like it and I want to enroll", callback_data=json.dumps({"method": "enroll", "kwargs": {}}), ) ], [ InlineKeyboardButton( text="I like it but will eroll another time", callback_data=json.dumps({"method": "not_enroll", "kwargs": {}}), ) ], [ InlineKeyboardButton( text="What the shit is this (I don't like it)", callback_data=json.dumps({"method": "wrong_predict", "kwargs": {}}), ) ], ] ) course_description = CourseDetails.fetch_about_attribute(course_key, "overview") course_title = modulestore().get_course(course_key).display_name_with_default bot.sendMessage(chat_id=chat_id, text="*%s*" % course_title, parse_mode=telegram.ParseMode.MARKDOWN) bot.sendMessage(chat_id=chat_id, text=truncate_course_info(course_description), reply_markup=reply_markup)
def test_update_and_fetch(self): SelfPacedConfiguration(enabled=True).save() jsondetails = CourseDetails.fetch(self.course.id) jsondetails.syllabus = "<a href='foo'>bar</a>" # encode - decode to convert date fields and other data which changes form with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.course.id): self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).syllabus, jsondetails.syllabus, "After set syllabus" ) jsondetails.short_description = "Short Description" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).short_description, jsondetails.short_description, "After set short_description" ) jsondetails.overview = "Overview" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).overview, jsondetails.overview, "After set overview" ) jsondetails.intro_video = "intro_video" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).intro_video, jsondetails.intro_video, "After set intro_video" ) jsondetails.effort = "effort" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).effort, jsondetails.effort, "After set effort" ) jsondetails.self_paced = True self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).self_paced, jsondetails.self_paced ) jsondetails.start_date = datetime.datetime(2010, 10, 1, 0, tzinfo=UTC()) self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).start_date, jsondetails.start_date ) jsondetails.end_date = datetime.datetime(2011, 10, 1, 0, tzinfo=UTC()) self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).end_date, jsondetails.end_date ) jsondetails.certificate_available_date = datetime.datetime(2010, 10, 1, 0, tzinfo=UTC()) self.assertEqual( CourseDetails.update_from_json( self.course.id, jsondetails.__dict__, self.user ).certificate_available_date, jsondetails.certificate_available_date ) jsondetails.course_image_name = "an_image.jpg" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).course_image_name, jsondetails.course_image_name ) jsondetails.banner_image_name = "an_image.jpg" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).banner_image_name, jsondetails.banner_image_name ) jsondetails.video_thumbnail_image_name = "an_image.jpg" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).video_thumbnail_image_name, jsondetails.video_thumbnail_image_name ) jsondetails.language = "hr" self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).language, jsondetails.language ) jsondetails.learning_info = ["test", "test"] self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).learning_info, jsondetails.learning_info ) jsondetails.instructor_info = { "instructors": [ { "name": "test", "title": "test", "organization": "test", "image": "test", "bio": "test" } ] } self.assertEqual( CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).instructor_info, jsondetails.instructor_info )
def check_course_overview_against_course(self, course): """ Compares a CourseOverview object against its corresponding CourseDescriptor object. Specifically, given a course, test that data within the following three objects match each other: - the CourseDescriptor itself - a CourseOverview that was newly constructed from _create_or_update - a CourseOverview that was loaded from the MySQL database Arguments: course (CourseDescriptor): the course to be checked. """ def get_seconds_since_epoch(date_time): """ Returns the number of seconds between the Unix Epoch and the given datetime. If the given datetime is None, return None. Arguments: date_time (datetime): the datetime in question. """ if date_time is None: return None epoch = datetime.datetime.utcfromtimestamp(0).replace(tzinfo=pytz.utc) return math.floor((date_time - epoch).total_seconds()) # Load the CourseOverview from the cache twice. The first load will be a cache miss (because the cache # is empty) so the course will be newly created with CourseOverview._create_or_update. The second # load will be a cache hit, so the course will be loaded from the cache. course_overview_cache_miss = CourseOverview.get_from_id(course.id) course_overview_cache_hit = CourseOverview.get_from_id(course.id) # Test if value of these attributes match between the three objects fields_to_test = [ 'id', 'display_name', 'display_number_with_default', 'display_org_with_default', 'advertised_start', 'social_sharing_url', 'certificates_display_behavior', 'certificates_show_before_end', 'cert_name_short', 'cert_name_long', 'lowest_passing_grade', 'end_of_course_survey_url', 'mobile_available', 'visible_to_staff_only', 'location', 'number', 'url_name', 'display_name_with_default', 'display_name_with_default_escaped', 'start_date_is_still_default', 'pre_requisite_courses', 'enrollment_domain', 'invitation_only', 'max_student_enrollments_allowed', 'catalog_visibility', ] for attribute_name in fields_to_test: course_value = getattr(course, attribute_name) cache_miss_value = getattr(course_overview_cache_miss, attribute_name) cache_hit_value = getattr(course_overview_cache_hit, attribute_name) self.assertEqual(course_value, cache_miss_value) self.assertEqual(cache_miss_value, cache_hit_value) # Test if return values for all methods are equal between the three objects methods_to_test = [ ('clean_id', ()), ('clean_id', ('#',)), ('has_ended', ()), ('has_started', ()), ('may_certify', ()), ] for method_name, method_args in methods_to_test: course_value = getattr(course, method_name)(*method_args) cache_miss_value = getattr(course_overview_cache_miss, method_name)(*method_args) cache_hit_value = getattr(course_overview_cache_hit, method_name)(*method_args) self.assertEqual(course_value, cache_miss_value) self.assertEqual(cache_miss_value, cache_hit_value) # Other values to test # Note: we test the time-related attributes here instead of in # fields_to_test, because we run into trouble while testing datetimes # for equality. When writing and reading dates from databases, the # resulting values are often off by fractions of a second. So, as a # workaround, we simply test if the start and end times are the same # number of seconds from the Unix epoch. time_field_accessor = lambda object, field_name: get_seconds_since_epoch(getattr(object, field_name)) # The course about fields are accessed through the CourseDetail # class for the course module, and stored as attributes on the # CourseOverview objects. course_about_accessor = lambda object, field_name: CourseDetails.fetch_about_attribute(object.id, field_name) others_to_test = [ ('start', time_field_accessor, time_field_accessor), ('end', time_field_accessor, time_field_accessor), ('enrollment_start', time_field_accessor, time_field_accessor), ('enrollment_end', time_field_accessor, time_field_accessor), ('announcement', time_field_accessor, time_field_accessor), ('short_description', course_about_accessor, getattr), ('effort', course_about_accessor, getattr), ( 'video', lambda c, __: CourseDetails.fetch_video_url(c.id), lambda c, __: c.course_video_url, ), ( 'course_image_url', lambda c, __: course_image_url(c), getattr, ), ( 'has_any_active_web_certificate', lambda c, field_name: get_active_web_certificate(c) is not None, getattr, ), ] for attribute_name, course_accessor, course_overview_accessor in others_to_test: course_value = course_accessor(course, attribute_name) cache_miss_value = course_overview_accessor(course_overview_cache_miss, attribute_name) cache_hit_value = course_overview_accessor(course_overview_cache_hit, attribute_name) self.assertEqual(course_value, cache_miss_value) self.assertEqual(cache_miss_value, cache_hit_value) # test tabs for both cached miss and cached hit courses for course_overview in [course_overview_cache_miss, course_overview_cache_hit]: course_overview_tabs = course_overview.tabs.all() course_resp_tabs = {tab.tab_id for tab in course_overview_tabs} self.assertEqual(self.COURSE_OVERVIEW_TABS, course_resp_tabs)
def get_course_details(course_id): course_descriptor = get_course_by_id(course_id) course = CourseDetails.populate(course_descriptor) return course
def get_student_info(self, email): if User.objects.filter(email=email).exists(): student = User.objects.get(email=email) # List of courses user is enrolled to org_filter_out_set = '' course_org_filter = '' course_enrollments = list( get_course_enrollments(student, course_org_filter, org_filter_out_set)) #last login data_email last_login_brut = str(student.last_login) last_login = last_login_brut.split('.') #Check if microsite admin if MicrositeAdminManager.objects.filter(user=student).exists(): check_admin_microsite = True microsite_key = MicrositeAdminManager.objects.get( user=student).microsite_id microsite_admin_org = Microsite.objects.get( pk=microsite_key).key else: check_admin_microsite = False #Check wich course invited first if CourseEnrollment.objects.filter(user=student).exists(): course_id = CourseEnrollment.objects.filter( user=student).order_by('-created')[0].course_id user_org = str(course_id).split('+')[0].replace( "course-v1:", "") else: user_org = "No organization found for user" #Course counters compteur_progress = 0 compteur_finish = 0 compteur_start = 0 compteur_certified = 0 progress_courses = [] finish_courses = [] start_courses = [] certified_courses = [] _now = int(datetime.datetime.now().strftime("%s")) if len(course_enrollments) > 0: #For each course user is enrolled to for dashboard_index, enrollment in enumerate( course_enrollments): course_id = enrollment.course_overview.id user_id = student.id course_tma = get_course_by_id(enrollment.course_id) try: course_grade_factory = CourseGradeFactory().create( student, course_tma) passed = course_grade_factory.passed percent = course_grade_factory.percent except: passed = False percent = 0 course_progression = get_overall_progress( user_id, course_id) try: _end = int( enrollment.course_overview.end.strftime("%s")) except: _end = 0 _progress = True if _end > 0 and _end < _now: _progress = False #storing student results for this class q = {} q['passed'] = passed q['percent'] = float(int(percent * 1000) / 10) q['course_id'] = str(enrollment.course_id) q['duration'] = CourseDetails.fetch( enrollment.course_id).effort q['required'] = course_tma.is_required_atp q['content_data'] = course_tma.content_data q['category'] = course_tma.categ q['display_name_with_default'] = enrollment.course_overview.display_name_with_default q['course_progression'] = course_progression if passed == True: compteur_certified += 1 certified_courses.append(q) if course_progression > 0 and course_progression < 100 and passed == False and _progress == True: compteur_progress += 1 progress_courses.append(q) elif course_progression == 100 or passed or _progress == False: compteur_finish += 1 finish_courses.append(q) elif course_progression == 0 and _progress == True: compteur_start += 1 start_courses.append(q) #Candidate status if student.is_staff: status = "Staff" elif check_admin_microsite: status = "Admin Microsite" else: status = "Student" context = { 'student_id': student.id, 'status': status, 'student_mail': student.email, 'student_name': student.first_name + " " + student.last_name, 'progress_courses': compteur_progress, #'progress_courses': progress_courses, 'finished_courses': compteur_finish, #'finish_courses': finish_courses, 'started_courses': compteur_start, #'start_courses':start_courses, 'certified_courses': compteur_certified, #'certified_course' : certified_courses, 'user_org': user_org, 'last login': last_login[0] } if check_admin_microsite: context['microsite_admin_org'] = microsite_admin_org else: if UserPreprofile.objects.filter(email=email).exists(): user = UserPreprofile.objects.get(email=email) if CourseEnrollmentAllowed.objects.filter( email=email).exists(): profile = CourseEnrollmentAllowed.objects.filter( email=email).order_by('-created') course_id = profile[0].course_id user_org = str(course_id).split('+')[0].replace( "course-v1:", "") else: user_org = "No organization found for user" context = { 'student_mail': email, 'student_name': user.first_name + " " + user.last_name, 'status': "User preregistered on platform", 'user_org': user_org } else: context = { 'student_mail': email, 'status': "Unknown user", } return context
def course_info_to_ccxcon(course_key): """ Function that gathers informations about the course and makes a post request to a CCXCon with the data. Args: course_key (CourseLocator): the master course key """ try: course = get_course_by_id(course_key) except Http404: log.error('Master Course with key "%s" not found', unicode(course_key)) return if not course.enable_ccx: log.debug('ccx not enabled for course key "%s"', unicode(course_key)) return if not course.ccx_connector: log.debug('ccx connector not defined for course key "%s"', unicode(course_key)) return if not is_valid_url(course.ccx_connector): log.error( 'ccx connector URL "%s" for course key "%s" is not a valid URL.', course.ccx_connector, unicode(course_key) ) return # get the oauth credential for this URL try: ccxcon = CCXCon.objects.get(url=course.ccx_connector) except CCXCon.DoesNotExist: log.error('ccx connector Oauth credentials not configured for URL "%s".', course.ccx_connector) return # get an oauth client with a valid token oauth_ccxcon = get_oauth_client( server_token_url=urlparse.urljoin(course.ccx_connector, CCXCON_TOKEN_URL), client_id=ccxcon.oauth_client_id, client_secret=ccxcon.oauth_client_secret ) # get the entire list of instructors course_instructors = CourseInstructorRole(course.id).users_with_role() # get anonymous ids for each of them course_instructors_ids = [anonymous_id_for_user(user, course_key) for user in course_instructors] # extract the course details course_details = CourseDetails.fetch(course_key) payload = { 'course_id': unicode(course_key), 'title': course.display_name, 'author_name': None, 'overview': course_details.overview, 'description': course_details.short_description, 'image_url': course_details.course_image_asset_path, 'instructors': course_instructors_ids } headers = {'content-type': 'application/json'} # make the POST request add_course_url = urlparse.urljoin(course.ccx_connector, CCXCON_COURSEXS_URL) resp = oauth_ccxcon.post( url=add_course_url, json=payload, headers=headers, timeout=CCXCON_REQUEST_TIMEOUT ) if resp.status_code >= 500: raise CCXConnServerError('Server returned error Status: %s, Content: %s', resp.status_code, resp.content) if resp.status_code >= 400: log.error("Error creating course on ccxcon. Status: %s, Content: %s", resp.status_code, resp.content) # this API performs a POST request both for POST and PATCH, but the POST returns 201 and the PATCH returns 200 elif resp.status_code != HTTP_200_OK and resp.status_code != HTTP_201_CREATED: log.error('Server returned unexpected status code %s', resp.status_code) else: log.debug('Request successful. Status: %s, Content: %s', resp.status_code, resp.content)
def test_update_and_fetch(self): jsondetails = CourseDetails.fetch(self.course.id) jsondetails.syllabus = "<a href='foo'>bar</a>" # encode - decode to convert date fields and other data which changes form with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.course.id): assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).syllabus ==\ jsondetails.syllabus, 'After set syllabus' jsondetails.short_description = "Short Description" assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).short_description ==\ jsondetails.short_description, 'After set short_description' jsondetails.overview = "Overview" assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).overview ==\ jsondetails.overview, 'After set overview' jsondetails.intro_video = "intro_video" assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).intro_video ==\ jsondetails.intro_video, 'After set intro_video' jsondetails.about_sidebar_html = "About Sidebar HTML" assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user)\ .about_sidebar_html == jsondetails.about_sidebar_html, 'After set about_sidebar_html' jsondetails.effort = "effort" assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).effort ==\ jsondetails.effort, 'After set effort' jsondetails.self_paced = True assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).self_paced ==\ jsondetails.self_paced jsondetails.start_date = datetime.datetime(2010, 10, 1, 0, tzinfo=UTC) assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).start_date ==\ jsondetails.start_date jsondetails.end_date = datetime.datetime(2011, 10, 1, 0, tzinfo=UTC) assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).end_date ==\ jsondetails.end_date jsondetails.certificate_available_date = datetime.datetime( 2010, 10, 1, 0, tzinfo=UTC) assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user)\ .certificate_available_date == jsondetails.certificate_available_date jsondetails.course_image_name = "an_image.jpg" assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).course_image_name ==\ jsondetails.course_image_name jsondetails.banner_image_name = "an_image.jpg" assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).banner_image_name ==\ jsondetails.banner_image_name jsondetails.video_thumbnail_image_name = "an_image.jpg" assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user)\ .video_thumbnail_image_name == jsondetails.video_thumbnail_image_name jsondetails.language = "hr" assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).language ==\ jsondetails.language jsondetails.learning_info = ["test", "test"] assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).learning_info ==\ jsondetails.learning_info jsondetails.instructor_info = { "instructors": [{ "name": "test", "title": "test", "organization": "test", "image": "test", "bio": "test" }] } assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).instructor_info ==\ jsondetails.instructor_info
def test_fetch_effort(self): effort_value = 'test_hours_of_effort' with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.course.id): CourseDetails.update_about_item(self.course, 'effort', effort_value, self.user.id) self.assertEqual(CourseDetails.fetch_effort(self.course.id), effort_value)