def test_not_in_the_list(self, patched_ugettext): value = 'Submit' with override_lang('eo'): with patch('util.i18n.ugettext') as patched_ugettext: force_translate( value, ugettext_noop('Name'), ugettext_noop('Cancel'), ) self.assertFalse(patched_ugettext.called, 'ugettext should not be called!') result = force_translate( value, ugettext_noop('Name'), ugettext_noop('Cancel'), ) self.assertEqual(value, result)
def test_happy_scenario(self): value = 'Submit' with override_lang('eo'): with patch('util.i18n.ugettext') as patched_ugettext: result = force_translate( value, ugettext_noop('Submit'), ugettext_noop('Cancel'), ) self.assertNotEqual(value, unicode(result)) patched_ugettext.assert_called_once_with('Submit')
def _progress_summary(student, request, course, field_data_cache=None, scores_client=None): """ Unwrapped version of "progress_summary". This pulls a summary of all problems in the course. Returns - courseware_summary is a summary of all sections with problems in the course. It is organized as an array of chapters, each containing an array of sections, each containing an array of scores. This contains information for graded and ungraded problems, and is good for displaying a course summary with due dates, etc. Arguments: student: A User object for the student to grade course: A Descriptor containing the course to grade If the student does not have access to load the course module, this function will return None. """ with outer_atomic(): if field_data_cache is None: field_data_cache = field_data_cache_for_grading(course, student) if scores_client is None: scores_client = ScoresClient.from_field_data_cache( field_data_cache) course_module = get_module_for_descriptor(student, request, course, field_data_cache, course.id, course=course) if not course_module: return None course_module = getattr(course_module, '_x_module', course_module) # We need to import this here to avoid a circular dependency of the form: # XBlock --> submissions --> Django Rest Framework error strings --> # Django translation --> ... --> courseware --> submissions from submissions import api as sub_api # installed from the edx-submissions repository with outer_atomic(): submissions_scores = sub_api.get_scores( course.id.to_deprecated_string(), anonymous_id_for_user(student, course.id)) max_scores_cache = MaxScoresCache.create_for_course(course) # For the moment, we have to get scorable_locations from field_data_cache # and not from scores_client, because scores_client is ignorant of things # in the submissions API. As a further refactoring step, submissions should # be hidden behind the ScoresClient. max_scores_cache.fetch_from_remote(field_data_cache.scorable_locations) chapters = [] locations_to_children = defaultdict(list) locations_to_weighted_scores = {} # Don't include chapters that aren't displayable (e.g. due to error) for chapter_module in course_module.get_display_items(): # Skip if the chapter is hidden if chapter_module.hide_from_toc: continue sections = [] for section_module in chapter_module.get_display_items(): # Skip if the section is hidden with outer_atomic(): if section_module.hide_from_toc: continue graded = section_module.graded scores = [] module_creator = section_module.xmodule_runtime.get_module for module_descriptor in yield_dynamic_descriptor_descendants( section_module, student.id, module_creator): locations_to_children[module_descriptor.parent].append( module_descriptor.location) (correct, total) = get_score( student, module_descriptor, module_creator, scores_client, submissions_scores, max_scores_cache, ) if correct is None and total is None: continue weighted_location_score = Score( correct, total, graded, module_descriptor.display_name_with_default_escaped, module_descriptor.location) scores.append(weighted_location_score) locations_to_weighted_scores[ module_descriptor.location] = weighted_location_score scores.reverse() section_total, _ = graders.aggregate_scores( scores, section_module.display_name_with_default_escaped) module_format = section_module.format if section_module.format is not None else '' sections.append({ 'display_name': section_module.display_name_with_default_escaped, 'url_name': section_module.url_name, 'scores': scores, 'section_total': section_total, 'format': force_translate(module_format, ugettext_noop('Entrance Exam')), 'due': section_module.due, 'graded': graded, }) chapters.append({ 'course': course.display_name_with_default_escaped, 'display_name': chapter_module.display_name_with_default_escaped, 'url_name': chapter_module.url_name, 'sections': sections }) max_scores_cache.push_to_remote() return ProgressSummary(chapters, locations_to_weighted_scores, locations_to_children)
def _progress_summary(student, request, course, field_data_cache=None, scores_client=None): """ Unwrapped version of "progress_summary". This pulls a summary of all problems in the course. Returns - courseware_summary is a summary of all sections with problems in the course. It is organized as an array of chapters, each containing an array of sections, each containing an array of scores. This contains information for graded and ungraded problems, and is good for displaying a course summary with due dates, etc. Arguments: student: A User object for the student to grade course: A Descriptor containing the course to grade If the student does not have access to load the course module, this function will return None. """ with outer_atomic(): if field_data_cache is None: field_data_cache = field_data_cache_for_grading(course, student) if scores_client is None: scores_client = ScoresClient.from_field_data_cache(field_data_cache) course_module = get_module_for_descriptor( student, request, course, field_data_cache, course.id, course=course ) if not course_module: return None course_module = getattr(course_module, '_x_module', course_module) # We need to import this here to avoid a circular dependency of the form: # XBlock --> submissions --> Django Rest Framework error strings --> # Django translation --> ... --> courseware --> submissions from submissions import api as sub_api # installed from the edx-submissions repository with outer_atomic(): submissions_scores = sub_api.get_scores( course.id.to_deprecated_string(), anonymous_id_for_user(student, course.id) ) max_scores_cache = MaxScoresCache.create_for_course(course) # For the moment, we have to get scorable_locations from field_data_cache # and not from scores_client, because scores_client is ignorant of things # in the submissions API. As a further refactoring step, submissions should # be hidden behind the ScoresClient. max_scores_cache.fetch_from_remote(field_data_cache.scorable_locations) chapters = [] locations_to_children = defaultdict(list) locations_to_weighted_scores = {} # Don't include chapters that aren't displayable (e.g. due to error) for chapter_module in course_module.get_display_items(): # Skip if the chapter is hidden if chapter_module.hide_from_toc: continue sections = [] for section_module in chapter_module.get_display_items(): # Skip if the section is hidden with outer_atomic(): if section_module.hide_from_toc: continue graded = section_module.graded scores = [] module_creator = section_module.xmodule_runtime.get_module for module_descriptor in yield_dynamic_descriptor_descendants( section_module, student.id, module_creator ): locations_to_children[module_descriptor.parent].append(module_descriptor.location) (correct, total) = get_score( student, module_descriptor, module_creator, scores_client, submissions_scores, max_scores_cache, ) if correct is None and total is None: continue weighted_location_score = Score( correct, total, graded, module_descriptor.display_name_with_default_escaped, module_descriptor.location ) scores.append(weighted_location_score) locations_to_weighted_scores[module_descriptor.location] = weighted_location_score scores.reverse() section_total, _ = graders.aggregate_scores( scores, section_module.display_name_with_default_escaped) module_format = section_module.format if section_module.format is not None else '' sections.append({ 'display_name': section_module.display_name_with_default_escaped, 'url_name': section_module.url_name, 'scores': scores, 'section_total': section_total, 'format': force_translate(module_format, ugettext_noop('Entrance Exam')), 'due': section_module.due, 'graded': graded, }) chapters.append({ 'course': course.display_name_with_default_escaped, 'display_name': chapter_module.display_name_with_default_escaped, 'url_name': chapter_module.url_name, 'sections': sections }) max_scores_cache.push_to_remote() return ProgressSummary(chapters, locations_to_weighted_scores, locations_to_children)
def toc_for_course(user, request, course, active_chapter, active_section, field_data_cache): ''' Create a table of contents from the module store Return format: [ {'display_name': name, 'url_name': url_name, 'sections': SECTIONS, 'active': bool}, ... ] where SECTIONS is a list [ {'display_name': name, 'url_name': url_name, 'format': format, 'due': due, 'active' : bool, 'graded': bool}, ...] active is set for the section and chapter corresponding to the passed parameters, which are expected to be url_names of the chapter+section. Everything else comes from the xml, or defaults to "". chapters with name 'hidden' are skipped. NOTE: assumes that if we got this far, user has access to course. Returns None if this is not the case. field_data_cache must include data from the course module and 2 levels of its descendents ''' with modulestore().bulk_operations(course.id): course_module = get_module_for_descriptor( user, request, course, field_data_cache, course.id, course=course ) if course_module is None: return None toc_chapters = list() chapters = course_module.get_display_items() # See if the course is gated by one or more content milestones required_content = milestones_helpers.get_required_content(course, user) # The user may not actually have to complete the entrance exam, if one is required if not user_must_complete_entrance_exam(request, user, course): required_content = [content for content in required_content if not content == course.entrance_exam_id] for chapter in chapters: # Only show required content, if there is required content # chapter.hide_from_toc is read-only (boo) display_id = slugify(chapter.display_name_with_default_escaped) local_hide_from_toc = False if required_content: if unicode(chapter.location) not in required_content: local_hide_from_toc = True # Skip the current chapter if a hide flag is tripped if chapter.hide_from_toc or local_hide_from_toc: continue sections = list() for section in chapter.get_display_items(): active = (chapter.url_name == active_chapter and section.url_name == active_section) if not section.hide_from_toc: section_format = section.format if section.format is not None else '' section_context = { 'display_name': section.display_name_with_default_escaped, 'url_name': section.url_name, 'format': force_translate(section_format, ugettext_noop('Entrance Exam')), 'due': section.due, 'active': active, 'graded': section.graded, } # # Add in rendering context if exam is a timed exam (which includes proctored) # section_is_time_limited = ( getattr(section, 'is_time_limited', False) and settings.FEATURES.get('ENABLE_SPECIAL_EXAMS', False) ) if section_is_time_limited: # We need to import this here otherwise Lettuce test # harness fails. When running in 'harvest' mode, the # test service appears to get into trouble with # circular references (not sure which as edx_proctoring.api # doesn't import anything from edx-platform). Odd thing # is that running: manage.py lms runserver --settings=acceptance # works just fine, it's really a combination of Lettuce and the # 'harvest' management command # # One idea is that there is some coupling between # lettuce and the 'terrain' Djangoapps projects in /common # This would need more investigation from edx_proctoring.api import get_attempt_status_summary # # call into edx_proctoring subsystem # to get relevant proctoring information regarding this # level of the courseware # # This will return None, if (user, course_id, content_id) # is not applicable # timed_exam_attempt_context = None try: timed_exam_attempt_context = get_attempt_status_summary( user.id, unicode(course.id), unicode(section.location) ) except Exception, ex: # pylint: disable=broad-except # safety net in case something blows up in edx_proctoring # as this is just informational descriptions, it is better # to log and continue (which is safe) than to have it be an # unhandled exception log.exception(ex) if timed_exam_attempt_context: # yes, user has proctoring context about # this level of the courseware # so add to the accordion data context section_context.update({ 'proctoring': timed_exam_attempt_context, 'proctoring_short_description': force_translate(timed_exam_attempt_context.get('short_description', ''), ugettext_noop('Timed Exam')) }) sections.append(section_context) toc_chapters.append({ 'display_name': chapter.display_name_with_default_escaped, 'display_id': display_id, 'url_name': chapter.url_name, 'sections': sections, 'active': chapter.url_name == active_chapter }) return toc_chapters