def test_handle(self, store_mock, get_mock, del_mock, call_mock): pr.Command().handle(*self.args, **self.options_get) get_mock.assert_called_once_with( *[CourseLocator.from_string(arg) for arg in self.args]) pr.Command().handle(*self.args, **self.options_create) call_mock.assert_called_once_with('create_report_task', *['create'], **{'course_id': self.args[0]}) pr.Command().handle(*self.args, **self.options_delete) del_mock.assert_called_once_with( *[CourseLocator.from_string(arg) for arg in self.args]) msg = '^"course_id" is not specified$' with self.assertRaisesRegexp(CommandError, msg): pr.Command().handle(*[], **self.options_get) msg = '^Cannot specify "-c" option and "-d" option at the same time.$' with self.assertRaisesRegexp(CommandError, msg): pr.Command().handle(*self.args, **self.options_error) msg = '^CSV not found.$' with self.assertRaisesRegexp(CommandError, msg): get_mock.side_effect = NotFoundError() pr.Command().handle(*self.args, **self.options_get)
def survey_ajax(request): """Ajax call to submit a survey.""" MAX_CHARACTER_LENGTH = 1000 course_id = request.POST.get('course_id') unit_id = request.POST.get('unit_id') survey_name = request.POST.get('survey_name') survey_answer = request.POST.get('survey_answer') if not course_id or not unit_id: log.warning("Illegal parameter. course_id=%s, unit_id=%s" % (course_id, unit_id)) raise Http404 if not survey_name: log.warning("Illegal parameter. survey_name=%s" % survey_name) raise Http404 if not survey_answer: log.warning("Illegal parameter. survey_answer=%s" % survey_answer) raise Http404 try: obj = json.loads(survey_answer) except: log.warning("Illegal parameter. survey_answer=%s" % survey_answer) raise Http404 for k, v in obj.iteritems(): if len(v) > MAX_CHARACTER_LENGTH: log.warning("%s cannot be more than %d characters long." % (k, MAX_CHARACTER_LENGTH)) raise Http404 try: submission = SurveySubmission.objects.filter( course_id=CourseLocator.from_string(course_id), unit_id=unit_id, user=request.user ).order_by('created')[0:1].get() except SurveySubmission.DoesNotExist: pass else: return JsonResponse({ 'success': False, 'survey_answer': submission.get_survey_answer(), }) submission = SurveySubmission( course_id=CourseLocator.from_string(course_id), unit_id=unit_id, user=request.user, survey_name=survey_name, survey_answer=survey_answer, ) submission.save() return JsonResponse({'success': True})
def setUp(self): self.user = "******" self.course_id = CourseLocator.from_string("org/num/run") self.debug = False self.noop = False self.file_prefix = "" self.exclude = None self.student = UserFactory.create() self.cert = GeneratedCertificateFactory.build( user=self.student, status="downloadable") self.summary = [ {"display_name": "section_name", "sections": [{ "display_name": "subsec_name", "format": "HW", "section_total": [10, 10], "scores": [ (10, 10, True, "unit_name")]}]}] self.grade = {"grade": "Pass", "percent": 1} self.invalid_grade = {"grade": None, "percent": 1} self.total = {'users': 0, 'pass': 0, 'notpass': 0} self.total_with_grade = { 'users': 0, 'pass': 0, 'notpass': 0, 'Pass': 0} self.total_items = { 'users': 3, 'pass': 2, 'notpass': 1, 'A': 1, 'B': 1}
def get_structure_history_graph(course): course_key = CourseLocator.from_string(course) try: history_graph = API.get_structure_history_graph(course_key) except API.CourseNotFound: abort(404) return Response(dumps(history_graph), mimetype='application/json')
def get_course_metadata(course): course_key = CourseLocator.from_string(course) try: course_metadata = API.get_course_metadata(course_key) except API.CourseNotFound: abort(404) return Response(dumps(course_metadata), mimetype='application/json')
def handle(self, *args, **options): create_report = options['create'] delete_report = options['delete'] if len(args) != 1: raise CommandError('"course_id" is not specified') elif create_report and delete_report: raise CommandError( 'Cannot specify "-c" option and "-d" option at the same time.') course_id = args[0] try: course_id = CourseLocator.from_string(course_id) except InvalidKeyError: raise CommandError("'{}' is an invalid course_id".format(course_id)) if not modulestore().get_course(course_id): raise CommandError("The specified course does not exist.") if delete_report: try: delete_pgreport_csv(course_id) except NotFoundError: raise CommandError("CSV not found.") call_command('create_report_task', *['clear_cache'], **{'course_id': course_id.to_deprecated_string()}) elif create_report: call_command('create_report_task', *['create'], **{'course_id': course_id.to_deprecated_string()}) else: try: get_pgreport_csv(course_id) except NotFoundError: raise CommandError("CSV not found.")
def test_gitlog_pagination_out_of_range_invalid(self): """ Make sure the pagination behaves properly when the requested page is out of range. """ self._setstaff_login() mongoengine.connect(TEST_MONGODB_LOG['db']) for _ in range(15): CourseImportLog( course_id=CourseLocator.from_string("test/test/test"), location="location", import_log="import_log", git_log="git_log", repo_dir="repo_dir", created=datetime.now()).save() for page, expected in [(-1, 1), (1, 1), (2, 2), (30, 2), ('abc', 1)]: response = self.client.get('{}?page={}'.format( reverse('gitlogs'), page)) self.assertContains(response, u'Page {} of 2'.format(expected)) CourseImportLog.objects.delete()
def handle(self, *args, **options): '''Handle management command request''' if len(args) != 3: raise CommandError('Usage is set_course_end {0}'.format(self.args)) try: end_date = pytz.timezone('Europe/Moscow').localize( parse_date(args[1], dayfirst=False, yearfirst=True)).astimezone(pytz.utc) except: raise CommandError('Could not parse date "{0}"'.format(args[1])) try: user = User.objects.get(email=args[2]) except: raise CommandError('Could not find user with email "{0}"'.format( args[2])) locator = CourseLocator.from_string(args[0]) coursedata = CourseDetails.fetch(locator) old_end_date = coursedata.end_date coursedata.end_date = end_date print "Changing course {0} end date from {1} to {2}".format( locator, old_end_date, end_date) coursedata.update_from_json(locator, coursedata.__dict__, user)
def test_from_course_locator(self): course_locator = CourseLocator.from_string( self.course_key_string) course_key = as_course_key(course_locator) assert isinstance(course_key, CourseKey) assert course_key == self.course_key assert course_key is course_locator
def recalculate_subsection_grade_handler(sender, **kwargs): # pylint: disable=unused-argument """ Consume the SCORE_CHANGED signal and trigger an update. This method expects that the kwargs dictionary will contain the following entries (See the definition of SCORE_CHANGED): - points_possible: Maximum score available for the exercise - points_earned: Score obtained by the user - user: User object - course_id: Unicode string representing the course - usage_id: Unicode string indicating the courseware instance """ student = kwargs['user'] course_key = CourseLocator.from_string(kwargs['course_id']) if not PersistentGradesEnabledFlag.feature_enabled(course_key): return scored_block_usage_key = UsageKey.from_string(kwargs['usage_id']).replace(course_key=course_key) collected_block_structure = get_course_in_cache(course_key) course = get_course_by_id(course_key, depth=0) subsections_to_update = collected_block_structure.get_transformer_block_field( scored_block_usage_key, GradesTransformer, 'subsections', set() ) for subsection_usage_key in subsections_to_update: transformed_subsection_structure = get_course_blocks( student, subsection_usage_key, collected_block_structure=collected_block_structure, ) SubsectionGradeFactory(student).update(subsection_usage_key, transformed_subsection_structure, course)
def get(self, request, course_id): """Implements the GET method as described in the class docstring.""" course_key = CourseLocator.from_string(course_id) course = get_course_with_access(request.user, 'load_forum', course_key) if not any([isinstance(tab, DiscussionTab) for tab in course.tabs]): raise Http404 return Response(get_course_topics(course, request.user))
def vote_for_thread(request, course_id, thread_id, value): """ given a course id and thread id vote for this thread ajax only """ thread = cc.Thread.find(thread_id) result = _vote_or_unvote(request, course_id, thread, value) course_key = CourseLocator.from_string(course_id) # Feature Flag to check that notifications are enabled or not. if value == 'up' and settings.FEATURES.get("ENABLE_NOTIFICATIONS", False): action_user_id = request.user.id original_poster_id = int(thread.user_id) # refetch the thread to get updated count metrics thread = cc.Thread.find(thread_id) # we have to only send the notifications when # the user voting the thread is not # the same user who created the thread if action_user_id != original_poster_id: _send_discussion_notification( 'open-edx.lms.discussions.post-upvoted', str(course_key), thread, request.user, recipient_user_id=original_poster_id, extra_payload={ 'num_upvotes': thread.votes['up_count'], }) return result
def follow_thread(request, course_id, thread_id): course_key = CourseLocator.from_string(course_id) user = cc.User.from_django_user(request.user) thread = cc.Thread.find(thread_id) user.follow(thread) thread_followed.send(sender=None, user=request.user, post=thread) # Feature Flag to check that notifications are enabled or not. if settings.FEATURES.get("ENABLE_NOTIFICATIONS", False): # only send notifications when the user # who is following the thread is not the same # who created the thread action_user_id = request.user.id original_poster_id = int(thread.user_id) # get number of followers try: num_followers = thread.get_num_followers() if original_poster_id != action_user_id: _send_discussion_notification( 'open-edx.lms.discussions.thread-followed', str(course_key), thread, request.user, recipient_user_id=original_poster_id, extra_payload={ 'num_followers': num_followers, }) except Exception as ex: # sending notifications is not critical, # so log error and continue log.exception(ex) return JsonResponse({})
def handle(self, *args, **options): '''Handle management command request''' if len(args) != 3: raise CommandError('Usage is expire_all_blocks {0}'.format( self.args)) try: end_date = pytz.timezone('Europe/Moscow').localize( parse_date(args[1], dayfirst=False, yearfirst=True)).astimezone(pytz.utc) except: raise CommandError('Could not parse date "{0}"'.format(args[1])) try: user = User.objects.get(email=args[2]) except: raise CommandError('Could not find user with email "{0}"'.format( args[2])) locator = CourseLocator.from_string(args[0]) print "Altering course {0} blocks due date to {1}".format( locator, end_date) for block in modulestore().get_items( locator, qualifiers={'category': 'sequential'}, revision=ModuleStoreEnum.RevisionOption.published_only): block.due = end_date modulestore().update_item(block, user.id)
def vote_for_comment(request, course_id, comment_id, value): """ Given a course_id and comment_id, vote for this response. AJAX only. """ comment = cc.Comment.find(comment_id) result = _vote_or_unvote(request, course_id, comment, value) comment_voted.send(sender=None, user=request.user, post=comment) course_key = CourseLocator.from_string(course_id) # Feature Flag to check that notifications are enabled or not. if value == 'up' and settings.FEATURES.get("ENABLE_NOTIFICATIONS", False): action_user_id = request.user.id original_poster_id = int(comment.user_id) thread = cc.Thread.find(comment.thread_id) # refetch the comment, so we have the updated counters comment = cc.Comment.find(comment_id) # we have to only send the notifications when # the user voting comment the comment is not # the same user who created the comment if action_user_id != original_poster_id: _send_discussion_notification( 'open-edx.lms.discussions.comment-upvoted', str(course_key), thread, request.user, recipient_user_id=original_poster_id, extra_payload={ 'num_upvotes': comment.votes['up_count'], }) return result
def test_gitlog_pagination_out_of_range_invalid(self): """ Make sure the pagination behaves properly when the requested page is out of range. """ self._setstaff_login() mongoengine.connect(TEST_MONGODB_LOG['db']) for _ in xrange(15): CourseImportLog( course_id=CourseLocator.from_string("test/test/test"), location="location", import_log="import_log", git_log="git_log", repo_dir="repo_dir", created=datetime.now() ).save() for page, expected in [(-1, 1), (1, 1), (2, 2), (30, 2), ('abc', 1)]: response = self.client.get( '{}?page={}'.format( reverse('gitlogs'), page ) ) self.assertIn( u'Page {} of 2'.format(expected), response.content.decode(response.charset) ) CourseImportLog.objects.delete()
def handle(self, *args, **options): if len(args) != 1: raise CommandError('Must called with arguments: {}'.format( self.args)) try: user = get_user_by_username_or_email(args[0]) except: raise CommandError('No user exists [ {} ]'.format(args[0])) course_id = options['course_id'] reactivate = options['reactivate'] if course_id: try: course_id = CourseLocator.from_string(course_id) except InvalidKeyError: raise CommandError( "'{}' is an invalid course_id".format(course_id)) if not modulestore().get_course(course_id): raise CommandError("The specified course does not exist.") self.change_optout_state(user, course_id, reactivate) else: course_enrollments = CourseEnrollment.enrollments_for_user(user) for enrollment in course_enrollments: course_id = enrollment.course_id self.change_optout_state(user, course_id, reactivate)
def handle(self, *args, **options): create_report = options['create'] delete_report = options['delete'] if len(args) != 1: raise CommandError('"course_id" is not specified') elif create_report and delete_report: raise CommandError( 'Cannot specify "-c" option and "-d" option at the same time.') course_id = args[0] try: course_id = CourseLocator.from_string(course_id) except InvalidKeyError: raise CommandError( "'{}' is an invalid course_id".format(course_id)) if not modulestore().get_course(course_id): raise CommandError("The specified course does not exist.") if delete_report: try: delete_pgreport_csv(course_id) except NotFoundError: raise CommandError("CSV not found.") call_command('create_report_task', *['clear_cache'], **{'course_id': course_id.to_deprecated_string()}) elif create_report: call_command('create_report_task', *['create'], **{'course_id': course_id.to_deprecated_string()}) else: try: get_pgreport_csv(course_id) except NotFoundError: raise CommandError("CSV not found.")
def recalculate_subsection_grade_v2(**kwargs): """ Updates a saved subsection grade. Arguments: user_id (int): id of applicable User object course_id (string): identifying the course usage_id (string): identifying the course block only_if_higher (boolean): indicating whether grades should be updated only if the new raw_earned is higher than the previous value. expected_modified_time (serialized timestamp): indicates when the task was queued so that we can verify the underlying data update. score_deleted (boolean): indicating whether the grade change is a result of the problem's score being deleted. event_transaction_id(string): uuid identifying the current event transaction. event_transaction_type(string): human-readable type of the event at the root of the current event transaction. """ try: course_key = CourseLocator.from_string(kwargs['course_id']) if not PersistentGradesEnabledFlag.feature_enabled(course_key): return score_deleted = kwargs['score_deleted'] scored_block_usage_key = UsageKey.from_string(kwargs['usage_id']).replace(course_key=course_key) expected_modified_time = from_timestamp(kwargs['expected_modified_time']) # The request cache is not maintained on celery workers, # where this code runs. So we take the values from the # main request cache and store them in the local request # cache. This correlates model-level grading events with # higher-level ones. set_event_transaction_id(kwargs.pop('event_transaction_id', None)) set_event_transaction_type(kwargs.pop('event_transaction_type', None)) # Verify the database has been updated with the scores when the task was # created. This race condition occurs if the transaction in the task # creator's process hasn't committed before the task initiates in the worker # process. if not _has_database_updated_with_new_score( kwargs['user_id'], scored_block_usage_key, expected_modified_time, score_deleted, ): raise _retry_recalculate_subsection_grade(**kwargs) _update_subsection_grades( course_key, scored_block_usage_key, kwargs['only_if_higher'], kwargs['user_id'], ) except Exception as exc: # pylint: disable=broad-except if not isinstance(exc, KNOWN_RETRY_ERRORS): log.info("tnl-6244 grades unexpected failure: {}. kwargs={}".format( repr(exc), kwargs )) raise _retry_recalculate_subsection_grade(exc=exc, **kwargs)
def import_test_course(self, solution_attribute=None, solution_element=None): """ Import the test course with the sga unit """ # adapted from edx-platform/cms/djangoapps/contentstore/management/commands/tests/test_cleanup_assets.py root = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) input_dir = os.path.join(root, "test_data") temp_dir = tempfile.mkdtemp() self.addCleanup(lambda: shutil.rmtree(temp_dir)) xml_dir = os.path.join(temp_dir, "xml") shutil.copytree(input_dir, xml_dir) with open(os.path.join(xml_dir, "2017_SGA", "vertical", "vertical.xml"), "w") as f: f.write(self.make_test_vertical(solution_attribute, solution_element)) store = modulestore() import_course_from_xml( store, 'sga_user', xml_dir, ) return store.get_course(CourseLocator.from_string('SGAU/SGA101/course'))
def auto_enroll_email(course_id, email, send_email=True): """ Auto-enroll email in course. Based on lms.djangoapps.instructor.views.api.students_update_enrollment() """ # Raises ValidationError if invalid validate_email(email) locator = CourseLocator.from_string(course_id) course = get_course_by_id(locator) # If we want to notify the newly enrolled student by email, fetch # the required parameters email_params = None language = None if send_email: email_params = get_email_params(course, True, secure=True) # Try to find out what language to send the email in. user = None try: user = User.objects.get(email=email) except User.DoesNotExist: pass else: language = get_user_email_language(user) # Enroll the email enroll_email(locator, email, auto_enroll=True, email_students=send_email, email_params=email_params, language=language)
def initdb(self, default): """ Initialize the database and create one test course in it """ # set the default modulestore store_configs = self.options['stores'] for index in range(len(store_configs)): if store_configs[index]['NAME'] == default: if index > 0: store_configs[index], store_configs[0] = store_configs[0], store_configs[index] break self.store = MixedModuleStore(None, create_modulestore_instance=create_modulestore_instance, **self.options) self.addCleanup(self.store.close_all_connections) # convert to CourseKeys self.course_locations = { course_id: CourseLocator.from_string(course_id) for course_id in [self.MONGO_COURSEID, self.XML_COURSEID1, self.XML_COURSEID2] } # and then to the root UsageKey self.course_locations = { course_id: course_key.make_usage_key('course', course_key.run) for course_id, course_key in self.course_locations.iteritems() # pylint: disable=maybe-no-member } if default == 'split': self.fake_location = CourseLocator( 'foo', 'bar', 'slowly', branch=ModuleStoreEnum.BranchName.draft ).make_usage_key('vertical', 'baz') else: self.fake_location = Location('foo', 'bar', 'slowly', 'vertical', 'baz') self.xml_chapter_location = self.course_locations[self.XML_COURSEID1].replace( category='chapter', name='Overview' ) self._create_course(default, self.course_locations[self.MONGO_COURSEID].course_key)
def clean_course_id(self): """Validate course_id""" value = self.cleaned_data["course_id"] try: return CourseLocator.from_string(value) except InvalidKeyError: raise ValidationError("'{}' is not a valid course id".format(value))
def recalculate_subsection_grade_handler(sender, **kwargs): # pylint: disable=unused-argument """ Consume the SCORE_CHANGED signal and trigger an update. This method expects that the kwargs dictionary will contain the following entries (See the definition of SCORE_CHANGED): - points_possible: Maximum score available for the exercise - points_earned: Score obtained by the user - user: User object - course_id: Unicode string representing the course - usage_id: Unicode string indicating the courseware instance """ try: course_id = kwargs.get('course_id', None) usage_id = kwargs.get('usage_id', None) student = kwargs.get('user', None) course_key = CourseLocator.from_string(course_id) if not PersistentGradesEnabledFlag.feature_enabled(course_key): return usage_key = UsageKey.from_string(usage_id).replace(course_key=course_key) from lms.djangoapps.grades.new.subsection_grade import SubsectionGradeFactory SubsectionGradeFactory(student).update(usage_key, course_key) except Exception as ex: # pylint: disable=broad-except log.exception( u"Failed to process SCORE_CHANGED signal. " "user: %s, course_id: %s, " "usage_id: %s. Exception: %s", unicode(student), course_id, usage_id, ex.message )
def recalculate_subsection_grade_handler(sender, **kwargs): # pylint: disable=unused-argument """ Consume the SCORE_CHANGED signal and trigger an update. This method expects that the kwargs dictionary will contain the following entries (See the definition of SCORE_CHANGED): - points_possible: Maximum score available for the exercise - points_earned: Score obtained by the user - user: User object - course_id: Unicode string representing the course - usage_id: Unicode string indicating the courseware instance """ if not settings.FEATURES.get('ENABLE_SUBSECTION_GRADES_SAVED', False): return try: course_id = kwargs.get('course_id', None) usage_id = kwargs.get('usage_id', None) student = kwargs.get('user', None) course_key = CourseLocator.from_string(course_id) usage_key = UsageKey.from_string(usage_id).replace( course_key=course_key) from lms.djangoapps.grades.new.subsection_grade import SubsectionGradeFactory SubsectionGradeFactory(student).update(usage_key, course_key) except Exception as ex: # pylint: disable=broad-except log.exception( u"Failed to process SCORE_CHANGED signal. " "user: %s, course_id: %s, " "usage_id: %s. Exception: %s", unicode(student), course_id, usage_id, ex.message)
def from_string(cls, serialized): """Deprecated. Use :meth:`locator.CourseLocator.from_string`.""" warnings.warn( "SlashSeparatedCourseKey is deprecated! Please use locator.CourseLocator", DeprecationWarning, stacklevel=2) return CourseLocator.from_string(serialized)
def clean_course_id(self): """Validate course_id""" value = self.cleaned_data["course_id"] try: return CourseLocator.from_string(value) except InvalidKeyError: raise ValidationError(u"'{}' is not a valid course id".format(value))
def recalculate_subsection_grade(user_id, course_id, usage_id): """ Updates a saved subsection grade. This method expects the following parameters: - user_id: serialized id of applicable User object - course_id: Unicode string representing the course - usage_id: Unicode string indicating the courseware instance """ course_key = CourseLocator.from_string(course_id) if not PersistentGradesEnabledFlag.feature_enabled(course_key): return student = User.objects.get(id=user_id) scored_block_usage_key = UsageKey.from_string(usage_id).replace( course_key=course_key) collected_block_structure = get_course_in_cache(course_key) course = get_course_by_id(course_key, depth=0) subsection_grade_factory = SubsectionGradeFactory( student, course, collected_block_structure) subsections_to_update = collected_block_structure.get_transformer_block_field( scored_block_usage_key, GradesTransformer, 'subsections', set()) for subsection_usage_key in subsections_to_update: transformed_subsection_structure = get_course_blocks( student, subsection_usage_key, collected_block_structure=collected_block_structure, ) subsection_grade_factory.update( transformed_subsection_structure[subsection_usage_key], transformed_subsection_structure)
def recalculate_subsection_grade_handler(sender, **kwargs): # pylint: disable=unused-argument """ Consume the SCORE_CHANGED signal and trigger an update. This method expects that the kwargs dictionary will contain the following entries (See the definition of SCORE_CHANGED): - points_possible: Maximum score available for the exercise - points_earned: Score obtained by the user - user: User object - course_id: Unicode string representing the course - usage_id: Unicode string indicating the courseware instance """ student = kwargs['user'] course_key = CourseLocator.from_string(kwargs['course_id']) if not PersistentGradesEnabledFlag.feature_enabled(course_key): return scored_block_usage_key = UsageKey.from_string( kwargs['usage_id']).replace(course_key=course_key) collected_block_structure = get_course_in_cache(course_key) course = get_course_by_id(course_key, depth=0) subsections_to_update = collected_block_structure.get_transformer_block_field( scored_block_usage_key, GradesTransformer, 'subsections', set()) subsection_grade_factory = SubsectionGradeFactory( student, course, collected_block_structure) for subsection_usage_key in subsections_to_update: transformed_subsection_structure = get_course_blocks( student, subsection_usage_key, collected_block_structure=collected_block_structure, ) subsection_grade_factory.update( transformed_subsection_structure[subsection_usage_key], transformed_subsection_structure)
def clean_course_id(self): """Validate course_id""" value = self.cleaned_data["course_id"] try: return CourseLocator.from_string(value) except InvalidKeyError: raise ValidationError(f"'{value}' is not a valid course id") # lint-amnesty, pylint: disable=raise-missing-from
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 recalculate_subsection_grade(user_id, course_id, usage_id, only_if_higher, raw_earned, raw_possible, **kwargs): """ Updates a saved subsection grade. Arguments: user_id (int): id of applicable User object course_id (string): identifying the course usage_id (string): identifying the course block only_if_higher (boolean): indicating whether grades should be updated only if the new raw_earned is higher than the previous value. raw_earned (float): the raw points the learner earned on the problem that triggered the update. raw_possible (float): the max raw points the leaner could have earned on the problem. score_deleted (boolean): indicating whether the grade change is a result of the problem's score being deleted. """ course_key = CourseLocator.from_string(course_id) if not PersistentGradesEnabledFlag.feature_enabled(course_key): return score_deleted = kwargs['score_deleted'] scored_block_usage_key = UsageKey.from_string(usage_id).replace( course_key=course_key) # Verify the database has been updated with the scores when the task was # created. This race condition occurs if the transaction in the task # creator's process hasn't committed before the task initiates in the worker # process. if not _has_database_updated_with_new_score( user_id, scored_block_usage_key, raw_earned, raw_possible, score_deleted, ): raise _retry_recalculate_subsection_grade( user_id, course_id, usage_id, only_if_higher, raw_earned, raw_possible, score_deleted, ) _update_subsection_grades( course_key, scored_block_usage_key, only_if_higher, course_id, user_id, usage_id, raw_earned, raw_possible, score_deleted, )
def handle(self, *args, **options): if len(args) > 1: raise CommandError("check_draft requires one or no arguments: |<course_id>|") # Check args: course_id course_id = args[0] if len(args) > 0 else None if course_id: try: course_id = CourseLocator.from_string(course_id) except InvalidKeyError: raise CommandError("The course_id is not of the right format. It should be like 'org/course/run' or 'course-v1:org+course+run'") if not modulestore().get_course(course_id): raise CommandError("The specified course does not exist.") # Check options: active_only active_only = options['active_only'] # Result output = PrettyTable(['Course ID', 'Course Name', 'Chapter Name', 'Sequential Name', 'Vertical Name', 'Draft?', 'Changed?']) output.align = 'l' # Find courses if course_id: course_items = modulestore().get_items(course_id, qualifiers={'category': 'course'}) if not course_items: raise CommandError("The specified course does not exist.") else: course_items = modulestore().get_courses() for course_item in course_items: # Note: Use only active courses if active_only and course_item.has_ended(): continue # Find chapter items chapter_items = [modulestore().get_item(item.location) for item in course_item.get_children()] chapter_items = sorted(chapter_items, key=lambda item: item.start) for chapter_item in chapter_items: # Find sequential items sequential_items = [modulestore().get_item(item.location) for item in chapter_item.get_children()] sequential_items = sorted(sequential_items, key=lambda item: item.start) for sequential_item in sequential_items: #print "sequential_item.location=%s" % sequential_item.location #print "sequential_item.published?=%s" % modulestore().has_item(sequential_item.location, revision=ModuleStoreEnum.RevisionOption.published_only) #print "sequential_item.changed?=%s" % modulestore().has_changes(sequential_item) # Find vertical items vertical_items = [modulestore().get_item(item.location) for item in sequential_item.get_children()] vertical_items = sorted(vertical_items, key=lambda item: item.start) for vertical_item in vertical_items: #print "vertical_item.location=%s" % vertical_item.location #print "vertical_item.published?=%s" % modulestore().has_item(vertical_item.location, revision=ModuleStoreEnum.RevisionOption.published_only) #print "vertical_item.changed?=%s" % modulestore().has_changes(vertical_item) is_draft = vertical_item.is_draft # Note: cribbed from cms/djangoapps/contentstore/views/tests/test_item.py has_changes = modulestore().has_changes(vertical_item) if is_draft or has_changes: output.add_row([course_item.id, course_item.display_name, chapter_item.display_name, sequential_item.display_name, vertical_item.display_name, is_draft, has_changes]) # Print result print output
def setUp(self): self.user = "******" self.course_id = CourseLocator.from_string("org/num/run") self.debug = False self.noop = False self.file_prefix = "" self.exclude = None self.seed = "seed"
def get_course_block_counts(course): course_key = CourseLocator.from_string(course) try: structure_id = API.get_structure_by_key(course_key) except API.CourseNotFound: abort(404) counts = API.get_block_counts(structure_id) return Response(dumps(counts), mimetype='application/json')
def from_string(cls, serialized): """Deprecated. Use :meth:`locator.CourseLocator.from_string`.""" warnings.warn( "SlashSeparatedCourseKey is deprecated! Please use locator.CourseLocator", DeprecationWarning, stacklevel=2 ) return CourseLocator.from_string(serialized)
def test_handle_publish(self, check_mock): cc.Command().handle(*self.args_publish, **self.kwargs) check_mock.assert_called_with(self.args_publish[1]) self.cert_mock.assert_called_with( self.kwargs['username'], CourseLocator.from_string(self.args_publish[1]), self.kwargs['debug'], self.kwargs['noop'], self.kwargs['prefix'], self.kwargs['exclude']) self.cert_mock().publish.assert_called_with()
def test_basic(self): form = self.get_form(expected_valid=True) self.assertEqual( form.cleaned_data, { "course_id": CourseLocator.from_string("Foo/Bar/Baz"), "page": 2, "page_size": 13, })
def test_basic(self): form = self.get_form(expected_valid=True) assert form.cleaned_data == { 'course_id': CourseLocator.from_string('a/b/c'), 'flagged': False, 'page': 2, 'page_size': 13, 'requested_fields': set() }
class SurveySubmissionFactory(DjangoModelFactory): FACTORY_FOR = SurveySubmission course_id = CourseLocator.from_string('edX/test/course1') unit_id = '11111111111111111111111111111111' user = factory.SubFactory(UserFactory) survey_name = 'survey #1' survey_answer = '{"Q1": "1", "Q2": ["2", "3"], "Q3": "test"}' created = datetime(2014, 4, 1, tzinfo=UTC)
def recalculate_subsection_grade(user_id, course_id, usage_id, only_if_higher, raw_earned, raw_possible): """ Updates a saved subsection grade. This method expects the following parameters: - user_id: serialized id of applicable User object - course_id: Unicode string identifying the course - usage_id: Unicode string identifying the course block - only_if_higher: boolean indicating whether grades should be updated only if the new raw_earned is higher than the previous value. - raw_earned: the raw points the learner earned on the problem that triggered the update - raw_possible: the max raw points the leaner could have earned on the problem """ course_key = CourseLocator.from_string(course_id) if not PersistentGradesEnabledFlag.feature_enabled(course_key): return scored_block_usage_key = UsageKey.from_string(usage_id).replace(course_key=course_key) score = get_score(user_id, scored_block_usage_key) # If the score is None, it has not been saved at all yet # and we need to retry until it has been saved. if score is None: raise _retry_recalculate_subsection_grade( user_id, course_id, usage_id, only_if_higher, raw_earned, raw_possible, ) else: module_raw_earned, module_raw_possible = score # pylint: disable=unpacking-non-sequence # Validate that the retrieved scores match the scores when the task was created. # This race condition occurs if the transaction in the task creator's process hasn't # committed before the task initiates in the worker process. grades_match = module_raw_earned == raw_earned and module_raw_possible == raw_possible # We have to account for the situation where a student's state # has been deleted- in this case, get_score returns (None, None), but # the score change signal will contain 0 for raw_earned. state_deleted = module_raw_earned is None and module_raw_possible is None and raw_earned == 0 if not (state_deleted or grades_match): raise _retry_recalculate_subsection_grade( user_id, course_id, usage_id, only_if_higher, raw_earned, raw_possible, ) _update_subsection_grades( course_key, scored_block_usage_key, only_if_higher, course_id, user_id, usage_id, raw_earned, raw_possible, )
def setUp(self): self.fp = StringIO.StringIO() self.username = "******" self.course_id = CourseLocator.from_string("org/num/run") self.course_name = "testcoursename" self.file_prefix = "prefix-" patcher0 = patch('pdfgen.views.logging') self.log_mock = patcher0.start() self.addCleanup(patcher0.stop)
def setUp(self): self.username = "******" self.course_id = CourseLocator.from_string("org/num/run") self.filepath = "/file/is/not/exists" self.bucket_name = settings.PDFGEN_BUCKET_NAME self.location = Location.APNortheast patcher0 = patch('pdfgen.views.logging') self.log = patcher0.start() self.addCleanup(patcher0.stop)
def setUp(self): self.user = "******" self.course_id = CourseLocator.from_string("org/num/run") self.debug = False self.noop = False self.file_prefix = "" self.exclude = None self.mail = "*****@*****.**" self.students = UserFactory.create_batch(3)
def setUp(self): self.user = "******" self.course_id = CourseLocator.from_string("org/num/run") self.debug = False self.noop = False self.file_prefix = "" self.exclude = None self.student = UserFactory.create() self.cert = MagicMock()
def test_basic(self): form = self.get_form(expected_valid=True) self.assertEqual( form.cleaned_data, { "course_id": CourseLocator.from_string("Foo/Bar/Baz"), "page": 2, "page_size": 13, } )
def setUp(self): self.user = "******" self.course_id = CourseLocator.from_string("org/num/run") self.debug = False self.noop = False self.file_prefix = "" self.exclude = None self.student = UserFactory.create() self.cert = GeneratedCertificateFactory.build( user=self.student, status="downloadable")
def test_certificate_for_missing_course(self): """ Verify that a certificate is not shown for a missing course. """ # Add new certificate cert = self._create_certificate(course_key=CourseLocator.from_string('course-v1:edX+INVALID+1')) cert.save() response = self.client.get('/u/{username}'.format(username=self.user.username)) self.assertNotContains(response, 'card certificate-card mode-{cert_mode}'.format(cert_mode=cert.mode))
def get_university_id(user, course_id): if isinstance(user, AnonymousUser): return None try: return UniversityID.objects.get( user=user, course_key=CourseLocator.from_string(course_id), ) except UniversityID.DoesNotExist: return None
def test_basic(self): self.form_data.setlist("topic_id", ["example topic_id", "example 2nd topic_id"]) form = self.get_form(expected_valid=True) self.assertEqual( form.cleaned_data, { "course_id": CourseLocator.from_string("Foo/Bar/Baz"), "page": 2, "page_size": 13, "topic_id": ["example topic_id", "example 2nd topic_id"], } )
def test_basic(self): form = self.get_form(expected_valid=True) self.assertEqual( form.cleaned_data, { "course_id": CourseLocator.from_string("Foo/Bar/Baz"), "page": 2, "page_size": 13, "topic_id": [], "text_search": "", "following": None, } )
def test_handle(self, store_mock, get_mock, del_mock, call_mock): pr.Command().handle(*self.args, **self.options_get) get_mock.assert_called_once_with(*[CourseLocator.from_string(arg) for arg in self.args]) pr.Command().handle(*self.args, **self.options_create) call_mock.assert_called_once_with( 'create_report_task', *['create'], **{'course_id': self.args[0]}) pr.Command().handle(*self.args, **self.options_delete) del_mock.assert_called_once_with(*[CourseLocator.from_string(arg) for arg in self.args]) msg = '^"course_id" is not specified$' with self.assertRaisesRegexp(CommandError, msg): pr.Command().handle(*[], **self.options_get) msg = '^Cannot specify "-c" option and "-d" option at the same time.$' with self.assertRaisesRegexp(CommandError, msg): pr.Command().handle(*self.args, **self.options_error) msg = '^CSV not found.$' with self.assertRaisesRegexp(CommandError, msg): get_mock.side_effect = NotFoundError() pr.Command().handle(*self.args, **self.options_get)