def get_course_tab_list(request, course): """ Retrieves the course tab list from xmodule.tabs and manipulates the set as necessary """ user = request.user xmodule_tab_list = CourseTabList.iterate_displayable(course, user=user) # Now that we've loaded the tabs for this course, perform the Entrance Exam work. # If the user has to take an entrance exam, we'll need to hide away all but the # "Courseware" tab. The tab is then renamed as "Entrance Exam". course_tab_list = [] must_complete_ee = user_must_complete_entrance_exam(request, user, course) for tab in xmodule_tab_list: if must_complete_ee: # Hide all of the tabs except for 'Courseware' # Rename 'Courseware' tab to 'Entrance Exam' if tab.type != 'courseware': continue tab.name = _("Entrance Exam") if tab.type == 'static_tab' and tab.course_staff_only and \ not bool(user and has_access(user, 'staff', course, course.id)): continue course_tab_list.append(tab) # Add in any dynamic tabs, i.e. those that are not persisted course_tab_list += _get_dynamic_tabs(course, user) return course_tab_list
def get_course_tab_list(request, course): """ Retrieves the course tab list from xmodule.tabs and manipulates the set as necessary """ user = request.user xmodule_tab_list = CourseTabList.iterate_displayable(course, user=user) # Now that we've loaded the tabs for this course, perform the Entrance Exam work. # If the user has to take an entrance exam, we'll need to hide away all but the # "Courseware" tab. The tab is then renamed as "Entrance Exam". course_tab_list = [] must_complete_ee = user_must_complete_entrance_exam(request, user, course) for tab in xmodule_tab_list: # Rename 'Home' tab to 'Information' if tab.type is 'course_info': tab.name = _("Information") if must_complete_ee: # Hide all of the tabs except for 'Courseware' # Rename 'Courseware' tab to 'Entrance Exam' if tab.type is not 'courseware': continue tab.name = _("Entrance Exam") course_tab_list.append(tab) # Add in any dynamic tabs, i.e. those that are not persisted course_tab_list += _get_dynamic_tabs(course, user) # Add course welcome tab if feature is enabled if settings.FEATURES.get('TMA_ENABLE_COURSE_WELCOME_PAGE' ) and CourseWelcomeTab.is_enabled(course, user): course_tab_list.insert(0, CourseWelcomeTab({})) return course_tab_list
def get_course_tab_list(request, course): """ Retrieves the course tab list from xmodule.tabs and manipulates the set as necessary """ user = request.user is_user_enrolled = user.is_authenticated() and CourseEnrollment.is_enrolled(user, course.id) xmodule_tab_list = CourseTabList.iterate_displayable( course, user=user, settings=settings, is_user_authenticated=user.is_authenticated(), is_user_staff=has_access(user, 'staff', course, course.id), is_user_enrolled=is_user_enrolled, is_user_sneakpeek=not UserProfile.has_registered(user), ) # Now that we've loaded the tabs for this course, perform the Entrance Exam work. # If the user has to take an entrance exam, we'll need to hide away all but the # "Courseware" tab. The tab is then renamed as "Entrance Exam". course_tab_list = [] must_complete_ee = user_must_complete_entrance_exam(request, user, course) for tab in xmodule_tab_list: if must_complete_ee: # Hide all of the tabs except for 'Courseware' # Rename 'Courseware' tab to 'Entrance Exam' if tab.type is not 'courseware': continue tab.name = _("Entrance Exam") course_tab_list.append(tab) # Add in any dynamic tabs, i.e. those that are not persisted course_tab_list += _get_dynamic_tabs(course, user) return course_tab_list
def get_course_tab_list(course, user): """ Retrieves the course tab list from xmodule.tabs and manipulates the set as necessary """ user_is_enrolled = user.is_authenticated( ) and CourseEnrollment.is_enrolled(user, course.id) xmodule_tab_list = CourseTabList.iterate_displayable( course, settings, user.is_authenticated(), has_access(user, 'staff', course, course.id), user_is_enrolled) # Now that we've loaded the tabs for this course, perform the Entrance Exam work # If the user has to take an entrance exam, we'll need to hide away all of the tabs # except for the Courseware and Instructor tabs (latter is only viewed if applicable) # We don't have access to the true request object in this context, but we can use a mock request = RequestFactory().request() request.user = user course_tab_list = [] for tab in xmodule_tab_list: if user_must_complete_entrance_exam(request, user, course): # Hide all of the tabs except for 'Courseware' and 'Instructor' # Rename 'Courseware' tab to 'Entrance Exam' if tab.type not in ['courseware', 'instructor']: continue if tab.type == 'courseware': tab.name = _("Entrance Exam") course_tab_list.append(tab) return course_tab_list
def get_course_tab_list(course, user): """ Retrieves the course tab list from xmodule.tabs and manipulates the set as necessary """ user_is_enrolled = user.is_authenticated() and CourseEnrollment.is_enrolled(user, course.id) xmodule_tab_list = CourseTabList.iterate_displayable( course, settings, user.is_authenticated(), has_access(user, 'staff', course, course.id), user_is_enrolled ) # Now that we've loaded the tabs for this course, perform the Entrance Exam work # If the user has to take an entrance exam, we'll need to hide away all of the tabs # except for the Courseware and Instructor tabs (latter is only viewed if applicable) # We don't have access to the true request object in this context, but we can use a mock request = RequestFactory().request() request.user = user course_tab_list = [] for tab in xmodule_tab_list: if user_must_complete_entrance_exam(request, user, course): # Hide all of the tabs except for 'Courseware' and 'Instructor' # Rename 'Courseware' tab to 'Entrance Exam' if tab.type not in ['courseware', 'instructor']: continue if tab.type == 'courseware': tab.name = _("Entrance Exam") course_tab_list.append(tab) return course_tab_list
def prepare_sections_with_grade(request, course): ''' Create sections with grade details. Return format: { 'sections': [ { 'display_name': name, # in case of cohorts or any other accessibility settings 'hidden': hidden, 'url_name': url_name, 'units': UNITS, 'rank': rank, 'badge': bagde status, 'points': grade points, 'podium': podium status, 'week': section_index + 1, }, ], } where UNITS is a list [ { 'display_name': name, 'position': unit position in section, 'css_class': css class, } , ... ] sections with name 'hidden' are skipped. NOTE: assumes that if we got this far, user has access to course. Returns [] if this is not the case. ''' # Set the student to request user student = request.user # Get the field data cache field_data_cache = FieldDataCache.cache_for_descriptor_descendents( course.id, student, course, depth=2, ) # Get the course module with modulestore().bulk_operations(course.id): course_module = get_module_for_descriptor( student, request, course, field_data_cache, course.id, course=course ) if course_module is None: return [] # Get the field data cache staff_user = User.objects.filter(is_staff=1)[0] staff_field_data_cache = FieldDataCache.cache_for_descriptor_descendents( course.id, staff_user, course, depth=2, ) # Get the course module with modulestore().bulk_operations(course.id): staff_course_module = get_module_for_descriptor( staff_user, request, course, staff_field_data_cache, course.id, course=course ) # staff accessible chapters staff_chapters = staff_course_module.get_display_items() # find the passing grade for the course nonzero_cutoffs = [cutoff for cutoff in course.grade_cutoffs.values() if cutoff > 0] success_cutoff = min(nonzero_cutoffs) if nonzero_cutoffs else 0 # find the course progress progress = get_course_progress(student, course.id) # prepare a list of discussions participated by user discussions_participated = get_discussions_participated( request, course.id.to_deprecated_string(), student.id ) # get courseware summary with outer_atomic(): field_data_cache = grades.field_data_cache_for_grading(course, student) scores_client = ScoresClient.from_field_data_cache(field_data_cache) courseware_summary = grades.progress_summary( student, request, course, field_data_cache=field_data_cache, scores_client=scores_client ) section_grades = {} for section in courseware_summary: earned = 0 total = 0 for sub_section in section['sections']: earned += sub_section['section_total'].earned total += sub_section['section_total'].possible section_score = earned / total if earned > 0 and total > 0 else 0 section_grades[section['url_name']] = { 'earned': earned, 'total': total, 'css_class': ('text-red', 'text-green')[int(section_score >= 0.6)] if total > 0 else '' } # Check for content which needs to be completed # before the rest of the content is made available required_content = milestones_helpers.get_required_content(course, student) # Check for gated content gated_content = gating_api.get_gated_content(course, student) # The user may not actually have to complete the entrance exam, if one is required if not user_must_complete_entrance_exam(request, student, course): required_content = [content for content in required_content if not content == course.entrance_exam_id] # define inner function def create_module(descriptor): '''creates an XModule instance given a descriptor''' return get_module_for_descriptor( student, request, descriptor, field_data_cache, course.id, course=course ) with outer_atomic(): submissions_scores = sub_api.get_scores( course.id.to_deprecated_string(), anonymous_id_for_user(student, course.id) ) max_scores_cache = grades.MaxScoresCache.create_for_course(course) max_scores_cache.fetch_from_remote(field_data_cache.scorable_locations) sections = list() student_chapters = course_module.get_display_items() urlname_chapters = {} for student_chap in student_chapters: urlname_chapters.update({student_chap.url_name:student_chap}) final_chapters = OrderedDict() for chapter_index, chapter in enumerate(staff_chapters): fin_chap = urlname_chapters.get(chapter.url_name) if fin_chap: final_chapters.update({str(chapter_index+1):{'hidden': False, 'chapter':fin_chap}}) else: final_chapters.update({str(chapter_index+1):{'hidden':True}}) for section_index, chapter_info in final_chapters.items(): # Mark as hidden and Skip the current chapter if a hide flag is tripped if chapter_info['hidden']: sections.append({ 'hidden': True, 'week': "WEEK {week}: ".format(week=section_index), 'points': { 'total': 0, 'earned': 0, 'css_class': 'text-disabled' }, }) continue chapter = chapter_info['chapter'] # get the points section_points = section_grades.get(chapter.url_name, {}) units = list() for sequential in chapter.get_display_items(): # Set hidden status of the sequential if it is gated/hidden from the user hidden = ( gated_content and unicode(sequential.location) in gated_content or sequential.hide_from_toc ) if hidden: continue for index, unit in enumerate(sequential.get_display_items()): css_class = 'dark-gray' if unit.graded: total_excercises = 0 attempted_excercises = 0 unit_max_score = 0 unit_score = 0 for component in unit.get_display_items(): if component.category == 'problem': if component.graded: total_excercises += 1 attempted_excercises += is_attempted_internal( str(component.location), progress ) (correct, total) = grades.get_score( student, component, create_module, scores_client, submissions_scores, max_scores_cache, ) unit_max_score += total unit_score += correct if total_excercises: css_class = 'blue' if attempted_excercises == total_excercises: css_class = 'green' if unit_max_score and unit_score / unit_max_score < success_cutoff: css_class = 'red' position = index + 1 # For jumping to the unit directly unit_context = { 'display_name': unit.display_name_with_default_escaped, 'position': position, 'css_class': css_class, 'courseware_url': reverse( 'courseware_position', args=[ course.id, chapter.url_name, sequential.url_name, position ] ) } units.append(unit_context) competency = None if int(section_points.get('total')): competency = int(section_points.get('earned')) == int(section_points.get('total')) section_context = { 'display_name': chapter.display_name_with_default_escaped, 'url_name': chapter.url_name, 'hidden': False, 'rank': 1, 'competency': competency, 'points': { 'total': int(section_points.get('total')), 'earned': int(section_points.get('earned')), 'css_class': section_points.get('css_class') }, 'participation': discussions_participated.get(chapter.url_name), 'units': units, 'week': "WEEK {week}: ".format(week=section_index), } sections.append(section_context) return sections
def toc_for_course(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(request.user, request, course, field_data_cache, course.id) 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, request.user) # The user may not actually have to complete the entrance exam, if one is required if not user_must_complete_entrance_exam(request, 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) 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: sections.append({'display_name': section.display_name_with_default, 'url_name': section.url_name, 'format': section.format if section.format is not None else '', 'due': get_extended_due_date(section), 'active': active, 'graded': section.graded, }) toc_chapters.append({ 'display_name': chapter.display_name_with_default, 'url_name': chapter.url_name, 'sections': sections, 'active': chapter.url_name == active_chapter }) return toc_chapters
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: { 'chapters': [ {'display_name': name, 'url_name': url_name, 'sections': SECTIONS, 'active': bool}, ], 'previous_of_active_section': {..}, 'next_of_active_section': {..} } where SECTIONS is a list [ {'display_name': name, 'url_name': url_name, 'format': format, 'due': due, 'active' : bool, 'graded': bool}, ...] where previous_of_active_section and next_of_active_section have information on the next/previous sections of the active section. 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 descendants ''' 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, None, None toc_chapters = list() chapters = course_module.get_display_items() # Check for content which needs to be completed # before the rest of the content is made available required_content = milestones_helpers.get_required_content(course, user) # Check for gated content gated_content = gating_api.get_gated_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] previous_of_active_section, next_of_active_section = None, None last_processed_section, last_processed_chapter = None, None found_active_section = False for chapter in chapters: # Only show required content, if there is required content # chapter.hide_from_toc is read-only (bool) 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(): # skip the section if it is gated/hidden from the user if gated_content and unicode(section.location) in gated_content: continue if section.hide_from_toc: continue is_section_active = (chapter.url_name == active_chapter and section.url_name == active_section) if is_section_active: found_active_section = True section_context = { 'display_name': section.display_name_with_default_escaped, 'url_name': section.url_name, 'format': section.format if section.format is not None else '', 'due': section.due, 'active': is_section_active, 'graded': section.graded, } _add_timed_exam_info(user, course, section, section_context) # update next and previous of active section, if applicable if is_section_active: if last_processed_section: previous_of_active_section = last_processed_section.copy() previous_of_active_section['chapter_url_name'] = last_processed_chapter.url_name elif found_active_section and not next_of_active_section: next_of_active_section = section_context.copy() next_of_active_section['chapter_url_name'] = chapter.url_name sections.append(section_context) last_processed_section = section_context last_processed_chapter = chapter 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 { 'chapters': toc_chapters, 'previous_of_active_section': previous_of_active_section, 'next_of_active_section': next_of_active_section, }
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) 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_context = { 'display_name': section.display_name_with_default, 'url_name': section.url_name, 'format': section.format if section.format is not None else '', '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, }) sections.append(section_context) toc_chapters.append({ 'display_name': chapter.display_name_with_default, 'display_id': display_id, 'url_name': chapter.url_name, 'sections': sections, 'active': chapter.url_name == active_chapter }) return toc_chapters