def does_location_exist(course_id, location): """ Checks to see if a valid module exists at a given location (ie has not been deleted) course_id - string course id location - string location """ try: search.path_to_location(modulestore(), course_id, location) return True except ItemNotFoundError: #If the problem cannot be found at the location received from the grading controller server, it has been deleted by the course author. return False
def location(self): """ Blend "location" property into the resultset, so that the path to the found component can be shown within the UI """ # TODO: update whern changes to "cohorted-courseware" branch are merged in (course_key, chapter, section, position) = path_to_location(self.get_module_store(), self.get_usage_key()) def get_display_name(item_key): """ gets display name from object's key """ item = self.get_item(item_key) display_name = getattr(item, "display_name", None) return display_name if display_name else UNNAMED_MODULE_NAME def get_position_name(section, position): """ helper to fetch name corresponding to the position therein """ if position: section_item = self.get_item(course_key.make_usage_key("sequential", section)) if section_item.has_children and len(section_item.children) >= position: return get_display_name(section_item.children[position - 1]) return None location_description = [] if chapter: location_description.append(get_display_name(course_key.make_usage_key("chapter", chapter))) if section: location_description.append(get_display_name(course_key.make_usage_key("sequential", section))) if position: # We're only wanting to show the first vertical, so we use the # navigation_index function to display the same location to which one # would be sent if navigating location_description.append(get_position_name(section, navigation_index(position))) return location_description
def find_peer_grading_module(course): """ Given a course, finds the first peer grading module in it. @param course: A course object. @return: boolean found_module, string problem_url """ # Reverse the base course url. base_course_url = reverse("courses") found_module = False problem_url = "" # Get the peer grading modules currently in the course. Explicitly specify the course id to avoid issues with different runs. items = modulestore().get_items(course.id, category="peergrading") # See if any of the modules are centralized modules (ie display info from multiple problems) items = [i for i in items if not getattr(i, "use_for_single_location", True)] # Loop through all potential peer grading modules, and find the first one that has a path to it. for item in items: # Generate a url for the first module and redirect the user to it. try: problem_url_parts = search.path_to_location(modulestore(), item.location) except NoPathToItem: # In the case of nopathtoitem, the peer grading module that was found is in an invalid state, and # can no longer be accessed. Log an informational message, but this will not impact normal behavior. log.info( u"Invalid peer grading module location {0} in course {1}. This module may need to be removed.".format( item_location, course.id ) ) continue problem_url = generate_problem_url(problem_url_parts, base_course_url) found_module = True return found_module, problem_url
def find_peer_grading_module(course): """ Given a course, finds the first peer grading module in it. @param course: A course object. @return: boolean found_module, string problem_url """ # Reverse the base course url. base_course_url = reverse('courses') found_module = False problem_url = "" # Get the course id and split it. peer_grading_query = course.location.replace(category='peergrading', name=None) # Get the peer grading modules currently in the course. Explicitly specify the course id to avoid issues with different runs. items = modulestore().get_items(peer_grading_query, course_id=course.id) #See if any of the modules are centralized modules (ie display info from multiple problems) items = [i for i in items if not getattr(i, "use_for_single_location", True)] # Loop through all potential peer grading modules, and find the first one that has a path to it. for item in items: item_location = item.location # Generate a url for the first module and redirect the user to it. try: problem_url_parts = search.path_to_location(modulestore(), course.id, item_location) except NoPathToItem: # In the case of nopathtoitem, the peer grading module that was found is in an invalid state, and # can no longer be accessed. Log an informational message, but this will not impact normal behavior. log.info(u"Invalid peer grading module location {0} in course {1}. This module may need to be removed.".format(item_location, course.id)) continue problem_url = generate_problem_url(problem_url_parts, base_course_url) found_module = True return found_module, problem_url
def jump_to(_request, course_id, location): """ Show the page that contains a specific location. If the location is invalid or not in any class, return a 404. Otherwise, delegates to the index view to figure out whether this user has access, and what they should see. """ try: course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) usage_key = course_key.make_usage_key_from_deprecated_string(location) except InvalidKeyError: raise Http404(u"Invalid course_key or usage_key") try: (course_key, chapter, section, position) = path_to_location(modulestore(), usage_key) except ItemNotFoundError: raise Http404(u"No data at this location: {0}".format(usage_key)) except NoPathToItem: raise Http404(u"This location is not in any class: {0}".format(usage_key)) # choose the appropriate view (and provide the necessary args) based on the # args provided by the redirect. # Rely on index to do all error handling and access control. if chapter is None: return redirect('courseware', course_id=course_key.to_deprecated_string()) elif section is None: return redirect('courseware_chapter', course_id=course_key.to_deprecated_string(), chapter=chapter) elif position is None: return redirect('courseware_section', course_id=course_key.to_deprecated_string(), chapter=chapter, section=section) else: return redirect('courseware_position', course_id=course_key.to_deprecated_string(), chapter=chapter, section=section, position=position)
def _get_legacy_courseware_url( usage_key: UsageKey, request: Optional[HttpRequest] = None, ) -> str: """ Return the URL to Legacy (LMS-rendered) courseware content. Raises: * ItemNotFoundError if no data at the usage_key. * NoPathToItem if location not in any class. """ (course_key, chapter, section, vertical_unused, position, final_target_id) = path_to_location(modulestore(), usage_key, request) # choose the appropriate view (and provide the necessary args) based on the # args provided by the redirect. # Rely on index to do all error handling and access control. if chapter is None: redirect_url = reverse('courseware', args=(str(course_key), )) elif section is None: redirect_url = reverse('courseware_chapter', args=(str(course_key), chapter)) elif position is None: redirect_url = reverse('courseware_section', args=(str(course_key), chapter, section)) else: # Here we use the navigation_index from the position returned from # path_to_location - we can only navigate to the topmost vertical at the # moment redirect_url = reverse('courseware_position', args=(str(course_key), chapter, section, navigation_index(position))) redirect_url += "?{}".format( urlencode({'activate_block_id': str(final_target_id)})) return redirect_url
def location(self): """ Blend "location" property into the resultset, so that the path to the found component can be shown within the UI """ # TODO: update whern changes to "cohorted-courseware" branch are merged in (course_key, chapter, section, position) = path_to_location(self.get_module_store(), self.get_usage_key()) def get_display_name(category, item_id): """ helper to get display name from object """ item = self.get_item(course_key.make_usage_key(category, item_id)) return getattr(item, "display_name", None) def get_position_name(section, position): """ helper to fetch name corresponding to the position therein """ pos = int(position) section_item = self.get_item( course_key.make_usage_key("sequential", section)) if section_item.has_children and len(section_item.children) >= pos: item = self.get_item(section_item.children[pos - 1]) return getattr(item, "display_name", None) return None location_description = [] if chapter: location_description.append(get_display_name("chapter", chapter)) if section: location_description.append(get_display_name( "sequential", section)) if position: location_description.append(get_position_name(section, position)) return location_description
def add_problem_data(self, base_course_url): """ Add metadata to problems. @param base_course_url: the base url for any course. Can get with reverse('course') @return: A list of valid problems in the course and their appended data. """ # Our list of valid problems. valid_problems = [] if not self.success or not isinstance(self.problem_list, list): log.error("Called add_problem_data without a valid problem list" + self.course_error_ending) return valid_problems # Iterate through all of our problems and add data. for problem in self.problem_list: try: # Try to load the problem. problem_url_parts = search.path_to_location(modulestore(), self.course_id, problem['location']) except (ItemNotFoundError, NoPathToItem): # If the problem cannot be found at the location received from the grading controller server, # it has been deleted by the course author. We should not display it. error_message = "Could not find module for course {0} at location {1}".format(self.course_id, problem['location']) log.error(error_message) continue # Get the problem url in the courseware. problem_url = generate_problem_url(problem_url_parts, base_course_url) # Map the grader name from ORA to a human readable version. grader_type_display_name = GRADER_DISPLAY_NAMES.get(problem['grader_type'], "edX Assessment") problem['actual_url'] = problem_url problem['grader_type_display_name'] = grader_type_display_name valid_problems.append(problem) return valid_problems
def location(self): """ Blend "location" property into the resultset, so that the path to the found component can be shown within the UI """ # TODO: update whern changes to "cohorted-courseware" branch are merged in (course_key, chapter, section, position) = path_to_location(self.get_module_store(), self.get_usage_key()) def get_display_name(item_key): """ gets display name from object's key """ item = self.get_item(item_key) display_name = getattr(item, "display_name", None) return display_name if display_name else UNNAMED_MODULE_NAME def get_position_name(section, position): """ helper to fetch name corresponding to the position therein """ pos = int(position) section_item = self.get_item(course_key.make_usage_key("sequential", section)) if section_item.has_children and len(section_item.children) >= pos: return get_display_name(section_item.children[pos - 1]) return None location_description = [] if chapter: location_description.append(get_display_name(course_key.make_usage_key("chapter", chapter))) if section: location_description.append(get_display_name(course_key.make_usage_key("sequential", section))) if position: location_description.append(get_position_name(section, position)) return location_description
def find_peer_grading_module(course): """ Given a course, finds the first peer grading module in it. @param course: A course object. @return: boolean found_module, string problem_url """ #Reverse the base course url base_course_url = reverse('courses') found_module = False problem_url = "" #Get the course id and split it course_id_parts = course.id.split("/") log.info("COURSE ID PARTS") log.info(course_id_parts) #Get the peer grading modules currently in the course. Explicitly specify the course id to avoid issues with different runs. items = modulestore().get_items( ['i4x', course_id_parts[0], course_id_parts[1], 'peergrading', None], course_id=course.id) #See if any of the modules are centralized modules (ie display info from multiple problems) items = [ i for i in items if not getattr(i, "use_for_single_location", True) ] #Get the first one if len(items) > 0: item_location = items[0].location #Generate a url for the first module and redirect the user to it problem_url_parts = search.path_to_location(modulestore(), course.id, item_location) problem_url = generate_problem_url(problem_url_parts, base_course_url) found_module = True return found_module, problem_url
def jump_to(request, course_id, location): """ Show the page that contains a specific location. If the location is invalid or not in any class, return a 404. Otherwise, delegates to the index view to figure out whether this user has access, and what they should see. """ # Complain if the location isn't valid try: location = Location(location) except InvalidLocationError: raise Http404("Invalid location") # Complain if there's not data for this location try: (course_id, chapter, section, position) = path_to_location(modulestore(), course_id, location) except ItemNotFoundError: raise Http404("No data at this location: {0}".format(location)) except NoPathToItem: raise Http404("This location is not in any class: {0}".format(location)) # choose the appropriate view (and provide the necessary args) based on the # args provided by the redirect. # Rely on index to do all error handling and access control. if chapter is None: return redirect("courseware", course_id=course_id) elif section is None: return redirect("courseware_chapter", course_id=course_id, chapter=chapter) elif position is None: return redirect("courseware_section", course_id=course_id, chapter=chapter, section=section) else: return redirect("courseware_position", course_id=course_id, chapter=chapter, section=section, position=position)
def get(self, request, course_key_string, *args, **kwargs): """ Return response to a GET request. """ course_id = CourseKey.from_string(course_key_string) resp = { 'block_id': None, 'section_id': None, 'unit_id': None, } try: block_key = get_key_to_last_completed_block( request.user, course_id) path = path_to_location(modulestore(), block_key, request, full_path=True) resp['section_id'] = str(path[2]) resp['unit_id'] = str(path[3]) resp['block_id'] = str(block_key) except UnavailableCompletionData: pass return Response(resp)
def jump_to(request, course_id, location): """ Show the page that contains a specific location. If the location is invalid or not in any class, return a 404. Otherwise, delegates to the index view to figure out whether this user has access, and what they should see. """ try: course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) usage_key = course_key.make_usage_key_from_deprecated_string(location) except InvalidKeyError: raise Http404(u"Invalid course_key or usage_key") try: (course_key, chapter, section, position) = path_to_location(modulestore(), usage_key) except ItemNotFoundError: raise Http404(u"No data at this location: {0}".format(usage_key)) except NoPathToItem: raise Http404(u"This location is not in any class: {0}".format(usage_key)) # choose the appropriate view (and provide the necessary args) based on the # args provided by the redirect. # Rely on index to do all error handling and access control. if chapter is None: return redirect('courseware', course_id=course_key.to_deprecated_string()) elif section is None: return redirect('courseware_chapter', course_id=course_key.to_deprecated_string(), chapter=chapter) elif position is None: return redirect('courseware_section', course_id=course_key.to_deprecated_string(), chapter=chapter, section=section) else: return redirect('courseware_position', course_id=course_key.to_deprecated_string(), chapter=chapter, section=section, position=position)
def get(self, request, course_key_string, *args, **kwargs): # lint-amnesty, pylint: disable=unused-argument """ Return response to a GET request. """ course_id = CourseKey.from_string(course_key_string) resp = { 'block_id': None, 'section_id': None, 'unit_id': None, } try: block_key = get_key_to_last_completed_block( request.user, course_id) path = path_to_location(modulestore(), block_key, request, full_path=True) resp['section_id'] = str(path[2]) resp['unit_id'] = str(path[3]) resp['block_id'] = str(block_key) except (ItemNotFoundError, NoPathToItem, UnavailableCompletionData): pass # leaving all the IDs as None indicates a redirect to the first unit in the course, as a backup return Response(resp)
def jump_to(request, course_id, location): """ Show the page that contains a specific location. If the location is invalid or not in any class, return a 404. Otherwise, delegates to the index view to figure out whether this user has access, and what they should see. """ # Complain if the location isn't valid try: location = Location(location) except InvalidLocationError: raise Http404("Invalid location") # Complain if there's not data for this location try: (course_id, chapter, section, position) = path_to_location(modulestore(), course_id, location) except ItemNotFoundError: raise Http404(u"No data at this location: {0}".format(location)) except NoPathToItem: raise Http404(u"This location is not in any class: {0}".format(location)) # choose the appropriate view (and provide the necessary args) based on the # args provided by the redirect. # Rely on index to do all error handling and access control. if chapter is None: return redirect('courseware', course_id=course_id) elif section is None: return redirect('courseware_chapter', course_id=course_id, chapter=chapter) elif position is None: return redirect('courseware_section', course_id=course_id, chapter=chapter, section=section) else: return redirect('courseware_position', course_id=course_id, chapter=chapter, section=section, position=position)
def _get_new_courseware_url( usage_key: UsageKey, request: Optional[HttpRequest] = None, ) -> str: """ Return the URL to the "new" (Learning Micro-Frontend) experience for a given block. Raises: * ItemNotFoundError if no data at the `usage_key`. * NoPathToItem if we cannot build a path to the `usage_key`. """ course_key = usage_key.course_key.replace(version_guid=None, branch=None) path = path_to_location(modulestore(), usage_key, request, full_path=True) if len(path) <= 1: # Course-run-level block: # We have no Sequence or Unit to return. sequence_key, unit_key = None, None elif len(path) == 2: # Section-level (ie chapter) block: # The Section is the Sequence. We have no Unit to return. sequence_key, unit_key = path[1], None elif len(path) == 3: # Subsection-level block: # The Subsection is the Sequence. We still have no Unit to return. sequence_key, unit_key = path[2], None else: # Unit-level (or lower) block: # The Subsection is the Sequence, and the next level down is the Unit. sequence_key, unit_key = path[2], path[3] return make_learning_mfe_courseware_url( course_key=course_key, sequence_key=sequence_key, unit_key=unit_key, )
def find_peer_grading_module(course): """ Given a course, finds the first peer grading module in it. @param course: A course object. @return: boolean found_module, string problem_url """ #Reverse the base course url base_course_url = reverse('courses') found_module = False problem_url = "" #Get the course id and split it course_id_parts = course.id.split("/") log.info("COURSE ID PARTS") log.info(course_id_parts) #Get the peer grading modules currently in the course. Explicitly specify the course id to avoid issues with different runs. items = modulestore().get_items(['i4x', course_id_parts[0], course_id_parts[1], 'peergrading', None], course_id=course.id) #See if any of the modules are centralized modules (ie display info from multiple problems) items = [i for i in items if not getattr(i, "use_for_single_location", True)] #Get the first one if len(items) > 0: item_location = items[0].location #Generate a url for the first module and redirect the user to it problem_url_parts = search.path_to_location(modulestore(), course.id, item_location) problem_url = generate_problem_url(problem_url_parts, base_course_url) found_module = True return found_module, problem_url
def test_path_to_location_for_orphan_chapter(self, module_store): r""" Make sure that path_to_location works with a component having multiple chapter parents, from which one of them is orphan course | chapter chapter | | vertical vertical \ / html """ # Get a course with orphan modules course = self.create_course_with_orphans(module_store) orphan_chapter = self.store.get_item(BlockUsageLocator(course.id, 'chapter', 'OrphanChapter')) chapter1 = self.store.get_item(BlockUsageLocator(course.id, 'chapter', 'Chapter1')) vertical1 = self.store.get_item(BlockUsageLocator(course.id, 'vertical', 'Vertical1')) # Verify `OrhanChapter` is an orphan self.assertIn(orphan_chapter.location, self.store.get_orphans(course.id)) # Create a vertical (`Vertical0`) in orphan chapter (`OrphanChapter`). # OrphanChapter -> Vertical0 vertical0 = self.store.create_child(self.user.id, orphan_chapter.location, 'vertical', "Vertical0") self.store.publish(vertical0.location, self.user.id) # Create a component in `Vertical0` # OrphanChapter -> Vertical0 -> Html html = self.store.create_child(self.user.id, vertical0.location, 'html', "HTML0") self.store.publish(html.location, self.user.id) # Verify chapter1 is parent of vertical1. vertical1_parent = self.store.get_parent_location(vertical1.location) self.assertEqual(unicode(vertical1_parent), unicode(chapter1.location)) # Make `Vertical1` the parent of `HTML0`. So `HTML0` will have to parents (`Vertical0` & `Vertical1`) vertical1.children.append(html.location) self.store.update_item(vertical1, self.user.id) # Get parent location & verify its either of the two verticals. As both parents are non-orphan, # alphabetically least is returned html_parent = self.store.get_parent_location(html.location) self.assertEquals(unicode(html_parent), unicode(vertical1.location)) # verify path_to_location returns a expected path path = path_to_location(self.store, html.location) expected_path = ( course.id, chapter1.location.block_id, vertical1.location.block_id, html.location.block_id, "", path[-1] ) self.assertIsNotNone(path) self.assertEqual(len(path), 6) self.assertEqual(path, expected_path)
def test_path_to_location_for_orphan_chapter(self, module_store): r""" Make sure that path_to_location works with a component having multiple chapter parents, from which one of them is orphan course | chapter chapter | | vertical vertical \ / html """ # Get a course with orphan modules course = self.create_course_with_orphans(module_store) orphan_chapter = self.store.get_item(BlockUsageLocator(course.id, 'chapter', 'OrphanChapter')) chapter1 = self.store.get_item(BlockUsageLocator(course.id, 'chapter', 'Chapter1')) vertical1 = self.store.get_item(BlockUsageLocator(course.id, 'vertical', 'Vertical1')) # Verify `OrhanChapter` is an orphan self.assertIn(orphan_chapter.location, self.store.get_orphans(course.id)) # Create a vertical (`Vertical0`) in orphan chapter (`OrphanChapter`). # OrphanChapter -> Vertical0 vertical0 = self.store.create_child(self.user.id, orphan_chapter.location, 'vertical', "Vertical0") self.store.publish(vertical0.location, self.user.id) # Create a component in `Vertical0` # OrphanChapter -> Vertical0 -> Html html = self.store.create_child(self.user.id, vertical0.location, 'html', "HTML0") self.store.publish(html.location, self.user.id) # Verify chapter1 is parent of vertical1. vertical1_parent = self.store.get_parent_location(vertical1.location) self.assertEqual(six.text_type(vertical1_parent), six.text_type(chapter1.location)) # Make `Vertical1` the parent of `HTML0`. So `HTML0` will have to parents (`Vertical0` & `Vertical1`) vertical1.children.append(html.location) self.store.update_item(vertical1, self.user.id) # Get parent location & verify its either of the two verticals. As both parents are non-orphan, # alphabetically least is returned html_parent = self.store.get_parent_location(html.location) self.assertEquals(six.text_type(html_parent), six.text_type(vertical1.location)) # verify path_to_location returns a expected path path = path_to_location(self.store, html.location) expected_path = ( course.id, chapter1.location.block_id, vertical1.location.block_id, html.location.block_id, "", path[-1] ) self.assertIsNotNone(path) self.assertEqual(len(path), 6) self.assertEqual(path, expected_path)
def get_chapter_from_location(usage_id, course_key): """ hawthorn backend """ usage_key = UsageKey.from_string( unquote_slashes(usage_id)).map_into_course(course_key) if usage_key: path = search.path_to_location(modulestore(), usage_key) chapter = path[ 1] # 1 is the position where path_to_location returns the chapter return chapter return None
def does_location_exist(usage_key): """ Checks to see if a valid module exists at a given location (ie has not been deleted) course_id - string course id location - string location """ try: search.path_to_location(modulestore(), usage_key) return True except ItemNotFoundError: # If the problem cannot be found at the location received from the grading controller server, # it has been deleted by the course author. return False except NoPathToItem: # If the problem can be found, but there is no path to it, then we assume it is a draft. # Log a warning in any case. log.warn("Got an unexpected NoPathToItem error in staff grading with location %s. " "This is ok if it is a draft; ensure that the location is valid.", usage_key) return False
def does_location_exist(course_id, location): """ Checks to see if a valid module exists at a given location (ie has not been deleted) course_id - string course id location - string location """ try: search.path_to_location(modulestore(), course_id, location) return True except ItemNotFoundError: # If the problem cannot be found at the location received from the grading controller server, # it has been deleted by the course author. return False except NoPathToItem: # If the problem can be found, but there is no path to it, then we assume it is a draft. # Log a warning if the problem is not a draft (location does not end in "draft"). if not location.endswith("draft"): log.warn(("Got an unexpected NoPathToItem error in staff grading with a non-draft location {0}. " "Ensure that the location is valid.").format(location)) return False
def does_location_exist(usage_key): """ Checks to see if a valid module exists at a given location (ie has not been deleted) course_id - string course id location - string location """ try: search.path_to_location(modulestore(), usage_key) return True except ItemNotFoundError: # If the problem cannot be found at the location received from the grading controller server, # it has been deleted by the course author. return False except NoPathToItem: # If the problem can be found, but there is no path to it, then we assume it is a draft. # Log a warning in any case. log.warn( "Got an unexpected NoPathToItem error in staff grading with location %s. " "This is ok if it is a draft; ensure that the location is valid.", usage_key) return False
def test_path_to_location_for_orphan_vertical(self, module_store): r""" Make sure that path_to_location works with a component having multiple vertical parents, from which one of them is orphan. course | chapter | vertical vertical \ / html """ # Get a course with orphan modules course = self.create_course_with_orphans(module_store) # Fetch the required course components. vertical1 = self.store.get_item( BlockUsageLocator(course.id, 'vertical', 'Vertical1')) chapter1 = self.store.get_item( BlockUsageLocator(course.id, 'chapter', 'Chapter1')) orphan_vertical = self.store.get_item( BlockUsageLocator(course.id, 'vertical', 'OrphanVert')) multi_parent_html = self.store.get_item( BlockUsageLocator(course.id, 'html', 'multi_parent_html')) # Verify `OrphanVert` is an orphan self.assertIn(orphan_vertical.location, self.store.get_orphans(course.id)) # Verify `multi_parent_html` is child of both `Vertical1` and `OrphanVert` self.assertIn(multi_parent_html.location, orphan_vertical.children) self.assertIn(multi_parent_html.location, vertical1.children) # HTML component has `vertical1` as its parent. html_parent = self.store.get_parent_location( multi_parent_html.location) self.assertNotEqual(six.text_type(html_parent), six.text_type(orphan_vertical.location)) self.assertEqual(six.text_type(html_parent), six.text_type(vertical1.location)) # Get path of the `multi_parent_html` & verify path_to_location returns a expected path path = path_to_location(self.store, multi_parent_html.location) expected_path = (course.id, chapter1.location.block_id, vertical1.location.block_id, multi_parent_html.location.block_id, "", path[-1]) self.assertIsNotNone(path) self.assertEqual(len(path), 6) self.assertEqual(path, expected_path)
def check_path_to_location(modulestore): """ Make sure that path_to_location works: should be passed a modulestore with the toy and simple courses loaded. """ course_id = SlashSeparatedCourseKey("edX", "toy", "2012_Fall") should_work = ( (course_id.make_usage_key('video', 'Welcome'), (course_id, "Overview", "Welcome", None)), (course_id.make_usage_key('chapter', 'Overview'), (course_id, "Overview", None, None)), ) for location, expected in should_work: assert_equals(path_to_location(modulestore, location), expected) not_found = ( course_id.make_usage_key('video', 'WelcomeX'), course_id.make_usage_key('course', 'NotHome'), ) for location in not_found: with assert_raises(ItemNotFoundError): path_to_location(modulestore, location)
def get_redirect_url(course_key, usage_key, child=None): """ Returns the redirect url back to courseware Args: course_id(str): Course Id string location(str): The location id of course component child(str): Optional child parameter to pass to the URL Raises: ItemNotFoundError if no data at the location or NoPathToItem if location not in any class Returns: Redirect url string """ ( course_key, chapter, section, vertical_unused, position, final_target_id ) = path_to_location(modulestore(), usage_key) # choose the appropriate view (and provide the necessary args) based on the # args provided by the redirect. # Rely on index to do all error handling and access control. if chapter is None: redirect_url = reverse('courseware', args=(unicode(course_key), )) elif section is None: redirect_url = reverse('courseware_chapter', args=(unicode(course_key), chapter)) elif position is None: redirect_url = reverse( 'courseware_section', args=(unicode(course_key), chapter, section) ) else: # Here we use the navigation_index from the position returned from # path_to_location - we can only navigate to the topmost vertical at the # moment redirect_url = reverse( 'courseware_position', args=(unicode(course_key), chapter, section, navigation_index(position)) ) redirect_url += "?{}".format(urlencode({'activate_block_id': unicode(final_target_id)})) if child: redirect_url += "&child={}".format(child) return redirect_url
def test_path_to_location_for_orphan_vertical(self, module_store): r""" Make sure that path_to_location works with a component having multiple vertical parents, from which one of them is orphan. course | chapter | vertical vertical \ / html """ # Get a course with orphan modules course = self.create_course_with_orphans(module_store) # Fetch the required course components. vertical1 = self.store.get_item(BlockUsageLocator(course.id, 'vertical', 'Vertical1')) chapter1 = self.store.get_item(BlockUsageLocator(course.id, 'chapter', 'Chapter1')) orphan_vertical = self.store.get_item(BlockUsageLocator(course.id, 'vertical', 'OrphanVert')) multi_parent_html = self.store.get_item(BlockUsageLocator(course.id, 'html', 'multi_parent_html')) # Verify `OrphanVert` is an orphan self.assertIn(orphan_vertical.location, self.store.get_orphans(course.id)) # Verify `multi_parent_html` is child of both `Vertical1` and `OrphanVert` self.assertIn(multi_parent_html.location, orphan_vertical.children) self.assertIn(multi_parent_html.location, vertical1.children) # HTML component has `vertical1` as its parent. html_parent = self.store.get_parent_location(multi_parent_html.location) self.assertNotEqual(unicode(html_parent), unicode(orphan_vertical.location)) self.assertEqual(unicode(html_parent), unicode(vertical1.location)) # Get path of the `multi_parent_html` & verify path_to_location returns a expected path path = path_to_location(self.store, multi_parent_html.location) expected_path = ( course.id, chapter1.location.block_id, vertical1.location.block_id, multi_parent_html.location.block_id, "", path[-1] ) self.assertIsNotNone(path) self.assertEqual(len(path), 6) self.assertEqual(path, expected_path)
def get_redirect_url(course_key, usage_key, child=None): """ Returns the redirect url back to courseware Args: course_id(str): Course Id string location(str): The location id of course component child(str): Optional child parameter to pass to the URL Raises: ItemNotFoundError if no data at the location or NoPathToItem if location not in any class Returns: Redirect url string """ (course_key, chapter, section, vertical_unused, position, final_target_id) = path_to_location(modulestore(), usage_key) # choose the appropriate view (and provide the necessary args) based on the # args provided by the redirect. # Rely on index to do all error handling and access control. if chapter is None: redirect_url = reverse('courseware', args=(unicode(course_key), )) elif section is None: redirect_url = reverse('courseware_chapter', args=(unicode(course_key), chapter)) elif position is None: redirect_url = reverse('courseware_section', args=(unicode(course_key), chapter, section)) else: # Here we use the navigation_index from the position returned from # path_to_location - we can only navigate to the topmost vertical at the # moment redirect_url = reverse('courseware_position', args=(unicode(course_key), chapter, section, navigation_index(position))) redirect_url += "?{}".format( urlencode({'activate_block_id': unicode(final_target_id)})) if child: redirect_url += "&child={}".format(child) return redirect_url
def get_redirect_url(course_key, usage_key, unified_course_view=False): """ Returns the redirect url back to courseware Args: course_id(str): Course Id string location(str): The location id of course component unified_course_view (bool): temporary parameter while this feature is behind a waffle flag. Is the unified_course_view waffle flag on? Raises: ItemNotFoundError if no data at the location or NoPathToItem if location not in any class Returns: Redirect url string """ if usage_key.block_type == 'course' and unified_course_view: return reverse('openedx.course_experience.course_home', args=[unicode(course_key)]) (course_key, chapter, section, vertical_unused, position, final_target_id) = path_to_location(modulestore(), usage_key) # choose the appropriate view (and provide the necessary args) based on the # args provided by the redirect. # Rely on index to do all error handling and access control. if chapter is None: redirect_url = reverse('courseware', args=(unicode(course_key), )) elif section is None: redirect_url = reverse('courseware_chapter', args=(unicode(course_key), chapter)) elif position is None: redirect_url = reverse('courseware_section', args=(unicode(course_key), chapter, section)) else: # Here we use the navigation_index from the position returned from # path_to_location - we can only navigate to the topmost vertical at the # moment redirect_url = reverse('courseware_position', args=(unicode(course_key), chapter, section, navigation_index(position))) redirect_url += "?{}".format( urlencode({'activate_block_id': unicode(final_target_id)})) return redirect_url
def check_path_to_location(modulestore): '''Make sure that path_to_location works: should be passed a modulestore with the toy and simple courses loaded.''' should_work = ( ("i4x://edX/toy/video/Welcome", ("edX/toy/2012_Fall", "Overview", "Welcome", None)), ("i4x://edX/toy/chapter/Overview", ("edX/toy/2012_Fall", "Overview", None, None)), ) course_id = "edX/toy/2012_Fall" for location, expected in should_work: assert_equals(path_to_location(modulestore, course_id, location), expected) not_found = ( "i4x://edX/toy/video/WelcomeX", "i4x://edX/toy/course/NotHome" ) for location in not_found: assert_raises(ItemNotFoundError, path_to_location, modulestore, course_id, location)
def get_legacy_courseware_url(course_key, usage_key, request=None): """ Return a str with the URL for the specified legacy (LMS-rendered) coursweare content. Args: course_id(str): Course Id string usage_key(str): The location id of course component Raises: ItemNotFoundError if no data at the location or NoPathToItem if location not in any class Returns: Redirect url string """ (course_key, chapter, section, vertical_unused, position, final_target_id) = path_to_location(modulestore(), usage_key, request) # choose the appropriate view (and provide the necessary args) based on the # args provided by the redirect. # Rely on index to do all error handling and access control. if chapter is None: redirect_url = reverse('courseware', args=(six.text_type(course_key), )) elif section is None: redirect_url = reverse('courseware_chapter', args=(six.text_type(course_key), chapter)) elif position is None: redirect_url = reverse('courseware_section', args=(six.text_type(course_key), chapter, section)) else: # Here we use the navigation_index from the position returned from # path_to_location - we can only navigate to the topmost vertical at the # moment redirect_url = reverse('courseware_position', args=(six.text_type(course_key), chapter, section, navigation_index(position))) redirect_url += "?{}".format( urlencode({'activate_block_id': six.text_type(final_target_id)})) return redirect_url
def get_path(usage_key): """ Returns data for the path to the block in the course graph. Note: In case of multiple paths to the block from the course root, this function returns a path arbitrarily but consistently, depending on the modulestore. In the future, we may want to extend it to check which of the paths, the user has access to and return its data. Arguments: block (XBlock): The block whose path is required. Returns: list of PathItems """ with modulestore().bulk_operations(usage_key.course_key): try: path = search.path_to_location(modulestore(), usage_key, full_path=True) except ItemNotFoundError: log.error(u'Block with usage_key: %s not found.', usage_key) return [] except NoPathToItem: log.error(u'No path to block with usage_key: %s.', usage_key) return [] path_data = [] for ancestor_usage_key in path: if ancestor_usage_key != usage_key and ancestor_usage_key.block_type != 'course': # pylint: disable=no-member try: block = modulestore().get_item(ancestor_usage_key) except ItemNotFoundError: return [] # No valid path can be found. path_data.append( PathItem(usage_key=block.location, display_name=block.display_name)) return path_data
def get_path(usage_key): """ Returns data for the path to the block in the course graph. Note: In case of multiple paths to the block from the course root, this function returns a path arbitrarily but consistently, depending on the modulestore. In the future, we may want to extend it to check which of the paths, the user has access to and return its data. Arguments: block (XBlock): The block whose path is required. Returns: list of PathItems """ with modulestore().bulk_operations(usage_key.course_key): try: path = search.path_to_location(modulestore(), usage_key, full_path=True) except ItemNotFoundError: log.error(u'Block with usage_key: %s not found.', usage_key) return [] except NoPathToItem: log.error(u'No path to block with usage_key: %s.', usage_key) return [] path_data = [] for ancestor_usage_key in path: if ancestor_usage_key != usage_key and ancestor_usage_key.block_type != 'course': # pylint: disable=no-member try: block = modulestore().get_item(ancestor_usage_key) except ItemNotFoundError: return [] # No valid path can be found. path_data.append( PathItem(usage_key=block.location, display_name=block.display_name) ) return path_data
def _get_sequence_and_unit_keys( usage_key: UsageKey, request: Optional[HttpRequest] = None, ) -> Tuple[Optional[UsageKey], Optional[UsageKey]]: """ Find the sequence and unit containg a block within a course run. Performance consideration: Currently, this function incurs a modulestore query. Raises: * ItemNotFoundError if no data at the `usage_key`. * NoPathToItem if we cannot build a path to the `usage_key`. Returns: (sequence_key|None, unit_key|None) * sequence_key points to a Section (ie chapter) or Subsection (ie sequential). * unit_key points to Unit (ie vertical). Either of these may be None if we are above that level in the course hierarchy. For example, if `usage_key` points to a Subsection, then unit_key will be None. """ path = path_to_location(modulestore(), usage_key, request, full_path=True) if len(path) <= 1: # Course-run-level block: # We have no Sequence or Unit to return. return None, None elif len(path) == 2: # Section-level (ie chapter) block: # The Section is the Sequence. We have no Unit to return. return path[1], None elif len(path) == 3: # Subsection-level block: # The Subsection is the Sequence. We still have no Unit to return. return path[2], None else: # Unit-level (or lower) block: # The Subsection is the Sequence, and the next level down is the Unit. return path[2], path[3]
def staff_notification(): """ To send ORA statactics to staff users of course """ try: course_data = CourseOverview.objects.all() for cid in course_data: assessment_data = AssessmentWorkflow.objects.filter( course_id=cid.id) item_data = [] for sid in assessment_data: if not bool(staff.get_latest_staff_assessment(sid.submission_uuid)): if sid.item_id not in item_data: item_data.append(sid.item_id) # item_data = AssessmentWorkflow.objects.filter( # course_id=cid.id).values_list('item_id', flat=True) # item_data = list(set(item_data)) for iid in item_data: statistics = api.get_status_counts(cid.id, iid, ["staff", "peer", "done", "waiting"]) modified_statistics = dict() for stat in statistics: modified_statistics[stat.get('status')] = stat.get('count') statistics = modified_statistics if (( statistics['staff'] == 0 ) and ( statistics['peer'] == 0 ) and ( statistics['waiting'] == 0 )): return course_struct = None chapter_name = None try: course_struct = CourseStructure.objects.get(course_id=cid.id) except Exception as e: print "Unexpected error {0}".format(e) if course_struct: block = json.loads(course_struct.structure_json)['blocks'][iid] chapter_name = block['display_name'] staff_users = CourseAccessRole.objects.filter(course_id=cid.id, role='staff') try: usage_key = UsageKey.from_string(iid).replace(course_key=cid.id) (course_key, chapter, section, vertical_unused, position, final_target_id ) = path_to_location(modulestore(), usage_key) current_site_domain = 'http://{0}'.format(settings.SITE_NAME) courseware_url = current_site_domain+"/courses/"+str(cid.id)+"/courseware/"+chapter+"/"+section for u in staff_users: html_message = render_to_string('peer_grading/ora_report.html', {'status_counts': modified_statistics, 'course': cid.display_name, 'chapter_name' : chapter_name, 'user': u.user, 'courseware_url':courseware_url }) email = EmailMessage( "LYNX Online-Training: Neue Aufgaben zur Bewertung", html_message, to=[u.user.email]) email.send() TASK_LOG.info("----------Email message sent to course admins----------") except Exception as e: TASK_LOG.info("----------Inner Exception while sending staff notification----------") import traceback print traceback.format_exc() print e,"Inner Exception<-------" pass except Exception as e: import traceback print traceback.format_exc() print e,"<--- Error"
def student_problem_list(request, course_id): ''' Show a student problem list to a student. Fetch the list from the grading controller server, get some metadata, and then show it to the student. ''' course = get_course_with_access(request.user, course_id, 'load') student_id = unique_id_for_user(request.user) # call problem list service success = False error_text = "" problem_list = [] base_course_url = reverse('courses') list_to_remove = [] try: #Get list of all open ended problems that the grading server knows about problem_list_json = controller_qs.get_grading_status_list(course_id, unique_id_for_user(request.user)) problem_list_dict = json.loads(problem_list_json) success = problem_list_dict['success'] if 'error' in problem_list_dict: error_text = problem_list_dict['error'] problem_list = [] else: problem_list = problem_list_dict['problem_list'] #A list of problems to remove (problems that can't be found in the course) for i in xrange(0, len(problem_list)): try: #Try to load each problem in the courseware to get links to them problem_url_parts = search.path_to_location(modulestore(), course.id, problem_list[i]['location']) except ItemNotFoundError: #If the problem cannot be found at the location received from the grading controller server, it has been deleted by the course author. #Continue with the rest of the location to construct the list error_message = "Could not find module for course {0} at location {1}".format(course.id, problem_list[i][ 'location']) log.error(error_message) #Mark the problem for removal from the list list_to_remove.append(i) continue problem_url = generate_problem_url(problem_url_parts, base_course_url) problem_list[i].update({'actual_url': problem_url}) eta_available = problem_list[i]['eta_available'] if isinstance(eta_available, basestring): eta_available = (eta_available.lower() == "true") eta_string = "N/A" if eta_available: try: eta_string = convert_seconds_to_human_readable(int(problem_list[i]['eta'])) except: #This is a student_facing_error eta_string = "Error getting ETA." problem_list[i].update({'eta_string': eta_string}) except GradingServiceError: #This is a student_facing_error error_text = STUDENT_ERROR_MESSAGE #This is a dev facing error log.error("Problem contacting open ended grading service.") success = False # catch error if if the json loads fails except ValueError: #This is a student facing error error_text = STUDENT_ERROR_MESSAGE #This is a dev_facing_error log.error("Problem with results from external grading service for open ended.") success = False #Remove problems that cannot be found in the courseware from the list problem_list = [problem_list[i] for i in xrange(0, len(problem_list)) if i not in list_to_remove] ajax_url = _reverse_with_slash('open_ended_problems', course_id) return render_to_response('open_ended_problems/open_ended_problems.html', { 'course': course, 'course_id': course_id, 'ajax_url': ajax_url, 'success': success, 'problem_list': problem_list, 'error_text': error_text, # Checked above 'staff_access': False, })
def find_peer_grading_module(course, user): """ Given a course, finds the first peer grading module in it. @param course: A course object. @return: boolean found_module, string problem_url """ # Reverse the base course url. base_course_url = reverse('courses') found_module = False problem_url = "" print "user: "******"Userid: ", user.id # Get the peer grading modules currently in the course. Explicitly specify the course id to avoid issues with different runs. items = modulestore().get_items(course.id, category='peergrading') # See if any of the modules are centralized modules (ie display info from multiple problems) items = [i for i in items if not getattr(i, "use_for_single_location", True)] # Loop through all potential peer grading modules, and find the first one that has a path to it. # Faz i nesmo esquema do conteudo do acordion do LMS i = 0 while i < len(items): try: problem_url_parts = search.path_to_location(modulestore(), items[i].location) except NoPathToItem: # In the case of nopathtoitem, the peer grading module that was found is in an invalid state, and # can no longer be accessed. Log an informational message, but this will not impact normal behavior. log.info(u"Invalid peer grading module location %s in course %s. This module may need to be removed.", items[i].location, course.id) continue urlSection = generate_problem_url(problem_url_parts, base_course_url).split('courseware')[1].split('/')[1] print "Problem url parts: ", urlSection need = NeedThread(user, course) if need: mythread = CadVersao(urlSection, user) mythread.start() mythread.join() imprimir = mythread.getResult() else: imprimir = VerABprint(urlSection, user) if imprimir == False: del items[i] else: i+=1 problem_url = generate_problem_url(problem_url_parts, base_course_url) for item in items: # Generate a url for the first module and redirect the user to it. try: problem_url_parts = search.path_to_location(modulestore(), item.location) except NoPathToItem: # In the case of nopathtoitem, the peer grading module that was found is in an invalid state, and # can no longer be accessed. Log an informational message, but this will not impact normal behavior. log.info(u"Invalid peer grading module location %s in course %s. This module may need to be removed.", item.location, course.id) continue problem_url = generate_problem_url(problem_url_parts, base_course_url) found_module = True return found_module, problem_url
def student_problem_list(request, course_id): ''' Show a student problem list to a student. Fetch the list from the grading controller server, get some metadata, and then show it to the student. ''' course = get_course_with_access(request.user, course_id, 'load') student_id = unique_id_for_user(request.user) # call problem list service success = False error_text = "" problem_list = [] base_course_url = reverse('courses') list_to_remove = [] try: #Get list of all open ended problems that the grading server knows about problem_list_json = controller_qs.get_grading_status_list( course_id, unique_id_for_user(request.user)) problem_list_dict = json.loads(problem_list_json) success = problem_list_dict['success'] if 'error' in problem_list_dict: error_text = problem_list_dict['error'] problem_list = [] else: problem_list = problem_list_dict['problem_list'] #A list of problems to remove (problems that can't be found in the course) for i in xrange(0, len(problem_list)): try: #Try to load each problem in the courseware to get links to them problem_url_parts = search.path_to_location( modulestore(), course.id, problem_list[i]['location']) except ItemNotFoundError: #If the problem cannot be found at the location received from the grading controller server, it has been deleted by the course author. #Continue with the rest of the location to construct the list error_message = "Could not find module for course {0} at location {1}".format( course.id, problem_list[i]['location']) log.error(error_message) #Mark the problem for removal from the list list_to_remove.append(i) continue problem_url = generate_problem_url(problem_url_parts, base_course_url) problem_list[i].update({'actual_url': problem_url}) eta_available = problem_list[i]['eta_available'] if isinstance(eta_available, basestring): eta_available = (eta_available.lower() == "true") eta_string = "N/A" if eta_available: try: eta_string = convert_seconds_to_human_readable( int(problem_list[i]['eta'])) except: #This is a student_facing_error eta_string = "Error getting ETA." problem_list[i].update({'eta_string': eta_string}) except GradingServiceError: #This is a student_facing_error error_text = STUDENT_ERROR_MESSAGE #This is a dev facing error log.error("Problem contacting open ended grading service.") success = False # catch error if if the json loads fails except ValueError: #This is a student facing error error_text = STUDENT_ERROR_MESSAGE #This is a dev_facing_error log.error( "Problem with results from external grading service for open ended." ) success = False #Remove problems that cannot be found in the courseware from the list problem_list = [ problem_list[i] for i in xrange(0, len(problem_list)) if i not in list_to_remove ] ajax_url = _reverse_with_slash('open_ended_problems', course_id) return render_to_response( 'open_ended_problems/open_ended_problems.html', { 'course': course, 'course_id': course_id, 'ajax_url': ajax_url, 'success': success, 'problem_list': problem_list, 'error_text': error_text, # Checked above 'staff_access': False, })