def instrument_course_progress_render( self, course_width, enable_ccx, view_as_ccx, sql_queries, mongo_reads, ): """ Renders the progress page, instrumenting Mongo reads and SQL queries. """ course_key = self.setup_course(course_width, enable_ccx, view_as_ccx) # Switch to published-only mode to simulate the LMS with self.settings(MODULESTORE_BRANCH='published-only'): # Clear all caches before measuring for cache in settings.CACHES: caches[cache].clear() # Refill the metadata inheritance cache get_course_in_cache(course_key) # We clear the request cache to simulate a new request in the LMS. RequestCache.clear_request_cache() # Reset the list of provider classes, so that our django settings changes # can actually take affect. OverrideFieldData.provider_classes = None with self.assertNumQueries(sql_queries, using='default', table_blacklist=QUERY_COUNT_TABLE_BLACKLIST): with self.assertNumQueries(0, using='student_module_history'): with self.assertMongoCallCount(mongo_reads): with self.assertXBlockInstantiations(1): self.grade_course(course_key)
def instrument_course_progress_render( self, course_width, enable_ccx, view_as_ccx, sql_queries, mongo_reads, ): """ Renders the progress page, instrumenting Mongo reads and SQL queries. """ course_key = self.setup_course(course_width, enable_ccx, view_as_ccx) # Switch to published-only mode to simulate the LMS with self.settings(MODULESTORE_BRANCH='published-only'): # Clear all caches before measuring for cache in settings.CACHES: caches[cache].clear() # Refill the metadata inheritance cache get_course_in_cache(course_key) # We clear the request cache to simulate a new request in the LMS. RequestCache.clear_request_cache() # Reset the list of provider classes, so that our django settings changes # can actually take affect. OverrideFieldData.provider_classes = None with self.assertNumQueries(sql_queries, using='default'): with self.assertNumQueries(0, using='student_module_history'): with self.assertMongoCallCount(mongo_reads): with self.assertXBlockInstantiations(1): self.grade_course(course_key)
def test_queries(self): """ Verify that the view's query count doesn't regress. """ # Pre-fill the course blocks cache get_course_in_cache(self.course.id) # Fetch the view and verify the query counts with self.assertNumQueries(36): with check_mongo_calls(3): url = course_home_url(self.course) self.client.get(url)
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 handle(self, *args, **options): if options.get('all'): course_keys = [ course.id for course in modulestore().get_course_summaries() ] else: if len(args) < 1: raise CommandError( 'At least one course or --all must be specified.') try: course_keys = [CourseKey.from_string(arg) for arg in args] except InvalidKeyError: raise CommandError('Invalid key specified.') log.info('Generating course blocks for %d courses.', len(course_keys)) log.debug('Generating course blocks for the following courses: %s', course_keys) for course_key in course_keys: try: if options.get('force'): block_structure = update_course_in_cache(course_key) else: block_structure = get_course_in_cache(course_key) if options.get('dags'): self._find_and_log_dags(block_structure, course_key) except Exception as ex: # pylint: disable=broad-except log.exception( 'An error occurred while generating course blocks for %s: %s', unicode(course_key), ex.message, ) log.info('Finished generating course blocks.')
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 handle(self, *args, **options): if options.get('all'): course_keys = [course.id for course in modulestore().get_course_summaries()] else: if len(args) < 1: raise CommandError('At least one course or --all must be specified.') try: course_keys = [CourseKey.from_string(arg) for arg in args] except InvalidKeyError: raise CommandError('Invalid key specified.') log.info('Generating course blocks for %d courses.', len(course_keys)) log.debug('Generating course blocks for the following courses: %s', course_keys) for course_key in course_keys: try: if options.get('force'): block_structure = update_course_in_cache(course_key) else: block_structure = get_course_in_cache(course_key) if options.get('dags'): self._find_and_log_dags(block_structure, course_key) except Exception as ex: # pylint: disable=broad-except log.exception( 'An error occurred while generating course blocks for %s: %s', unicode(course_key), ex.message, ) log.info('Finished generating course blocks.')
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(user_id, course_id, usage_id, only_if_higher): """ 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 - only_if_higher: boolean indicating whether grades should be updated only if the new grade is higher than the previous value. """ if not PersistentGradesEnabledFlag.feature_enabled(course_id): return course_key = CourseLocator.from_string(course_id) 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 = modulestore().get_course(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()) try: 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 = subsection_grade_factory.update( transformed_subsection_structure[subsection_usage_key], transformed_subsection_structure, only_if_higher, ) SUBSECTION_SCORE_CHANGED.send( sender=recalculate_subsection_grade, course=course, user=student, subsection_grade=subsection_grade, ) except IntegrityError as exc: raise recalculate_subsection_grade.retry( args=[user_id, course_id, usage_id], exc=exc)
def handle(self, *args, **options): if options.get('all'): course_keys = [ course.id for course in modulestore().get_course_summaries() ] if options.get('start'): end = options.get('end') or len(course_keys) course_keys = course_keys[options['start']:end] else: if len(args) < 1: raise CommandError( 'At least one course or --all must be specified.') try: course_keys = [CourseKey.from_string(arg) for arg in args] except InvalidKeyError: raise CommandError('Invalid key specified.') log.info('Generating course blocks for %d courses.', len(course_keys)) if options.get('verbose'): log.setLevel(logging.DEBUG) else: log.setLevel(logging.CRITICAL) dag_info = _DAGInfo() for course_key in course_keys: try: if options.get('force'): block_structure = update_course_in_cache(course_key) else: block_structure = get_course_in_cache(course_key) if options.get('dags'): self._find_and_log_dags(block_structure, course_key, dag_info) except Exception as ex: # pylint: disable=broad-except log.exception( 'An error occurred while generating course blocks for %s: %s', unicode(course_key), ex.message, ) log.info('Finished generating course blocks.') if options.get('dags'): log.critical('DAG data: %s', unicode(dag_info))
def setUp(self): super(CourseDataTest, self).setUp() with self.store.default_store(ModuleStoreEnum.Type.split): self.course = CourseFactory.create() # need to re-retrieve the course since the version on the original course isn't accurate. self.course = self.store.get_course(self.course.id) self.user = UserFactory.create() self.collected_structure = get_course_in_cache(self.course.id) self.one_true_structure = get_course_blocks( self.user, self.course.location, collected_block_structure=self.collected_structure, ) self.expected_results = { 'course': self.course, 'collected_block_structure': self.collected_structure, 'structure': self.one_true_structure, 'course_key': self.course.id, 'location': self.course.location, }
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['course_id'] usage_id = kwargs['usage_id'] student = kwargs['user'] except KeyError: log.exception( u"Failed to process SCORE_CHANGED signal, some arguments were missing." "user: %s, course_id: %s, usage_id: %s.", kwargs.get('user', None), kwargs.get('course_id', None), kwargs.get('usage_id', None), ) return 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) block_structure = get_course_in_cache(course_key) subsections_to_update = block_structure.get_transformer_block_field( usage_key, GradesTransformer, 'subsections', set() ) for subsection in subsections_to_update: SubsectionGradeFactory(student).update(subsection, course_key)
def handle(self, *args, **options): if options.get("all"): course_keys = [course.id for course in modulestore().get_course_summaries()] if options.get("start"): end = options.get("end") or len(course_keys) course_keys = course_keys[options["start"] : end] else: if len(args) < 1: raise CommandError("At least one course or --all must be specified.") try: course_keys = [CourseKey.from_string(arg) for arg in args] except InvalidKeyError: raise CommandError("Invalid key specified.") log.info("Generating course blocks for %d courses.", len(course_keys)) if options.get("verbose"): log.setLevel(logging.DEBUG) else: log.setLevel(logging.CRITICAL) dag_info = _DAGInfo() for course_key in course_keys: try: if options.get("force"): block_structure = update_course_in_cache(course_key) else: block_structure = get_course_in_cache(course_key) if options.get("dags"): self._find_and_log_dags(block_structure, course_key, dag_info) except Exception as ex: # pylint: disable=broad-except log.exception( "An error occurred while generating course blocks for %s: %s", unicode(course_key), ex.message ) log.info("Finished generating course blocks.") if options.get("dags"): log.critical("DAG data: %s", unicode(dag_info))
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['course_id'] usage_id = kwargs['usage_id'] student = kwargs['user'] except KeyError: log.exception( u"Failed to process SCORE_CHANGED signal, some arguments were missing." "user: %s, course_id: %s, usage_id: %s.", kwargs.get('user', None), kwargs.get('course_id', None), kwargs.get('usage_id', None), ) return 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) block_structure = get_course_in_cache(course_key) subsections_to_update = block_structure.get_transformer_block_field( usage_key, GradesTransformer, 'subsections', set()) for subsection in subsections_to_update: SubsectionGradeFactory(student).update(subsection, course_key)
def course_structure(self): return get_course_in_cache(self.course_id)
def get_user_grades(user_id, course_str): """ Get a single user's grades for course. """ user = USER_MODEL.objects.get(id=user_id) course_key = CourseKey.from_string(str(course_str)) course = courses.get_course(course_key) course_grade = CourseGradeFactory().update(user, course) course_structure = get_course_in_cache(course.id) courseware_summary = course_grade.chapter_grades.values() grade_summary = course_grade.summary grades_schema = {} courseware_summary = course_grade.chapter_grades.items() chapter_schema = {} for key, chapter in courseware_summary: subsection_schema = {} for section in chapter['sections']: section_children = course_structure.get_children(section.location) verticals = course_structure.get_children(section.location) vertical_schema = {} for vertical_key in verticals: sections_scores = {} problem_keys = course_structure.get_children(vertical_key) for problem_key in problem_keys: if problem_key in section.problem_scores: problem_score = section.problem_scores[problem_key] xblock_content_url = reverse( 'courseware.views.views.render_xblock', kwargs={'usage_key_string': unicode(problem_key)}, ) xblock_structure_url = generate_xblock_structure_url( course_str, problem_key, user) sections_scores[str(problem_key)] = { "date": problem_score.first_attempted if problem_score.first_attempted is not None else "Not attempted", "earned": problem_score.earned, "possible": problem_score.possible, "xblock_content_url": "{}{}".format(settings.LMS_ROOT_URL, xblock_content_url), "xblock_structure_url": "{}{}".format(settings.LMS_ROOT_URL, xblock_structure_url) } else: sections_scores[str( problem_key)] = "This block has no grades" vertical_structure_url = generate_xblock_structure_url( course_str, vertical_key, user) vertical_schema[str(vertical_key)] = { 'problem_blocks': sections_scores, "vertical_structure_url": vertical_structure_url } subsection_structure_url = generate_xblock_structure_url( course_str, section.location, user) subsection_schema[str(section.location)] = { "verticals": vertical_schema, "section_score": course_grade.score_for_module(section.location), "subsection_structure_url": subsection_structure_url } chapter_structure_url = generate_xblock_structure_url( course_str, key, user) chapter_schema[str(key)] = { "sections": subsection_schema, "chapter_structure_url": chapter_structure_url } return chapter_schema
def grading_context_for_course(course_key): """ Same as grading_context, but takes in a course key. """ course_structure = get_course_in_cache(course_key) return grading_context(course_structure)
def grading_context_for_course(course): """ Same as grading_context, but takes in a course key. """ course_structure = get_course_in_cache(course.id) return grading_context(course, course_structure)
def grading_context_for_course(course): """ Same as grading_context, but takes in a course object. """ course_structure = get_course_in_cache(course.id) return grading_context(course_structure)
def grading_context_for_course(course_key): """ Same as grading_context, but takes in a course object. """ course_structure = get_course_in_cache(course_key) return grading_context(course_structure)