def post(self, request): """Handle all actions from courses view""" if not request.user.is_staff: raise Http404 action = request.POST.get('action', '') track.views.server_track(request, action, {}, page='courses_sysdashboard') courses = {course.id: course for course in self.get_courses()} if action == 'add_course': gitloc = request.POST.get('repo_location', '').strip().replace(' ', '').replace(';', '') branch = request.POST.get('repo_branch', '').strip().replace(' ', '').replace(';', '') self.msg += self.get_course_from_git(gitloc, branch) elif action == 'del_course': course_id = request.POST.get('course_id', '').strip() course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course_found = False if course_key in courses: course_found = True course = courses[course_key] else: try: course = get_course_by_id(course_key) course_found = True except Exception, err: # pylint: disable=broad-except self.msg += _( 'Error - cannot get course with ID {0}<br/><pre>{1}</pre>' ).format( course_key, escape(str(err)) ) is_xml_course = (modulestore().get_modulestore_type(course_key) == XML_MODULESTORE_TYPE) if course_found and is_xml_course: cdir = course.data_dir self.def_ms.courses.pop(cdir) # now move the directory (don't actually delete it) new_dir = "{course_dir}_deleted_{timestamp}".format( course_dir=cdir, timestamp=int(time.time()) ) os.rename(settings.DATA_DIR / cdir, settings.DATA_DIR / new_dir) self.msg += (u"<font color='red'>Deleted " u"{0} = {1} ({2})</font>".format( cdir, course.id, course.display_name)) elif course_found and not is_xml_course: # delete course that is stored with mongodb backend content_store = contentstore() commit = True delete_course(self.def_ms, content_store, course.id, commit) # don't delete user permission groups, though self.msg += \ u"<font color='red'>{0} {1} = {2} ({3})</font>".format( _('Deleted'), course.location.to_deprecated_string(), course.id.to_deprecated_string(), course.display_name)
def test_delete_course(self): module_store = modulestore("direct") import_from_xml(module_store, "common/test/data/", ["full"]) content_store = contentstore() location = CourseDescriptor.id_to_location("edX/full/6.002_Spring_2012") delete_course(module_store, content_store, location, commit=True) items = module_store.get_items(Location(["i4x", "edX", "full", "vertical", None])) self.assertEqual(len(items), 0)
def test_delete_course(self): module_store = modulestore('direct') import_from_xml(module_store, 'common/test/data/', ['full']) content_store = contentstore() location = CourseDescriptor.id_to_location('edX/full/6.002_Spring_2012') delete_course(module_store, content_store, location, commit=True) items = module_store.get_items(Location(['i4x', 'edX', 'full', 'vertical', None])) self.assertEqual(len(items), 0)
def post(self, request): """Handle all actions from courses view""" if not request.user.is_staff: raise Http404 action = request.POST.get("action", "") track.views.server_track(request, action, {}, page="courses_sysdashboard") courses = self.get_courses() if action == "add_course": gitloc = request.POST.get("repo_location", "").strip().replace(" ", "").replace(";", "") branch = request.POST.get("repo_branch", "").strip().replace(" ", "").replace(";", "") self.msg += self.get_course_from_git(gitloc, branch) elif action == "del_course": course_id = request.POST.get("course_id", "").strip() course_found = False if course_id in courses: course_found = True course = courses[course_id] else: try: course = get_course_by_id(course_id) course_found = True except Exception, err: # pylint: disable=broad-except self.msg += _("Error - cannot get course with ID " "{0}<br/><pre>{1}</pre>").format( course_id, escape(str(err)) ) is_xml_course = modulestore().get_modulestore_type(course_id) == XML_MODULESTORE_TYPE if course_found and is_xml_course: cdir = course.data_dir self.def_ms.courses.pop(cdir) # now move the directory (don't actually delete it) new_dir = "{course_dir}_deleted_{timestamp}".format(course_dir=cdir, timestamp=int(time.time())) os.rename(settings.DATA_DIR / cdir, settings.DATA_DIR / new_dir) self.msg += u"<font color='red'>Deleted " u"{0} = {1} ({2})</font>".format( cdir, course.id, course.display_name ) elif course_found and not is_xml_course: # delete course that is stored with mongodb backend loc = course.location content_store = contentstore() commit = True delete_course(self.def_ms, content_store, loc, commit) # don't delete user permission groups, though self.msg += u"<font color='red'>{0} {1} = {2} ({3})</font>".format( _("Deleted"), loc, course.id, course.display_name )
def handle(self, *args, **options): if len(args) != 1 and len(args) != 2: raise CommandError( "delete_course requires one or more arguments: <location> |commit|" ) loc_str = args[0] commit = False if len(args) == 2: commit = args[1] == 'commit' if commit: print 'Actually going to delete the course from DB....' ms = modulestore('direct') cs = contentstore() if query_yes_no("Deleting course {0}. Confirm?".format(loc_str), default="no"): if query_yes_no("Are you sure. This action cannot be undone!", default="no"): loc = CourseDescriptor.id_to_location(loc_str) if delete_course(ms, cs, loc, commit): print 'removing User permissions from course....' # in the django layer, we need to remove all the user permissions groups associated with this course if commit: _delete_course_group(loc)
def delete_course_and_groups(course_id, commit=False): """ This deletes the courseware associated with a course_id as well as cleaning update_item the various user table stuff (groups, permissions, etc.) """ module_store = modulestore('direct') content_store = contentstore() course_id_dict = Location.parse_course_id(course_id) module_store.ignore_write_events_on_courses.append( '{org}/{course}'.format(**course_id_dict)) loc = CourseDescriptor.id_to_location(course_id) if delete_course(module_store, content_store, loc, commit): print 'removing User permissions from course....' # in the django layer, we need to remove all the user permissions groups associated with this course if commit: try: staff_role = CourseStaffRole(loc) staff_role.remove_users(*staff_role.users_with_role()) instructor_role = CourseInstructorRole(loc) instructor_role.remove_users( *instructor_role.users_with_role()) except Exception as err: log.error( "Error in deleting course groups for {0}: {1}".format( loc, err)) # remove location of this course from loc_mapper and cache loc_mapper().delete_course_mapping(loc)
def delete_course_and_groups(course_id, commit=False): """ This deletes the courseware associated with a course_id as well as cleaning update_item the various user table stuff (groups, permissions, etc.) """ module_store = modulestore('direct') content_store = contentstore() org, course_num, _ = course_id.split("/") module_store.ignore_write_events_on_courses.append('{0}/{1}'.format( org, course_num)) loc = CourseDescriptor.id_to_location(course_id) if delete_course(module_store, content_store, loc, commit): print 'removing forums permissions and roles...' unseed_permissions_roles(course_id) print 'removing User permissions from course....' # in the django layer, we need to remove all the user permissions groups associated with this course if commit: try: staff_role = CourseStaffRole(loc) staff_role.remove_users(*staff_role.users_with_role()) instructor_role = CourseInstructorRole(loc) instructor_role.remove_users( *instructor_role.users_with_role()) except Exception as err: log.error( "Error in deleting course groups for {0}: {1}".format( loc, err))
def delete_course_and_groups(course_id, commit=False): """ This deletes the courseware associated with a course_id as well as cleaning update_item the various user table stuff (groups, permissions, etc.) """ module_store = modulestore('direct') content_store = contentstore() course_id_dict = Location.parse_course_id(course_id) module_store.ignore_write_events_on_courses.append('{org}/{course}'.format(**course_id_dict)) loc = CourseDescriptor.id_to_location(course_id) if delete_course(module_store, content_store, loc, commit): print 'removing User permissions from course....' # in the django layer, we need to remove all the user permissions groups associated with this course if commit: try: staff_role = CourseStaffRole(loc) staff_role.remove_users(*staff_role.users_with_role()) instructor_role = CourseInstructorRole(loc) instructor_role.remove_users(*instructor_role.users_with_role()) except Exception as err: log.error("Error in deleting course groups for {0}: {1}".format(loc, err)) # remove location of this course from loc_mapper and cache loc_mapper().delete_course_mapping(loc)
def handle(self, *args, **options): if len(args) != 1 and len(args) != 2: raise CommandError( "delete_course requires one or more arguments: <location> |commit|") loc_str = args[0] commit = False if len(args) == 2: commit = args[1] == 'commit' if commit: print 'Actually going to delete the course from DB....' ms = modulestore('direct') cs = contentstore() if query_yes_no("Deleting course {0}. Confirm?".format(loc_str), default="no"): if query_yes_no("Are you sure. This action cannot be undone!", default="no"): loc = CourseDescriptor.id_to_location(loc_str) if delete_course(ms, cs, loc, commit): print 'removing User permissions from course....' # in the django layer, we need to remove all the user # permissions groups associated with this course if commit: _delete_course_group(loc)
def handle(self, *args, **options): if len(args) != 1 and len(args) != 2: raise CommandError("delete_course requires one or more arguments: <location> |commit|") course_id = args[0] commit = False if len(args) == 2: commit = args[1] == 'commit' if commit: print 'Actually going to delete the course from DB....' ms = modulestore('direct') cs = contentstore() org, course_num, run = course_id.split("/") ms.ignore_write_events_on_courses.append('{0}/{1}'.format(org, course_num)) if query_yes_no("Deleting course {0}. Confirm?".format(course_id), default="no"): if query_yes_no("Are you sure. This action cannot be undone!", default="no"): loc = CourseDescriptor.id_to_location(course_id) if delete_course(ms, cs, loc, commit): print 'removing User permissions from course....' # in the django layer, we need to remove all the user permissions groups associated with this course if commit: try: _delete_course_group(loc) except Exception as err: print("Error in deleting course groups for {0}: {1}".format(loc, err))
def delete_course_and_groups(course_id, commit=False): """ This deletes the courseware associated with a course_id as well as cleaning update_item the various user table stuff (groups, permissions, etc.) """ module_store = modulestore('direct') content_store = contentstore() module_store.ignore_write_events_on_courses.add(course_id) if delete_course(module_store, content_store, course_id, commit): print 'removing User permissions from course....' # in the django layer, we need to remove all the user permissions groups associated with this course if commit: try: staff_role = CourseStaffRole(course_id) staff_role.remove_users(*staff_role.users_with_role()) instructor_role = CourseInstructorRole(course_id) instructor_role.remove_users( *instructor_role.users_with_role()) except Exception as err: log.error( "Error in deleting course groups for {0}: {1}".format( course_id, err))
def test_branching(self): """ Exercise branching code of import """ repo_dir = self.GIT_REPO_DIR # Test successful import from command if not os.path.isdir(repo_dir): os.mkdir(repo_dir) self.addCleanup(shutil.rmtree, repo_dir) # Checkout non existent branch with self.assertRaisesRegexp(GitImportError, GitImportError.REMOTE_BRANCH_MISSING): git_import.add_repo(self.TEST_REPO, repo_dir / 'edx4edx_lite', 'asdfasdfasdf') # Checkout new branch git_import.add_repo(self.TEST_REPO, repo_dir / 'edx4edx_lite', self.TEST_BRANCH) def_ms = modulestore() # Validate that it is different than master self.assertIsNotNone(def_ms.get_course(self.TEST_BRANCH_COURSE)) # Attempt to check out the same branch again to validate branch choosing # works git_import.add_repo(self.TEST_REPO, repo_dir / 'edx4edx_lite', self.TEST_BRANCH) # Delete to test branching back to master delete_course(def_ms, contentstore(), def_ms.get_course(self.TEST_BRANCH_COURSE).location, True) self.assertIsNone(def_ms.get_course(self.TEST_BRANCH_COURSE)) git_import.add_repo(self.TEST_REPO, repo_dir / 'edx4edx_lite', 'master') self.assertIsNone(def_ms.get_course(self.TEST_BRANCH_COURSE)) self.assertIsNotNone(def_ms.get_course(self.TEST_COURSE))
def test_branching(self): """ Exercise branching code of import """ repo_dir = self.GIT_REPO_DIR # Test successful import from command if not os.path.isdir(repo_dir): os.mkdir(repo_dir) self.addCleanup(shutil.rmtree, repo_dir) # Checkout non existent branch with self.assertRaisesRegexp(GitImportError, GitImportError.REMOTE_BRANCH_MISSING): git_import.add_repo(self.TEST_REPO, repo_dir / 'edx4edx_lite', 'asdfasdfasdf') # Checkout new branch git_import.add_repo(self.TEST_REPO, repo_dir / 'edx4edx_lite', self.TEST_BRANCH) def_ms = modulestore() # Validate that it is different than master self.assertIsNotNone(def_ms.get_course(self.TEST_BRANCH_COURSE)) # Attempt to check out the same branch again to validate branch choosing # works git_import.add_repo(self.TEST_REPO, repo_dir / 'edx4edx_lite', self.TEST_BRANCH) # Delete to test branching back to master delete_course(def_ms, contentstore(), self.TEST_BRANCH_COURSE, True) self.assertIsNone(def_ms.get_course(self.TEST_BRANCH_COURSE)) git_import.add_repo(self.TEST_REPO, repo_dir / 'edx4edx_lite', 'master') self.assertIsNone(def_ms.get_course(self.TEST_BRANCH_COURSE)) self.assertIsNotNone( def_ms.get_course( SlashSeparatedCourseKey.from_deprecated_string( self.TEST_COURSE)))
def handle(self, *args, **options): if len(args) != 1 and len(args) != 2: raise CommandError( "delete_course requires one or more arguments: <location> |commit|" ) course_id = args[0] commit = False if len(args) == 2: commit = args[1] == 'commit' if commit: print 'Actually going to delete the course from DB....' ms = modulestore('direct') cs = contentstore() ms.set_modulestore_configuration({ 'metadata_inheritance_cache_subsystem': CACHE, 'request_cache': RequestCache.get_request_cache() }) org, course_num, run = course_id.split("/") ms.ignore_write_events_on_courses.append('{0}/{1}'.format( org, course_num)) if query_yes_no("Deleting course {0}. Confirm?".format(course_id), default="no"): if query_yes_no("Are you sure. This action cannot be undone!", default="no"): loc = CourseDescriptor.id_to_location(course_id) if delete_course(ms, cs, loc, commit): print 'removing User permissions from course....' # in the django layer, we need to remove all the user permissions groups associated with this course if commit: try: _delete_course_group(loc) except Exception as err: print( "Error in deleting course groups for {0}: {1}". format(loc, err))
def delete_course_and_groups(course_id, commit=False): """ This deletes the courseware associated with a course_id as well as cleaning update_item the various user table stuff (groups, permissions, etc.) """ module_store = modulestore() content_store = contentstore() with store_bulk_write_operations_on_course(module_store, course_id): if delete_course(module_store, content_store, course_id, commit): print 'removing User permissions from course....' # in the django layer, we need to remove all the user permissions groups associated with this course if commit: try: staff_role = CourseStaffRole(course_id) staff_role.remove_users(*staff_role.users_with_role()) instructor_role = CourseInstructorRole(course_id) instructor_role.remove_users(*instructor_role.users_with_role()) except Exception as err: log.error("Error in deleting course groups for {0}: {1}".format(course_id, err))
def test_export_course(self): module_store = modulestore('direct') draft_store = modulestore('draft') content_store = contentstore() import_from_xml(module_store, 'common/test/data/', ['full']) location = CourseDescriptor.id_to_location('edX/full/6.002_Spring_2012') # get a vertical (and components in it) to put into 'draft' vertical = module_store.get_item(Location(['i4x', 'edX', 'full', 'vertical', 'vertical_66', None]), depth=1) draft_store.clone_item(vertical.location, vertical.location) # We had a bug where orphaned draft nodes caused export to fail. This is here to cover that case. draft_store.clone_item(vertical.location, Location(['i4x', 'edX', 'full', 'vertical', 'no_references', 'draft'])) for child in vertical.get_children(): draft_store.clone_item(child.location, child.location) root_dir = path(mkdtemp_clean()) # now create a private vertical private_vertical = draft_store.clone_item(vertical.location, Location(['i4x', 'edX', 'full', 'vertical', 'a_private_vertical', None])) # add private to list of children sequential = module_store.get_item(Location(['i4x', 'edX', 'full', 'sequential', 'Administrivia_and_Circuit_Elements', None])) private_location_no_draft = private_vertical.location.replace(revision=None) module_store.update_children(sequential.location, sequential.children + [private_location_no_draft.url()]) # read back the sequential, to make sure we have a pointer to sequential = module_store.get_item(Location(['i4x', 'edX', 'full', 'sequential', 'Administrivia_and_Circuit_Elements', None])) self.assertIn(private_location_no_draft.url(), sequential.children) print 'Exporting to tempdir = {0}'.format(root_dir) # export out to a tempdir export_to_xml(module_store, content_store, location, root_dir, 'test_export', draft_modulestore=draft_store) # check for static tabs self.verify_content_existence(module_store, root_dir, location, 'tabs', 'static_tab', '.html') # check for custom_tags self.verify_content_existence(module_store, root_dir, location, 'info', 'course_info', '.html') # check for custom_tags self.verify_content_existence(module_store, root_dir, location, 'custom_tags', 'custom_tag_template') # check for about content self.verify_content_existence(module_store, root_dir, location, 'about', 'about', '.html') # check for graiding_policy.json filesystem = OSFS(root_dir / 'test_export/policies/6.002_Spring_2012') self.assertTrue(filesystem.exists('grading_policy.json')) course = module_store.get_item(location) # compare what's on disk compared to what we have in our course with filesystem.open('grading_policy.json', 'r') as grading_policy: on_disk = loads(grading_policy.read()) self.assertEqual(on_disk, course.grading_policy) #check for policy.json self.assertTrue(filesystem.exists('policy.json')) # compare what's on disk to what we have in the course module with filesystem.open('policy.json', 'r') as course_policy: on_disk = loads(course_policy.read()) self.assertIn('course/6.002_Spring_2012', on_disk) self.assertEqual(on_disk['course/6.002_Spring_2012'], own_metadata(course)) # remove old course delete_course(module_store, content_store, location) # reimport import_from_xml(module_store, root_dir, ['test_export'], draft_store=draft_store) items = module_store.get_items(Location(['i4x', 'edX', 'full', 'vertical', None])) self.assertGreater(len(items), 0) for descriptor in items: # don't try to look at private verticals. Right now we're running # the service in non-draft aware if getattr(descriptor, 'is_draft', False): print "Checking {0}....".format(descriptor.location.url()) resp = self.client.get(reverse('edit_unit', kwargs={'location': descriptor.location.url()})) self.assertEqual(resp.status_code, 200) # verify that we have the content in the draft store as well vertical = draft_store.get_item(Location(['i4x', 'edX', 'full', 'vertical', 'vertical_66', None]), depth=1) self.assertTrue(getattr(vertical, 'is_draft', False)) for child in vertical.get_children(): self.assertTrue(getattr(child, 'is_draft', False)) # make sure that we don't have a sequential that is in draft mode sequential = draft_store.get_item(Location(['i4x', 'edX', 'full', 'sequential', 'Administrivia_and_Circuit_Elements', None])) self.assertFalse(getattr(sequential, 'is_draft', False)) # verify that we have the private vertical test_private_vertical = draft_store.get_item(Location(['i4x', 'edX', 'full', 'vertical', 'vertical_66', None])) self.assertTrue(getattr(test_private_vertical, 'is_draft', False)) # make sure the textbook survived the export/import course = module_store.get_item(Location(['i4x', 'edX', 'full', 'course', '6.002_Spring_2012', None])) self.assertGreater(len(course.textbooks), 0) shutil.rmtree(root_dir)