diff --git a/lms/djangoapps/dashboard/sysadmin.py b/lms/djangoapps/dashboard/sysadmin.py index fcbaa9bdc3..3d89a86f77 100644 --- a/lms/djangoapps/dashboard/sysadmin.py +++ b/lms/djangoapps/dashboard/sysadmin.py @@ -59,10 +59,6 @@ class SysadminDashboardView(TemplateView): """ self.def_ms = modulestore() - - self.is_using_mongo = True - if self.def_ms.get_modulestore_type(None) == 'xml': - self.is_using_mongo = False self.msg = u'' self.datatable = [] super(SysadminDashboardView, self).__init__(**kwargs) @@ -374,10 +370,7 @@ class Courses(SysadminDashboardView): return _("The git repo location should end with '.git', " "and be a valid url") - if self.is_using_mongo: - return self.import_mongo_course(gitloc, branch) - - return self.import_xml_course(gitloc, branch) + return self.import_mongo_course(gitloc, branch) def import_mongo_course(self, gitloc, branch): """ @@ -429,80 +422,6 @@ class Courses(SysadminDashboardView): msg += u"
{0}
".format(escape(ret)) return msg - def import_xml_course(self, gitloc, branch): - """Imports a git course into the XMLModuleStore""" - - msg = u'' - if not getattr(settings, 'GIT_IMPORT_WITH_XMLMODULESTORE', False): - # Translators: "GIT_IMPORT_WITH_XMLMODULESTORE" is a variable name. - # "XMLModuleStore" and "MongoDB" are database systems. You should not - # translate these names. - return _('Refusing to import. GIT_IMPORT_WITH_XMLMODULESTORE is ' - 'not turned on, and it is generally not safe to import ' - 'into an XMLModuleStore with multithreaded. We ' - 'recommend you enable the MongoDB based module store ' - 'instead, unless this is a development environment.') - cdir = (gitloc.rsplit('/', 1)[1])[:-4] - gdir = settings.DATA_DIR / cdir - if os.path.exists(gdir): - msg += _("The course {0} already exists in the data directory! " - "(reloading anyway)").format(cdir) - cmd = ['git', 'pull', ] - cwd = gdir - else: - cmd = ['git', 'clone', gitloc, ] - cwd = settings.DATA_DIR - cwd = os.path.abspath(cwd) - try: - cmd_output = escape( - subprocess.check_output(cmd, stderr=subprocess.STDOUT, cwd=cwd) - ) - except subprocess.CalledProcessError as ex: - log.exception('Git pull or clone output was: %r', ex.output) - # Translators: unable to download the course content from - # the source git repository. Clone occurs if this is brand - # new, and pull is when it is being updated from the - # source. - return _('Unable to clone or pull repository. Please check ' - 'your url. Output was: {0!r}').format(ex.output) - - msg += u'
{0}
'.format(cmd_output) - if not os.path.exists(gdir): - msg += _('Failed to clone repository to {directory_name}').format(directory_name=gdir) - return msg - # Change branch if specified - if branch: - try: - git_import.switch_branch(branch, gdir) - except GitImportError as ex: - return str(ex) - # Translators: This is a git repository branch, which is a - # specific version of a courses content - msg += u'

{0}

'.format( - _('Successfully switched to branch: ' - '{branch_name}').format(branch_name=branch)) - - self.def_ms.try_load_course(os.path.abspath(gdir)) - errlog = self.def_ms.errored_courses.get(cdir, '') - if errlog: - msg += u'
{0}
'.format(escape(errlog)) - else: - course = self.def_ms.courses[os.path.abspath(gdir)] - msg += _('Loaded course {course_name}
Errors:').format( - course_name="{} {}".format(cdir, course.display_name) - ) - errors = self.def_ms.get_course_errors(course.id) - if not errors: - msg += u'None' - else: - msg += u'' - - return msg - def make_datatable(self): """Creates course information datatable""" diff --git a/lms/djangoapps/dashboard/tests/test_sysadmin.py b/lms/djangoapps/dashboard/tests/test_sysadmin.py index ee0355cf68..a3a954c1d9 100644 --- a/lms/djangoapps/dashboard/tests/test_sysadmin.py +++ b/lms/djangoapps/dashboard/tests/test_sysadmin.py @@ -20,8 +20,6 @@ from django.utils.translation import ugettext as _ import mongoengine from opaque_keys.edx.locations import SlashSeparatedCourseKey -from xmodule.modulestore.tests.django_utils import TEST_DATA_XML_MODULESTORE - from dashboard.models import CourseImportLog from dashboard.sysadmin import Users from dashboard.git_import import GitImportError @@ -30,7 +28,7 @@ from external_auth.models import ExternalAuthMap from student.roles import CourseStaffRole, GlobalStaff from student.tests.factories import UserFactory from xmodule.modulestore.django import modulestore -from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase +from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.mongo_connection import MONGO_PORT_NUM, MONGO_HOST @@ -46,7 +44,7 @@ FEATURES_WITH_SSL_AUTH = settings.FEATURES.copy() FEATURES_WITH_SSL_AUTH['AUTH_USE_CERTIFICATES'] = True -class SysadminBaseTestCase(ModuleStoreTestCase): +class SysadminBaseTestCase(SharedModuleStoreTestCase): """ Base class with common methods used in XML and Mongo tests """ @@ -57,7 +55,7 @@ class SysadminBaseTestCase(ModuleStoreTestCase): def setUp(self): """Setup test case by adding primary user.""" - super(SysadminBaseTestCase, self).setUp(create_user=False) + super(SysadminBaseTestCase, self).setUp() self.user = UserFactory.create(username='test_user', email='test_user+sysadmin@edx.org', password='foo') @@ -115,297 +113,6 @@ class SysadminBaseTestCase(ModuleStoreTestCase): self.addCleanup(shutil.rmtree, path) -@attr('shard_1') -@unittest.skipUnless(settings.FEATURES.get('ENABLE_SYSADMIN_DASHBOARD'), - "ENABLE_SYSADMIN_DASHBOARD not set") -@override_settings(GIT_IMPORT_WITH_XMLMODULESTORE=True) -class TestSysadmin(SysadminBaseTestCase): - """ - Test sysadmin dashboard features using XMLModuleStore - """ - MODULESTORE = TEST_DATA_XML_MODULESTORE - - def test_staff_access(self): - """Test access controls.""" - - test_views = ['sysadmin', 'sysadmin_courses', 'sysadmin_staffing', ] - for view in test_views: - response = self.client.get(reverse(view)) - self.assertEqual(response.status_code, 302) - - self.user.is_staff = False - self.user.save() - - logged_in = self.client.login(username=self.user.username, - password='foo') - self.assertTrue(logged_in) - - for view in test_views: - response = self.client.get(reverse(view)) - self.assertEqual(response.status_code, 404) - - response = self.client.get(reverse('gitlogs')) - self.assertEqual(response.status_code, 404) - - self.user.is_staff = True - self.user.save() - - self.client.logout() - self.client.login(username=self.user.username, password='foo') - - for view in test_views: - response = self.client.get(reverse(view)) - self.assertTrue(response.status_code, 200) - - response = self.client.get(reverse('gitlogs')) - self.assertTrue(response.status_code, 200) - - def test_user_mod(self): - """Create and delete a user""" - - self._setstaff_login() - - self.client.login(username=self.user.username, password='foo') - - # Create user tests - - # No uname - response = self.client.post(reverse('sysadmin'), - {'action': 'create_user', - 'student_fullname': 'blah', - 'student_password': 'foozor', }) - self.assertIn('Must provide username', response.content.decode('utf-8')) - # no full name - response = self.client.post(reverse('sysadmin'), - {'action': 'create_user', - 'student_uname': 'test_cuser+sysadmin@edx.org', - 'student_password': 'foozor', }) - self.assertIn('Must provide full name', response.content.decode('utf-8')) - - # Test create valid user - self.client.post(reverse('sysadmin'), - {'action': 'create_user', - 'student_uname': 'test_cuser+sysadmin@edx.org', - 'student_fullname': 'test cuser', - 'student_password': 'foozor', }) - - self.assertIsNotNone( - User.objects.get(username='test_cuser+sysadmin@edx.org', - email='test_cuser+sysadmin@edx.org')) - - # login as new user to confirm - self.assertTrue(self.client.login( - username='test_cuser+sysadmin@edx.org', password='foozor')) - - self.client.logout() - self.client.login(username=self.user.username, password='foo') - - # Delete user tests - - # Try no username - response = self.client.post(reverse('sysadmin'), - {'action': 'del_user', }) - self.assertIn('Must provide username', response.content.decode('utf-8')) - - # Try bad usernames - response = self.client.post(reverse('sysadmin'), - {'action': 'del_user', - 'student_uname': 'flabbergast@example.com', - 'student_fullname': 'enigma jones', }) - self.assertIn('Cannot find user with email address', response.content.decode('utf-8')) - - response = self.client.post(reverse('sysadmin'), - {'action': 'del_user', - 'student_uname': 'flabbergast', - 'student_fullname': 'enigma jones', }) - self.assertIn('Cannot find user with username', response.content.decode('utf-8')) - - self.client.post(reverse('sysadmin'), - {'action': 'del_user', - 'student_uname': 'test_cuser+sysadmin@edx.org', - 'student_fullname': 'test cuser', }) - - self.assertEqual(0, len(User.objects.filter( - username='test_cuser+sysadmin@edx.org', - email='test_cuser+sysadmin@edx.org'))) - - self.assertEqual(1, len(User.objects.all())) - - def test_user_csv(self): - """Download and validate user CSV""" - - num_test_users = 100 - self._setstaff_login() - - # Stuff full of users to test streaming - for user_num in xrange(num_test_users): - Users().create_user('testingman_with_long_name{}'.format(user_num), - 'test test') - - response = self.client.post(reverse('sysadmin'), - {'action': 'download_users', }) - - self.assertIn('attachment', response['Content-Disposition']) - self.assertEqual('text/csv', response['Content-Type']) - self.assertIn('test_user', response.content) - self.assertTrue(num_test_users + 2, len(response.content.splitlines())) - - # Clean up - User.objects.filter( - username__startswith='testingman_with_long_name').delete() - - @override_settings(FEATURES=FEATURES_WITH_SSL_AUTH) - def test_authmap_repair(self): - """Run authmap check and repair""" - - self._setstaff_login() - - Users().create_user('test0', 'test test') - # Will raise exception, so no assert needed - eamap = ExternalAuthMap.objects.get(external_name='test test') - mitu = User.objects.get(username='test0') - - self.assertTrue(check_password(eamap.internal_password, mitu.password)) - mitu.set_password('not autogenerated') - mitu.save() - self.assertFalse(check_password(eamap.internal_password, mitu.password)) - - # Create really non user AuthMap - ExternalAuthMap(external_id='ll', - external_domain='ll', - external_credentials='{}', - external_email='a@b.c', - external_name='c', - internal_password='').save() - - response = self.client.post(reverse('sysadmin'), - {'action': 'repair_eamap', }) - - self.assertIn('{0} test0'.format('Failed in authenticating'), - response.content) - self.assertIn('fixed password', response.content.decode('utf-8')) - - self.assertTrue(self.client.login(username='test0', - password=eamap.internal_password)) - - # Check for all OK - self._setstaff_login() - response = self.client.post(reverse('sysadmin'), - {'action': 'repair_eamap', }) - self.assertIn('All ok!', response.content.decode('utf-8')) - - def test_xml_course_add_delete(self): - """add and delete course from xml module store""" - - self._setstaff_login() - - # Try bad git repo - response = self.client.post(reverse('sysadmin_courses'), { - 'repo_location': 'github.com/mitocw/edx4edx_lite', - 'action': 'add_course', }) - self.assertIn(_("The git repo location should end with '.git', " - "and be a valid url"), response.content.decode('utf-8')) - - response = self.client.post(reverse('sysadmin_courses'), { - 'repo_location': 'http://example.com/not_real.git', - 'action': 'add_course', }) - self.assertIn('Unable to clone or pull repository', - response.content.decode('utf-8')) - # Create git loaded course - response = self._add_edx4edx() - - def_ms = modulestore() - - self.assertEqual('xml', def_ms.get_modulestore_type(None)) - course = def_ms.courses.get('{0}/edx4edx_lite'.format( - os.path.abspath(settings.DATA_DIR)), None) - self.assertIsNotNone(course) - - # Delete a course - self._rm_edx4edx() - course = def_ms.courses.get('{0}/edx4edx_lite'.format( - os.path.abspath(settings.DATA_DIR)), None) - self.assertIsNone(course) - - # Load a bad git branch - response = self._add_edx4edx('asdfasdfasdf') - self.assertIn(GitImportError.REMOTE_BRANCH_MISSING, - response.content.decode('utf-8')) - - # Load a course from a git branch - self._add_edx4edx(self.TEST_BRANCH) - course = def_ms.courses.get('{0}/edx4edx_lite'.format( - os.path.abspath(settings.DATA_DIR)), None) - self.assertIsNotNone(course) - self.assertEqual(self.TEST_BRANCH_COURSE, course.id) - self._rm_edx4edx() - - # Try and delete a non-existent course - response = self.client.post(reverse('sysadmin_courses'), - {'course_id': 'foobar/foo/blah', - 'action': 'del_course', }) - self.assertIn('Error - cannot get course with ID', - response.content.decode('utf-8')) - - @override_settings(GIT_IMPORT_WITH_XMLMODULESTORE=False) - def test_xml_safety_flag(self): - """Make sure the settings flag to disable xml imports is working""" - - self._setstaff_login() - response = self._add_edx4edx() - self.assertIn('GIT_IMPORT_WITH_XMLMODULESTORE', response.content) - - def_ms = modulestore() - course = def_ms.courses.get('{0}/edx4edx_lite'.format( - os.path.abspath(settings.DATA_DIR)), None) - self.assertIsNone(course) - - def test_git_pull(self): - """Make sure we can pull""" - - self._setstaff_login() - - response = self._add_edx4edx() - response = self._add_edx4edx() - self.assertIn(_("The course {0} already exists in the data directory! " - "(reloading anyway)").format('edx4edx_lite'), - response.content.decode('utf-8')) - self._rm_edx4edx() - - def test_staff_csv(self): - """Download and validate staff CSV""" - - self._setstaff_login() - self._add_edx4edx() - - def_ms = modulestore() - course = def_ms.get_course(SlashSeparatedCourseKey('MITx', 'edx4edx', 'edx4edx')) - CourseStaffRole(course.id).add_users(self.user) - - response = self.client.post(reverse('sysadmin_staffing'), - {'action': 'get_staff_csv', }) - self.assertIn('attachment', response['Content-Disposition']) - self.assertEqual('text/csv', response['Content-Type']) - columns = ['course_id', 'role', 'username', - 'email', 'full_name', ] - self.assertIn(','.join('"' + c + '"' for c in columns), - response.content) - - self._rm_edx4edx() - - def test_enrollment_page(self): - """ - Adds a course and makes sure that it shows up on the staffing and - enrollment page - """ - - self._setstaff_login() - self._add_edx4edx() - response = self.client.get(reverse('sysadmin_staffing')) - self.assertIn('edx4edx', response.content) - self._rm_edx4edx() - - @attr('shard_1') @override_settings(MONGODB_LOG=TEST_MONGODB_LOG) @unittest.skipUnless(settings.FEATURES.get('ENABLE_SYSADMIN_DASHBOARD'),