From 3ff31892609bcb15f88f35c46a86d8872c173f6a Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Tue, 15 Jan 2013 15:39:05 -0500 Subject: [PATCH 01/76] Finished tests for access.py (as far as able) --- .../courseware/tests/test_access.py | 126 ++++++++++++++++++ .../courseware/tests/test_progress.py | 67 ++++++++++ 2 files changed, 193 insertions(+) create mode 100644 lms/djangoapps/courseware/tests/test_access.py create mode 100644 lms/djangoapps/courseware/tests/test_progress.py diff --git a/lms/djangoapps/courseware/tests/test_access.py b/lms/djangoapps/courseware/tests/test_access.py new file mode 100644 index 0000000000..ebdad9e6a8 --- /dev/null +++ b/lms/djangoapps/courseware/tests/test_access.py @@ -0,0 +1,126 @@ +import unittest +import logging +import time +from mock import MagicMock, patch + +from django.conf import settings +from django.test import TestCase + +from xmodule.course_module import CourseDescriptor +from xmodule.error_module import ErrorDescriptor +from xmodule.modulestore import Location +from xmodule.timeparse import parse_time +from xmodule.x_module import XModule, XModuleDescriptor +import courseware.access as access + +class Stub: + def __init__(self): + pass + +class AccessTestCase(TestCase): + def setUp(self): + pass + + def test__has_global_staff_access(self): + # Only 2 branches? + mock_user = MagicMock() + mock_user.is_staff = True + self.assertTrue(access._has_global_staff_access(mock_user)) + mock_user_2 = MagicMock() + mock_user.is_staff = False + self.assertFalse(access._has_global_staff_access(mock_user)) + + def test__has_access_to_location(self): + mock_user = MagicMock() + mock_user.is_authenticated.return_value = False + self.assertFalse(access._has_access_to_location(mock_user, "dummy", + "dummy")) + mock_user_2 = MagicMock() + mock_user_2.groups.all.return_value = ['instructor_toy'] + location = MagicMock(spec=Location) + location.course = 'toy' + self.assertTrue(access._has_access_to_location(mock_user_2, location, + "instructor")) + mock_user_3 = MagicMock() + mock_user_3.is_staff = False + self.assertFalse(access._has_access_to_location(mock_user_3, 'dummy', + 'dummy')) + + def test__dispatch(self): + self.assertRaises(ValueError, access._dispatch,{}, 'action', 'dummy', + 'dummy') + + def test__has_access_string(self): + mock_user = MagicMock() + mock_user.is_staff = True + self.assertTrue(access._has_access_string(mock_user, 'global', 'staff')) + self.assertFalse(access._has_access_string(mock_user, 'dummy', 'staff')) + + def test__has_access_descriptor(self): + mock_descriptor = MagicMock() + mock_descriptor.start = 0 + # test has dependency on time.gmtime() > 0 + self.assertTrue(access._has_access_descriptor("dummy", mock_descriptor, + 'load')) + mock_descriptor_2 = MagicMock() + mock_descriptor_2.start = None + self.assertTrue(access._has_access_descriptor("dummy", mock_descriptor_2, + 'load')) + + def test__has_access_error_desc(self): + mock_user = None + mock_descriptor = MagicMock() + mock_descriptor.location = None + # Just want to make sure function goes through path. + self.assertFalse(access._has_access_error_desc(mock_user, mock_descriptor, + 'load')) + + def test__get_access_group_name_course_desc(self): + self.assertEquals(access._get_access_group_name_course_desc('dummy', + 'notstaff'), + []) + # Problem: Can't use a Mock for location because needs to be a valid + # input to Location + # Getting "IndentationError: expected an indented block" +## tag, org, course, category, name = [MagicMock()]*5 +## #mock_course.location = ['tag', 'org', 'course', 'category', 'name'] +## L = Location([tag, org, course, category, name]) +## print L.course_id() +## assert False + #mock_course.location.course = 'toy' + #access._get_access_group_name_course_desc(mock_course, 'staff') + + def test__has_access_course_desc(self): + # This is more of a test for see_exists + mock_course = MagicMock() + mock_course.metadata.get = 'is_public' + self.assertTrue(access._has_access_course_desc('dummy', mock_course, + 'see_exists')) + mock_course_2 = MagicMock() + mock_course_2.metadata.get = 'private' + # Is there a way to see all the functions that have been called on a mock? + # Basically, I want to see if _has_staff_access_to_descriptor is called on + # the mock user and course + # This actually doesn't seem possible, according to the API + # None user can see course even if not 'is_public'? + self.assertTrue(access._has_access_course_desc(None, mock_course_2, + 'see_exists')) + def test_get_access_group_name(self): + # Need to create an instance of CourseDescriptor + # Is it necessary to test? basically "testing" python + self.assertRaises(TypeError, access.get_access_group_name, + 'notCourseDescriptor', 'dummy_action') + + def test_has_access(self): + magic = MagicMock() + error = ErrorDescriptor(magic) + mock_user = MagicMock() + self.assertFalse(access.has_access(None, error, 'load')) + self.assertFalse(access.has_access(mock_user, 'dummy', 'staff')) + self.assertRaises(TypeError, access.has_access,'dummyuser', {}, 'dummy') + +# How do decorators work? I think that is the correct +## def test_patches(self): +## user = Stub() +## @patch.object(Stub, "is_staff", True) +## self.assertTrue(access._has_global_staff_access(mock_user)) diff --git a/lms/djangoapps/courseware/tests/test_progress.py b/lms/djangoapps/courseware/tests/test_progress.py new file mode 100644 index 0000000000..480d594863 --- /dev/null +++ b/lms/djangoapps/courseware/tests/test_progress.py @@ -0,0 +1,67 @@ +from django.test import TestCase +from courseware import progress +from mock import MagicMock + + + +class ProgessTests(TestCase): + def setUp(self): + + self.d = dict({'duration_total': 0, + 'duration_watched': 0, + 'done': True, + 'questions_correct': 4, + 'questions_incorrect': 0, + 'questions_total': 0}) + + self.c = progress.completion() + self.c2= progress.completion() + self.c2.dict = dict({'duration_total': 0, + 'duration_watched': 0, + 'done': True, + 'questions_correct': 2, + 'questions_incorrect': 1, + 'questions_total': 0}) + + self.cplusc2 = dict({'duration_total': 0, + 'duration_watched': 0, + 'done': True, + 'questions_correct': 2, + 'questions_incorrect': 1, + 'questions_total': 0}) + + + + self.oth = dict({'duration_total': 0, + 'duration_watched': 0, + 'done': True, + 'questions_correct': 4, + 'questions_incorrect': 0, + 'questions_total': 7}) + + self.x = MagicMock() + self.x.dict = self.oth + + self.d_oth = {'duration_total': 0, + 'duration_watched': 0, + 'done': True, + 'questions_correct': 4, + 'questions_incorrect': 0, + 'questions_total': 7} + def test_getitem(self): + self.assertEqual(self.c.__getitem__('duration_watched'), 0) + + def test_setitem(self): + self.c.__setitem__('questions_correct', 4) + self.assertEqual(str(self.c),str(self.d)) + + # def test_add(self): + # self.assertEqual(self.c.__add__(self.c2), self.cplusc2) + + def test_contains(self): + + return self.c.__contains__('meow') + #self.assertEqual(self.c.__contains__('done'), True) + + def test_repr(self): + self.assertEqual(self.c.__repr__(), str(progress.completion())) From 1565cd5bd364caa2d478ae3bd27095462e46a5ec Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Fri, 18 Jan 2013 14:44:17 -0500 Subject: [PATCH 02/76] Test cases for lms/djangoapps/courseware/module_render in test_module_render --- .../courseware/tests/test_module_render.py | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 lms/djangoapps/courseware/tests/test_module_render.py diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py new file mode 100644 index 0000000000..8340d1fda2 --- /dev/null +++ b/lms/djangoapps/courseware/tests/test_module_render.py @@ -0,0 +1,164 @@ +from unittest import TestCase +import logging +from mock import MagicMock, patch +import json +import factory + +from django.http import Http404, HttpResponse, HttpRequest +from django.conf import settings +from django.contrib.auth.models import User +from django.test.client import Client +from django.conf import settings + +from courseware.models import StudentModule +from xmodule.modulestore.exceptions import ItemNotFoundError +from xmodule.modulestore import Location +import courseware.module_render as render + +class Stub: + def __init__(self): + pass + +class ModuleRenderTestCase(TestCase): + def setUp(self): + self.location = ['tag', 'org', 'course', 'category', 'name'] + + def test_toc_for_course(self): + mock_course = MagicMock() + mock_course.id = 'dummy' + mock_course.location = Location(self.location) + mock_course.get_children.return_value = [] + mock_user = MagicMock() + mock_user.is_authenticated.return_value = False + self.assertIsNone(render.toc_for_course(mock_user,'dummy', + mock_course, 'dummy', 'dummy')) + + def test_get_module(self): + self.assertIsNone(render.get_module('dummyuser',None,\ + 'invalid location',None,None)) + + + def test__get_module(self): + mock_user = MagicMock() + mock_user.is_authenticated.return_value = True + location = ['tag', 'org', 'course', 'category', 'name'] + #render._get_module(mock_user, + + def test_get_instance_module(self): + mock_user = MagicMock() + mock_user.is_authenticated.return_value = False + self.assertIsNone(render.get_instance_module('dummy', mock_user, 'dummy', + 'dummy')) + mock_user_2 = MagicMock() + mock_user_2.is_authenticated.return_value = True + mock_module = MagicMock() + mock_module.descriptor.stores_state = False + self.assertIsNone(render.get_instance_module('dummy', mock_user_2, + mock_module,'dummy')) + + def test_get_shared_instance_module(self): +## class MockUserFactory(factory.Factory): +## FACTORY_FOR = MagicMock +## is_authenticated.return_value = + mock_user = MagicMock(User) + mock_user.is_authenticated.return_value = False + self.assertIsNone(render.get_shared_instance_module('dummy', mock_user, 'dummy', + 'dummy')) + mock_user_2 = MagicMock(User) + mock_user_2.is_authenticated.return_value = True + mock_module = MagicMock() + mock_module.shared_state_key = 'key' + self.assertIsInstance(render.get_shared_instance_module('dummy', mock_user, + mock_module, 'dummy'), StudentModule) + + + + def test_xqueue_callback(self): + mock_request = MagicMock() + mock_request.POST.copy.return_value = {} + # 339 + self.assertRaises(Http404, render.xqueue_callback,mock_request, + 'dummy', 'dummy', 'dummy', 'dummy') + mock_request_2 = MagicMock() + xpackage = {'xqueue_header': json.dumps({}), + 'xqueue_body' : 'Message from grader'} + mock_request_2.POST.copy.return_value = xpackage + # 342 + self.assertRaises(Http404, render.xqueue_callback,mock_request_2, + 'dummy', 'dummy', 'dummy', 'dummy') + mock_request_3 = MagicMock() + xpackage_2 = {'xqueue_header': json.dumps({'lms_key':'secretkey'}), + 'xqueue_body' : 'Message from grader'} + mock_request_3.POST.copy.return_value = xpackage_2 +## self.assertRaises(Http404, render.xqueue_callback, mock_request_3, +## 'dummy', 0, 'dummy', 'dummy') + # continue later + + def test_modx_dispatch(self): + self.assertRaises(Http404, render.modx_dispatch, 'dummy', 'dummy', + 'invalid Location', 'dummy') + mock_request = MagicMock() + mock_request.FILES.keys.return_value = ['file_id'] + mock_request.FILES.getlist.return_value = ['file']*(settings.MAX_FILEUPLOADS_PER_INPUT + 1) + self.assertEquals(render.modx_dispatch(mock_request, 'dummy', self.location, + 'dummy').content, + json.dumps({'success': 'Submission aborted! Maximum %d files may be submitted at once' %\ + settings.MAX_FILEUPLOADS_PER_INPUT})) + mock_request_2 = MagicMock() + mock_request_2.FILES.keys.return_value = ['file_id'] + inputfile = Stub() + inputfile.size = 1 + settings.STUDENT_FILEUPLOAD_MAX_SIZE + inputfile.name = 'name' + filelist = [inputfile] + mock_request_2.FILES.getlist.return_value = filelist + self.assertEquals(render.modx_dispatch(mock_request_2, 'dummy', self.location, + 'dummy').content, + json.dumps({'success': 'Submission aborted! Your file "%s" is too large (max size: %d MB)' %\ + (inputfile.name, settings.STUDENT_FILEUPLOAD_MAX_SIZE/(1000**2))})) + mock_request_3 = MagicMock() + mock_request_3.POST.copy.return_value = {} + inputfile_2 = Stub() + inputfile_2.size = 1 + inputfile_2.name = 'name' + self.assertRaises(ItemNotFoundError, render.modx_dispatch, + mock_request_3, 'dummy', self.location, 'toy') + # Deadend + + def test_preview_chemcalc(self): + mock_request = MagicMock() + mock_request.method = 'notGET' + self.assertRaises(Http404, render.preview_chemcalc, mock_request) + mock_request_2 = MagicMock() + mock_request_2.method = 'GET' + mock_request_2.GET.get.return_value = None + self.assertEquals(render.preview_chemcalc(mock_request_2).content, + json.dumps({'preview':'', + 'error':'No formula specified.'})) + + mock_request_3 = MagicMock() + mock_request_3.method = 'GET' + # Test fails because chemcalc.render_to_html always parses strings? + mock_request_3.GET.get.return_value = unicode('\x12400', errors="strict") +## self.assertEquals(render.preview_chemcalc(mock_request_3).content, +## json.dumps({'preview':'', +## 'error':"Couldn't parse formula: formula"})) +## + mock_request_3 = MagicMock() + mock_request_3.method = 'GET' + mock_request_3.GET.get.return_value = Stub() + self.assertEquals(render.preview_chemcalc(mock_request_3).content, + json.dumps({'preview':'', + 'error':"Error while rendering preview"})) + + + def test_get_score_bucket(self): + self.assertEquals(render.get_score_bucket(0, 10), 'incorrect') + self.assertEquals(render.get_score_bucket(1, 10), 'partial') + self.assertEquals(render.get_score_bucket(10, 10), 'correct') + # get_score_bucket calls error cases 'incorrect' + self.assertEquals(render.get_score_bucket(11, 10), 'incorrect') + self.assertEquals(render.get_score_bucket(-1, 10), 'incorrect') + + +class MagicMockFactory(factory.Factory): + FACTORY_FOR = MagicMock From 6e773909e48b84475d239c76ee8d9431eb80d71b Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Tue, 22 Jan 2013 10:36:05 -0500 Subject: [PATCH 03/76] added test_views --- .../courseware/tests/test_module_render.py | 4 +- lms/djangoapps/courseware/tests/test_views.py | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 lms/djangoapps/courseware/tests/test_views.py diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py index 8340d1fda2..d4748ebb04 100644 --- a/lms/djangoapps/courseware/tests/test_module_render.py +++ b/lms/djangoapps/courseware/tests/test_module_render.py @@ -70,8 +70,6 @@ class ModuleRenderTestCase(TestCase): mock_module.shared_state_key = 'key' self.assertIsInstance(render.get_shared_instance_module('dummy', mock_user, mock_module, 'dummy'), StudentModule) - - def test_xqueue_callback(self): mock_request = MagicMock() @@ -162,3 +160,5 @@ class ModuleRenderTestCase(TestCase): class MagicMockFactory(factory.Factory): FACTORY_FOR = MagicMock + v = factory.LazyAttribute(i for i in [True, False, False]) + diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py new file mode 100644 index 0000000000..1acf175bbd --- /dev/null +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -0,0 +1,37 @@ +from unittest import TestCase +import logging +from mock import MagicMock, patch + +from django.conf import settings +from django.test.utils import override_settings + +import courseware.views as views + +class Stub(): + pass + +class ViewsTestCase(TestCase): + def setUp(self): + pass + + + def test_user_groups(self): + mock_user = MagicMock() + mock_user.is_authenticated.return_value = False + self.assertEquals(views.user_groups(mock_user),[]) + + @override_settings(DEBUG = True) + def test_user_groups_debug(self): + mock_user = MagicMock() + mock_user.is_authenticated.return_value = True + pass + #views.user_groups(mock_user) + #Keep going later + + def test_get_current_child(self): + self.assertIsNone(views.get_current_child(Stub())) + mock_xmodule = MagicMock() + mock_xmodule.position = -1 + mock_xmodule.get_display_items.return_value = ['one','two'] + print views.user_groups(mock_xmodule) + self.assertEquals(views.user_groups(mock_xmodule), 'one') From 35650233891b668d7c74570d59fd030dd49b0485 Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Tue, 22 Jan 2013 14:01:01 -0500 Subject: [PATCH 04/76] created test_courses.py --- .../courseware/tests/test_courses.py | 21 +++++++++ lms/djangoapps/courseware/tests/test_views.py | 47 +++++++++++++++++-- 2 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 lms/djangoapps/courseware/tests/test_courses.py diff --git a/lms/djangoapps/courseware/tests/test_courses.py b/lms/djangoapps/courseware/tests/test_courses.py new file mode 100644 index 0000000000..7e1456efec --- /dev/null +++ b/lms/djangoapps/courseware/tests/test_courses.py @@ -0,0 +1,21 @@ +from mock import MagicMock, patch +import datetime + +from django.test import TestCase +from django.contrib.auth.models import User + +from student.models import CourseEnrollment +import courseware.courses as courses + +class CoursesTestCase(TestCase): + def setUp(self): + self.user = User.objects.create(username='dummy', password='123456', + email='test@mit.edu') + self.date = datetime.datetime(2013,1,22) + self.course_id = 'edx/toy/Fall_2012' + self.enrollment = CourseEnrollment.objects.get_or_create(user = self.user, + course_id = self.course_id, + created = self.date)[0] + + def test_get_course_by_id(self): + courses.get_course_by_id(self.course_id) diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index 1acf175bbd..ba7393f939 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -1,24 +1,37 @@ -from unittest import TestCase import logging from mock import MagicMock, patch +import datetime +from django.test import TestCase +from django.http import Http404 from django.conf import settings from django.test.utils import override_settings +from django.contrib.auth.models import User +from student.models import CourseEnrollment import courseware.views as views +from xmodule.modulestore.django import modulestore + class Stub(): pass class ViewsTestCase(TestCase): def setUp(self): - pass - + self.user = User.objects.create(username='dummy', password='123456', + email='test@mit.edu') + self.date = datetime.datetime(2013,1,22) + self.course_id = 'edx/toy/Fall_2012' + self.enrollment = CourseEnrollment.objects.get_or_create(user = self.user, + course_id = self.course_id, + created = self.date)[0] def test_user_groups(self): + # depreciated function? mock_user = MagicMock() mock_user.is_authenticated.return_value = False self.assertEquals(views.user_groups(mock_user),[]) + @override_settings(DEBUG = True) def test_user_groups_debug(self): @@ -33,5 +46,29 @@ class ViewsTestCase(TestCase): mock_xmodule = MagicMock() mock_xmodule.position = -1 mock_xmodule.get_display_items.return_value = ['one','two'] - print views.user_groups(mock_xmodule) - self.assertEquals(views.user_groups(mock_xmodule), 'one') + self.assertEquals(views.get_current_child(mock_xmodule), 'one') + mock_xmodule_2 = MagicMock() + mock_xmodule_2.position = 3 + mock_xmodule_2.get_display_items.return_value = [] + self.assertIsNone(views.get_current_child(mock_xmodule_2)) + + def test_redirect_to_course_position(self): + mock_module = MagicMock() + mock_module.descriptor.id = 'Underwater Basketweaving' + mock_module.position = 3 + mock_module.get_display_items.return_value = [] + self.assertRaises(Http404, views.redirect_to_course_position, + mock_module, True) + + def test_index(self): + print modulestore() + assert False + + def test_registered_for_course(self): + self.assertFalse(views.registered_for_course('Basketweaving', None)) + mock_user = MagicMock() + mock_user.is_authenticated.return_value = False + self.assertFalse(views.registered_for_course('dummy', mock_user)) + mock_course = MagicMock() + mock_course.id = self.course_id + self.assertTrue(views.registered_for_course(mock_course, self.user)) From 7884e2c6906b99c181ca70bd8d8c8e7b9d34c146 Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Tue, 22 Jan 2013 15:08:44 -0500 Subject: [PATCH 05/76] more tests --- lms/djangoapps/courseware/tests/test_courses.py | 2 ++ lms/djangoapps/courseware/tests/test_views.py | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lms/djangoapps/courseware/tests/test_courses.py b/lms/djangoapps/courseware/tests/test_courses.py index 7e1456efec..7cb2f8b54b 100644 --- a/lms/djangoapps/courseware/tests/test_courses.py +++ b/lms/djangoapps/courseware/tests/test_courses.py @@ -6,6 +6,7 @@ from django.contrib.auth.models import User from student.models import CourseEnrollment import courseware.courses as courses +from xmodule.modulestore.xml import XMLModuleStore class CoursesTestCase(TestCase): def setUp(self): @@ -16,6 +17,7 @@ class CoursesTestCase(TestCase): self.enrollment = CourseEnrollment.objects.get_or_create(user = self.user, course_id = self.course_id, created = self.date)[0] + self.xml_modulestore = XMLModuleStore() def test_get_course_by_id(self): courses.get_course_by_id(self.course_id) diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index ba7393f939..9bca202761 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -61,8 +61,9 @@ class ViewsTestCase(TestCase): mock_module, True) def test_index(self): - print modulestore() - assert False + pass + #print modulestore() + #assert False def test_registered_for_course(self): self.assertFalse(views.registered_for_course('Basketweaving', None)) @@ -72,3 +73,7 @@ class ViewsTestCase(TestCase): mock_course = MagicMock() mock_course.id = self.course_id self.assertTrue(views.registered_for_course(mock_course, self.user)) + + def test_jump_to(self): + self.assertRaises(Http404, views.jump_to, 'foo', 'bar', ()) + From 26ee9a24ef72d035d7554e84b5cc161da8fb3f7d Mon Sep 17 00:00:00 2001 From: Jay Zoldak Date: Tue, 22 Jan 2013 13:42:01 -0500 Subject: [PATCH 06/76] Add test for TOC rendering --- .../courseware/tests/test_module_render.py | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 lms/djangoapps/courseware/tests/test_module_render.py diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py new file mode 100644 index 0000000000..d5164d4903 --- /dev/null +++ b/lms/djangoapps/courseware/tests/test_module_render.py @@ -0,0 +1,94 @@ +import unittest +import logging + +from django.conf import settings +from django.test import TestCase +from django.test.client import RequestFactory +from override_settings import override_settings + +import factory +from django.contrib.auth.models import User + +from xmodule.modulestore.django import modulestore, _MODULESTORES +from courseware import module_render + +def xml_store_config(data_dir): + return { + 'default': { + 'ENGINE': 'xmodule.modulestore.xml.XMLModuleStore', + 'OPTIONS': { + 'data_dir': data_dir, + 'default_class': 'xmodule.hidden_module.HiddenDescriptor', + } + } +} + +TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT +TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR) + +class UserFactory(factory.Factory): + first_name = 'Test' + last_name = 'Robot' + is_staff = True + is_active = True + +@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE) +class TestTOC(TestCase): + """Check the Table of Contents for a course""" + def setUp(self): + self._MODULESTORES = {} + + # Toy courses should be loaded + self.course_name = 'edX/toy/2012_Fall' + self.toy_course = modulestore().get_course(self.course_name) + + self.portal_user = UserFactory() + + def test_toc_toy_from_chapter(self): + chapter = 'Overview' + chapter_url = '%s/%s/%s' % ('/courses', self.course_name, chapter) + factory = RequestFactory() + request = factory.get(chapter_url) + + expected = ([{'active': True, 'sections': + [{'url_name': 'Toy_Videos', 'display_name': u'Toy Videos', 'graded': True, + 'format': u'Lecture Sequence', 'due': '', 'active': False}, + {'url_name': 'Welcome', 'display_name': u'Welcome', 'graded': True, + 'format': '', 'due': '', 'active': False}, + {'url_name': 'video_123456789012', 'display_name': 'video 123456789012', 'graded': True, + 'format': '', 'due': '', 'active': False}, + {'url_name': 'video_4f66f493ac8f', 'display_name': 'video 4f66f493ac8f', 'graded': True, + 'format': '', 'due': '', 'active': False}], + 'url_name': 'Overview', 'display_name': u'Overview'}, + {'active': False, 'sections': + [{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True, + 'format': '', 'due': '', 'active': False}], + 'url_name': 'secret:magic', 'display_name': 'secret:magic'}]) + + actual = module_render.toc_for_course(self.portal_user, request, self.toy_course, chapter, None) + self.assertEqual(expected, actual) + + def test_toc_toy_from_section(self): + chapter = 'Overview' + chapter_url = '%s/%s/%s' % ('/courses', self.course_name, chapter) + section = 'Welcome' + factory = RequestFactory() + request = factory.get(chapter_url) + + expected = ([{'active': True, 'sections': + [{'url_name': 'Toy_Videos', 'display_name': u'Toy Videos', 'graded': True, + 'format': u'Lecture Sequence', 'due': '', 'active': False}, + {'url_name': 'Welcome', 'display_name': u'Welcome', 'graded': True, + 'format': '', 'due': '', 'active': True}, + {'url_name': 'video_123456789012', 'display_name': 'video 123456789012', 'graded': True, + 'format': '', 'due': '', 'active': False}, + {'url_name': 'video_4f66f493ac8f', 'display_name': 'video 4f66f493ac8f', 'graded': True, + 'format': '', 'due': '', 'active': False}], + 'url_name': 'Overview', 'display_name': u'Overview'}, + {'active': False, 'sections': + [{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True, + 'format': '', 'due': '', 'active': False}], + 'url_name': 'secret:magic', 'display_name': 'secret:magic'}]) + + actual = module_render.toc_for_course(self.portal_user, request, self.toy_course, chapter, section) + self.assertEqual(expected, actual) \ No newline at end of file From 3fd69bcae4f36405e4c5069867ac5c1d62d0b4cb Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Wed, 23 Jan 2013 10:41:12 -0500 Subject: [PATCH 07/76] more tests in test_views.py --- lms/djangoapps/courseware/tests/test_views.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index 9bca202761..cf4c792ab7 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -11,7 +11,8 @@ from django.contrib.auth.models import User from student.models import CourseEnrollment import courseware.views as views from xmodule.modulestore.django import modulestore - +from xmodule.modulestore.exceptions import InvalidLocationError,\ + ItemNotFoundError, NoPathToItem class Stub(): pass @@ -25,6 +26,7 @@ class ViewsTestCase(TestCase): self.enrollment = CourseEnrollment.objects.get_or_create(user = self.user, course_id = self.course_id, created = self.date)[0] + self.location = ['tag', 'org', 'course', 'category', 'name'] def test_user_groups(self): # depreciated function? @@ -75,5 +77,8 @@ class ViewsTestCase(TestCase): self.assertTrue(views.registered_for_course(mock_course, self.user)) def test_jump_to(self): - self.assertRaises(Http404, views.jump_to, 'foo', 'bar', ()) + mock_request = MagicMock() + self.assertRaises(Http404, views.jump_to, mock_request, 'bar', ()) + self.assertRaises(ItemNotFoundError, views.jump_to, mock_request, 'dummy', + self.location) From 4d469fd9f7d0148eacb70823b8b44998de173142 Mon Sep 17 00:00:00 2001 From: Jay Zoldak Date: Thu, 24 Jan 2013 09:55:24 -0500 Subject: [PATCH 08/76] Add test for jump_to. --- lms/djangoapps/courseware/tests/test_views.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 lms/djangoapps/courseware/tests/test_views.py diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py new file mode 100644 index 0000000000..f3206004d6 --- /dev/null +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -0,0 +1,48 @@ +import unittest +import logging + +from django.conf import settings +from django.test import TestCase +from django.test.client import RequestFactory +from override_settings import override_settings + +from xmodule.modulestore import Location +from xmodule.modulestore.django import modulestore, _MODULESTORES +from courseware import views + +def xml_store_config(data_dir): + return { + 'default': { + 'ENGINE': 'xmodule.modulestore.xml.XMLModuleStore', + 'OPTIONS': { + 'data_dir': data_dir, + 'default_class': 'xmodule.hidden_module.HiddenDescriptor', + } + } +} + +TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT +TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR) + +@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE) +class TestJumpTo(TestCase): + """Check the jumpto link for a course""" + def setUp(self): + self._MODULESTORES = {} + + # Toy courses should be loaded + self.course_name = 'edX/toy/2012_Fall' + + def test_jumpto_invalid_location(self): + location = Location('i4x', 'edX', 'toy', 'NoSuchPlace', None) + jumpto_url = '%s/%s/jump_to/%s' % ('/courses', self.course_name, location) + expected = 'courses/edX/toy/2012_Fall/courseware/Overview/' + response = self.client.get(jumpto_url) + self.assertEqual(response.status_code, 404) + + def test_jumpto_from_chapter(self): + location = Location('i4x', 'edX', 'toy', 'chapter', 'Overview') + jumpto_url = '%s/%s/jump_to/%s' % ('/courses', self.course_name, location) + expected = 'courses/edX/toy/2012_Fall/courseware/Overview/' + response = self.client.get(jumpto_url) + self.assertRedirects(response, expected, status_code=302, target_status_code=302) From 61e12f57e7f64721c969ed4ad03e1ee03474be63 Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Thu, 24 Jan 2013 09:59:33 -0500 Subject: [PATCH 09/76] experimenting with creating a course --- .../courseware/tests/test_course_creation.py | 61 ++++++++++ .../courseware/tests/test_module_render.py | 13 +-- lms/djangoapps/courseware/tests/test_views.py | 106 ++++++++++++++++-- 3 files changed, 164 insertions(+), 16 deletions(-) create mode 100644 lms/djangoapps/courseware/tests/test_course_creation.py diff --git a/lms/djangoapps/courseware/tests/test_course_creation.py b/lms/djangoapps/courseware/tests/test_course_creation.py new file mode 100644 index 0000000000..af3e3ee0e1 --- /dev/null +++ b/lms/djangoapps/courseware/tests/test_course_creation.py @@ -0,0 +1,61 @@ +import logging +from mock import MagicMock, patch +import factory +import copy +from path import path + +from django.test import TestCase +from django.test.client import Client +from django.core.urlresolvers import reverse +from django.conf import settings +from override_settings import override_settings + +from xmodule.modulestore.xml_importer import import_from_xml +import xmodule.modulestore.django + +TEST_DATA_MODULESTORE = copy.deepcopy(settings.MODULESTORE) +TEST_DATA_MODULESTORE['default']['OPTIONS']['fs_root'] = path('common/test/data') + +@override_settings(MODULESTORE=TEST_DATA_MODULESTORE) +class CreateTest(TestCase): + def setUp(self): + xmodule.modulestore.django._MODULESTORES = {} + xmodule.modulestore.django.modulestore().collection.drop() + import_from_xml(modulestore(), 'common/test/data/', [test_course_name]) + + def check_edit_item(self, test_course_name): + import_from_xml(modulestore(), 'common/test/data/', [test_course_name]) + for descriptor in modulestore().get_items(Location(None, None, None, None, None)): + print "Checking ", descriptor.location.url() + print descriptor.__class__, descriptor.location + resp = self.client.get(reverse('edit_item'), {'id': descriptor.location.url()}) + self.assertEqual(resp.status_code, 200) + + def test_edit_item_toy(self): + self.check_edit_item('toy') + +## def setUp(self): +## self.client = Client() +## self.username = 'username' +## self.email = 'test@foo.com' +## self.pw = 'password' +## +## def create_account(self, username, email, pw): +## resp = self.client.post('/create_account', { +## 'username': username, +## 'email': email, +## 'password': pw, +## 'location': 'home', +## 'language': 'Franglish', +## 'name': 'Fred Weasley', +## 'terms_of_service': 'true', +## 'honor_code': 'true', +## }) +## return resp +## +## def registration(self, email): +## '''look up registration object by email''' +## return Registration.objects.get(user__email=email) +## +## def activate_user(self, email): +## activation_key = self.registration(email).activation_key diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py index 24f21241f0..ae725638e4 100644 --- a/lms/djangoapps/courseware/tests/test_module_render.py +++ b/lms/djangoapps/courseware/tests/test_module_render.py @@ -1,26 +1,22 @@ -from unittest import TestCase import logging from mock import MagicMock, patch import json import factory +import unittest from django.http import Http404, HttpResponse, HttpRequest from django.conf import settings from django.contrib.auth.models import User from django.test.client import Client from django.conf import settings +from django.test import TestCase +from django.test.client import RequestFactory from courseware.models import StudentModule from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.modulestore import Location import courseware.module_render as render - -import unittest - -from django.test import TestCase -from django.test.client import RequestFactory from override_settings import override_settings - from xmodule.modulestore.django import modulestore, _MODULESTORES @@ -202,7 +198,8 @@ class TestTOC(TestCase): # Toy courses should be loaded self.course_name = 'edX/toy/2012_Fall' self.toy_course = modulestore().get_course(self.course_name) - + print type(self.toy_course) + assert False self.portal_user = UserFactory() def test_toc_toy_from_chapter(self): diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index cf4c792ab7..3ff14657b8 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -1,32 +1,84 @@ import logging from mock import MagicMock, patch import datetime +import factory from django.test import TestCase -from django.http import Http404 +from django.http import Http404, HttpResponse from django.conf import settings from django.test.utils import override_settings from django.contrib.auth.models import User +from django.test.client import RequestFactory from student.models import CourseEnrollment -import courseware.views as views from xmodule.modulestore.django import modulestore from xmodule.modulestore.exceptions import InvalidLocationError,\ - ItemNotFoundError, NoPathToItem + ItemNotFoundError, NoPathToItem +import courseware.views as views +from xmodule.modulestore import Location +#import mitx.common.djangoapps.mitxmako as mako class Stub(): pass +def render_to_response(template_name, dictionary, context_instance=None, + namespace='main', **kwargs): + # The original returns HttpResponse + print dir() + print template_name + print dictionary + return HttpResponse('foo') + +class UserFactory(factory.Factory): + first_name = 'Test' + last_name = 'Robot' + is_staff = True + is_active = True + +# This part is required for modulestore() to work properly +def xml_store_config(data_dir): + return { + 'default': { + 'ENGINE': 'xmodule.modulestore.xml.XMLModuleStore', + 'OPTIONS': { + 'data_dir': data_dir, + 'default_class': 'xmodule.hidden_module.HiddenDescriptor', + } + } +} + +TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT +TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR) + +class ModulestoreTest(TestCase): + def setUp(self): + self._MODULESTORES = {} + + # Toy courses should be loaded + self.course_name = 'edX/toy/2012_Fall' + self.toy_course = modulestore().get_course('edX/toy/2012_Fall') + + def test(self): + self.assertEquals(1,2) + class ViewsTestCase(TestCase): def setUp(self): self.user = User.objects.create(username='dummy', password='123456', email='test@mit.edu') self.date = datetime.datetime(2013,1,22) - self.course_id = 'edx/toy/Fall_2012' + self.course_id = 'edx/toy/2012_Fall' self.enrollment = CourseEnrollment.objects.get_or_create(user = self.user, course_id = self.course_id, created = self.date)[0] self.location = ['tag', 'org', 'course', 'category', 'name'] + self._MODULESTORES = {} + # This is a CourseDescriptor object + self.toy_course = modulestore().get_course('edX/toy/2012_Fall') + self.request_factory = RequestFactory() + # Many functions call upon render_to_response + # Problem is that we don't know what templates there are? + views.render_to_response = render_to_response + #m = mako.MakoMiddleware() def test_user_groups(self): # depreciated function? @@ -77,8 +129,46 @@ class ViewsTestCase(TestCase): self.assertTrue(views.registered_for_course(mock_course, self.user)) def test_jump_to(self): - mock_request = MagicMock() - self.assertRaises(Http404, views.jump_to, mock_request, 'bar', ()) + chapter = 'Overview' + chapter_url = '%s/%s/%s' % ('/courses', self.course_id, chapter) + request = self.request_factory.get(chapter_url) + self.assertRaisesRegexp(Http404, 'Invalid location', views.jump_to, + request, 'bar', ()) + self.assertRaisesRegexp(Http404, 'No data*', views.jump_to, request, + 'dummy', self.location) + print type(self.toy_course) + print dir(self.toy_course) + print self.toy_course.location + print self.toy_course.__dict__ + valid = ['i4x', 'edX', 'toy', 'chapter', 'overview'] + L = Location('i4x', 'edX', 'toy', 'chapter', 'Overview', None) - self.assertRaises(ItemNotFoundError, views.jump_to, mock_request, 'dummy', - self.location) + views.jump_to(request, 'dummy', L) + + def test_static_tab(self): + mock_request = MagicMock() + mock_request.user = self.user + # What is tab_slug? + #views.static_tab(mock_request, self.course_id, 'dummy') + + def test_university_profile(self): + chapter = 'Overview' + chapter_url = '%s/%s/%s' % ('/courses', self.course_id, chapter) + request = self.request_factory.get(chapter_url) + request.user = UserFactory() + self.assertRaisesRegexp(Http404, 'University Profile*', + views.university_profile, request, 'Harvard') + # Mocked out function render_to_response + self.assertIsInstance(views.university_profile(request, 'edX'), HttpResponse) + + def test_syllabus(self): + chapter = 'Overview' + chapter_url = '%s/%s/%s' % ('/courses', self.course_id, chapter) + request = self.request_factory.get(chapter_url) + request.user = UserFactory() + # course not found + views.syllabus(request, self.course_id) + + def test_render_notifications(self): + request = self.request_factory.get('foo') + views.render_notifications(request, self.course_id, 'dummy') From 9663973038a13e8cce1fd9eee0bd7ff212ca37bb Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Thu, 24 Jan 2013 14:59:46 -0500 Subject: [PATCH 10/76] more tests for courseware/views --- .../courseware/tests/test_module_render.py | 5 -- lms/djangoapps/courseware/tests/test_views.py | 70 +++++++++++++------ 2 files changed, 48 insertions(+), 27 deletions(-) diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py index ae725638e4..925601ba37 100644 --- a/lms/djangoapps/courseware/tests/test_module_render.py +++ b/lms/djangoapps/courseware/tests/test_module_render.py @@ -62,9 +62,6 @@ class ModuleRenderTestCase(TestCase): mock_module,'dummy')) def test_get_shared_instance_module(self): -## class MockUserFactory(factory.Factory): -## FACTORY_FOR = MagicMock -## is_authenticated.return_value = mock_user = MagicMock(User) mock_user.is_authenticated.return_value = False self.assertIsNone(render.get_shared_instance_module('dummy', mock_user, 'dummy', @@ -198,8 +195,6 @@ class TestTOC(TestCase): # Toy courses should be loaded self.course_name = 'edX/toy/2012_Fall' self.toy_course = modulestore().get_course(self.course_name) - print type(self.toy_course) - assert False self.portal_user = UserFactory() def test_toc_toy_from_chapter(self): diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index 57ef91c79e..fda591b2e6 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -1,9 +1,9 @@ -<<<<<<< HEAD import logging from mock import MagicMock, patch import datetime import factory import unittest +import os from django.test import TestCase from django.http import Http404, HttpResponse @@ -25,13 +25,13 @@ from xmodule.modulestore import Location class Stub(): pass -def render_to_response(template_name, dictionary, context_instance=None, - namespace='main', **kwargs): - # The original returns HttpResponse - print dir() - print template_name - print dictionary - return HttpResponse('foo') +##def render_to_response(template_name, dictionary, context_instance=None, +## namespace='main', **kwargs): +## # The original returns HttpResponse +## print dir() +## print template_name +## print dictionary +## return HttpResponse('foo') class UserFactory(factory.Factory): first_name = 'Test' @@ -56,7 +56,7 @@ TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR) -class ModulestoreTest(TestCase): +#class ModulestoreTest(TestCase): @override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE) class TestJumpTo(TestCase): @@ -68,9 +68,6 @@ class TestJumpTo(TestCase): self.course_name = 'edX/toy/2012_Fall' self.toy_course = modulestore().get_course('edX/toy/2012_Fall') - def test(self): - self.assertEquals(1,2) - def test_jumpto_invalid_location(self): location = Location('i4x', 'edX', 'toy', 'NoSuchPlace', None) jumpto_url = '%s/%s/jump_to/%s' % ('/courses', self.course_name, location) @@ -101,11 +98,11 @@ class ViewsTestCase(TestCase): self.request_factory = RequestFactory() # Many functions call upon render_to_response # Problem is that we don't know what templates there are? - views.render_to_response = render_to_response + #views.render_to_response = render_to_response #m = mako.MakoMiddleware() def test_user_groups(self): - # depreciated function? + # depreciated function mock_user = MagicMock() mock_user.is_authenticated.return_value = False self.assertEquals(views.user_groups(mock_user),[]) @@ -160,10 +157,10 @@ class ViewsTestCase(TestCase): request, 'bar', ()) self.assertRaisesRegexp(Http404, 'No data*', views.jump_to, request, 'dummy', self.location) - print type(self.toy_course) - print dir(self.toy_course) - print self.toy_course.location - print self.toy_course.__dict__ +## print type(self.toy_course) +## print dir(self.toy_course) +## print self.toy_course.location +## print self.toy_course.__dict__ valid = ['i4x', 'edX', 'toy', 'chapter', 'overview'] L = Location('i4x', 'edX', 'toy', 'chapter', 'Overview', None) @@ -172,18 +169,32 @@ class ViewsTestCase(TestCase): def test_static_tab(self): mock_request = MagicMock() mock_request.user = self.user - # What is tab_slug? + # What is tab_slug? A string? #views.static_tab(mock_request, self.course_id, 'dummy') + def test_static_university_profile(self): + # TODO + # Can't test unless have a valid template file +## request = self.client.get('university_profile/edX') +## views.static_university_profile(request, 'edX') + pass + + def test_university_profile(self): + chapter = 'Overview' chapter_url = '%s/%s/%s' % ('/courses', self.course_id, chapter) request = self.request_factory.get(chapter_url) request.user = UserFactory() self.assertRaisesRegexp(Http404, 'University Profile*', views.university_profile, request, 'Harvard') - # Mocked out function render_to_response - self.assertIsInstance(views.university_profile(request, 'edX'), HttpResponse) + # Supposed to return a HttpResponse object + # Templates don't exist because not in database + # TODO + # assertTemplateUsed is called on an HttpResponse, but + #request_2 = self.client.get('/university_profile/edx') + #self.assertIsInstance(views.university_profile(request, 'edX'), HttpResponse) + # Can't continue testing unless have valid template file def test_syllabus(self): chapter = 'Overview' @@ -191,8 +202,23 @@ class ViewsTestCase(TestCase): request = self.request_factory.get(chapter_url) request.user = UserFactory() # course not found + # TODO views.syllabus(request, self.course_id) def test_render_notifications(self): request = self.request_factory.get('foo') - views.render_notifications(request, self.course_id, 'dummy') + #views.render_notifications(request, self.course_id, 'dummy') + # TODO + # Needs valid template + + def test_news(self): + #print settings.TEMPLATE_DIRS + #assert False + # I want news to get all the way to render_to_response + # Bug? get_notifications is actually in lms/lib/comment_client/legacy.py + request = self.client.get('/news') + self.user.id = 'foo' + request.user = self.user + course_id = 'edX/toy/2012_Fall' + views.news(request, course_id) + # TODO From f9b0ec37cd760750bdc2522d22184d1b0cb66a39 Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Thu, 24 Jan 2013 16:08:19 -0500 Subject: [PATCH 11/76] more tests in test_views, needs templates to continue --- lms/djangoapps/courseware/tests/test_views.py | 67 +++++++++++-------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index fda591b2e6..52496bcb6b 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -4,6 +4,7 @@ import datetime import factory import unittest import os +from nose.plugins.skip import SkipTest from django.test import TestCase from django.http import Http404, HttpResponse @@ -19,6 +20,12 @@ from xmodule.modulestore.exceptions import InvalidLocationError,\ import courseware.views as views from xmodule.modulestore import Location +def skipped(func): + from nose.plugins.skip import SkipTest + def _(): + raise SkipTest("Test %s is skipped" % func.__name__) + _.__name__ = func.__name__ + return _ #from override_settings import override_settings @@ -39,8 +46,14 @@ class UserFactory(factory.Factory): is_staff = True is_active = True -# This part is required for modulestore() to work properly +def skipped(func): + from nose.plugins.skip import SkipTest + def _(): + raise SkipTest("Test %s is skipped" % func.__name__) + _.__name__ = func.__name__ + return _ +# This part is required for modulestore() to work properly def xml_store_config(data_dir): return { 'default': { @@ -96,10 +109,6 @@ class ViewsTestCase(TestCase): # This is a CourseDescriptor object self.toy_course = modulestore().get_course('edX/toy/2012_Fall') self.request_factory = RequestFactory() - # Many functions call upon render_to_response - # Problem is that we don't know what templates there are? - #views.render_to_response = render_to_response - #m = mako.MakoMiddleware() def test_user_groups(self): # depreciated function @@ -161,49 +170,50 @@ class ViewsTestCase(TestCase): ## print dir(self.toy_course) ## print self.toy_course.location ## print self.toy_course.__dict__ - valid = ['i4x', 'edX', 'toy', 'chapter', 'overview'] - L = Location('i4x', 'edX', 'toy', 'chapter', 'Overview', None) - - views.jump_to(request, 'dummy', L) +## valid = ['i4x', 'edX', 'toy', 'chapter', 'overview'] +## L = Location('i4x', 'edX', 'toy', 'chapter', 'Overview', None) +## +## views.jump_to(request, 'dummy', L) def test_static_tab(self): - mock_request = MagicMock() - mock_request.user = self.user - # What is tab_slug? A string? - #views.static_tab(mock_request, self.course_id, 'dummy') - + request = self.request_factory.get('foo') + request.user = self.user + self.assertRaises(Http404, views.static_tab, request, 'edX/toy/2012_Fall', + 'dummy') + # What are valid tab_slugs? +## request_2 = self.request_factory.get('foo') +## request_2.user = UserFactory() + def test_static_university_profile(self): # TODO # Can't test unless have a valid template file -## request = self.client.get('university_profile/edX') -## views.static_university_profile(request, 'edX') - pass + raise SkipTest + request = self.client.get('university_profile/edX') + self.assertIsInstance(views.static_university_profile(request, 'edX'), HttpResponse) - def test_university_profile(self): - + raise SkipTest chapter = 'Overview' chapter_url = '%s/%s/%s' % ('/courses', self.course_id, chapter) request = self.request_factory.get(chapter_url) request.user = UserFactory() self.assertRaisesRegexp(Http404, 'University Profile*', views.university_profile, request, 'Harvard') - # Supposed to return a HttpResponse object - # Templates don't exist because not in database # TODO - # assertTemplateUsed is called on an HttpResponse, but #request_2 = self.client.get('/university_profile/edx') - #self.assertIsInstance(views.university_profile(request, 'edX'), HttpResponse) + self.assertIsInstance(views.university_profile(request, 'edX'), HttpResponse) # Can't continue testing unless have valid template file + def test_syllabus(self): + raise SkipTest chapter = 'Overview' chapter_url = '%s/%s/%s' % ('/courses', self.course_id, chapter) request = self.request_factory.get(chapter_url) request.user = UserFactory() - # course not found + # Can't find valid template # TODO - views.syllabus(request, self.course_id) + views.syllabus(request, 'edX/toy/2012_Fall') def test_render_notifications(self): request = self.request_factory.get('foo') @@ -212,13 +222,12 @@ class ViewsTestCase(TestCase): # Needs valid template def test_news(self): - #print settings.TEMPLATE_DIRS - #assert False - # I want news to get all the way to render_to_response + raise SkipTest # Bug? get_notifications is actually in lms/lib/comment_client/legacy.py request = self.client.get('/news') self.user.id = 'foo' request.user = self.user course_id = 'edX/toy/2012_Fall' - views.news(request, course_id) + self.assertIsInstance(views.news(request, course_id), HttpResponse) + # TODO From 583505b95ba8a2a803e4130829e9c19c8e586533 Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Fri, 25 Jan 2013 12:19:16 -0500 Subject: [PATCH 12/76] test_courses.py --- .../courseware/tests/test_courses.py | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/lms/djangoapps/courseware/tests/test_courses.py b/lms/djangoapps/courseware/tests/test_courses.py index 7cb2f8b54b..2f4868b27b 100644 --- a/lms/djangoapps/courseware/tests/test_courses.py +++ b/lms/djangoapps/courseware/tests/test_courses.py @@ -3,21 +3,59 @@ import datetime from django.test import TestCase from django.contrib.auth.models import User +from django.conf import settings +from django.test.utils import override_settings from student.models import CourseEnrollment import courseware.courses as courses from xmodule.modulestore.xml import XMLModuleStore +from xmodule.modulestore.django import modulestore +def xml_store_config(data_dir): + return { + 'default': { + 'ENGINE': 'xmodule.modulestore.xml.XMLModuleStore', + 'OPTIONS': { + 'data_dir': data_dir, + 'default_class': 'xmodule.hidden_module.HiddenDescriptor', + } + } +} + +TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT +TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR) + +@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE) class CoursesTestCase(TestCase): def setUp(self): self.user = User.objects.create(username='dummy', password='123456', email='test@mit.edu') self.date = datetime.datetime(2013,1,22) - self.course_id = 'edx/toy/Fall_2012' + self.course_id = 'edx/toy/2012_Fall' self.enrollment = CourseEnrollment.objects.get_or_create(user = self.user, course_id = self.course_id, created = self.date)[0] - self.xml_modulestore = XMLModuleStore() + self._MODULESTORES = {} + self.toy_course = modulestore().get_course('edX/toy/2012_Fall') def test_get_course_by_id(self): - courses.get_course_by_id(self.course_id) + courses.get_course_by_id("edx/toy/2012_Fall") + +@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE) +class CoursesTests(TestCase): + def setUp(self): + self._MODULESTORES = {} + self.course_name = 'edX/toy/2012_Fall' + self.toy_course = modulestore().get_course('edX/toy/2012_Fall') + self.fake_user = User.objects.create(is_superuser=True) + + ''' + no test written for get_request_for_thread + ''' + + def test_get_course_by_id(self): + self.test_course_id = "edX/toy/2012_Fall" + # print modulestore().get_instance(test_course_id, Location('i4x', 'edx', 'toy', 'course', '2012_Fall')) + self.assertEqual(courses.get_course_by_id(self.test_course_id),modulestore().get_instance(self.test_course_id, Location('i4x', 'edX', 'toy', 'course', '2012_Fall'), None)) + + From 7918c35414ca2f6d9cfe7205b684902deaf43ae9 Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Mon, 28 Jan 2013 15:29:55 -0500 Subject: [PATCH 13/76] fixing up tests --- .../courseware/tests/test_courses.py | 30 ++-- .../courseware/tests/test_module_render.py | 130 ++++++++++++------ lms/djangoapps/courseware/tests/test_views.py | 44 +++--- 3 files changed, 126 insertions(+), 78 deletions(-) diff --git a/lms/djangoapps/courseware/tests/test_courses.py b/lms/djangoapps/courseware/tests/test_courses.py index 2f4868b27b..91b6af4dfc 100644 --- a/lms/djangoapps/courseware/tests/test_courses.py +++ b/lms/djangoapps/courseware/tests/test_courses.py @@ -10,6 +10,7 @@ from student.models import CourseEnrollment import courseware.courses as courses from xmodule.modulestore.xml import XMLModuleStore from xmodule.modulestore.django import modulestore +from xmodule.modulestore import Location def xml_store_config(data_dir): return { @@ -28,34 +29,35 @@ TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR) @override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE) class CoursesTestCase(TestCase): def setUp(self): - self.user = User.objects.create(username='dummy', password='123456', - email='test@mit.edu') - self.date = datetime.datetime(2013,1,22) - self.course_id = 'edx/toy/2012_Fall' - self.enrollment = CourseEnrollment.objects.get_or_create(user = self.user, - course_id = self.course_id, - created = self.date)[0] +## self.user = User.objects.create(username='dummy', password='123456', +## email='test@mit.edu') +## self.date = datetime.datetime(2013,1,22) +## self.enrollment = CourseEnrollment.objects.get_or_create(user = self.user, +## course_id = self.course_id, +## created = self.date)[0] self._MODULESTORES = {} + #self.course_id = 'edx/toy/2012_Fall' self.toy_course = modulestore().get_course('edX/toy/2012_Fall') def test_get_course_by_id(self): - courses.get_course_by_id("edx/toy/2012_Fall") + courses.get_course_by_id("edX/toy/2012_Fall") + @override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE) class CoursesTests(TestCase): + # runs def setUp(self): self._MODULESTORES = {} - self.course_name = 'edX/toy/2012_Fall' + #self.course_id = 'edX/toy/2012_Fall' self.toy_course = modulestore().get_course('edX/toy/2012_Fall') - self.fake_user = User.objects.create(is_superuser=True) +## self.fake_user = User.objects.create(is_superuser=True) ''' no test written for get_request_for_thread ''' def test_get_course_by_id(self): - self.test_course_id = "edX/toy/2012_Fall" + #self.test_course_id = "edX/toy/2012_Fall" + courses.get_course_by_id("edX/toy/2012_Fall") # print modulestore().get_instance(test_course_id, Location('i4x', 'edx', 'toy', 'course', '2012_Fall')) - self.assertEqual(courses.get_course_by_id(self.test_course_id),modulestore().get_instance(self.test_course_id, Location('i4x', 'edX', 'toy', 'course', '2012_Fall'), None)) - - + #self.assertEqual(courses.get_course_by_id(self.test_course_id),modulestore().get_instance(self.test_course_id, Location('i4x', 'edX', 'toy', 'course', '2012_Fall'), None)) diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py index 925601ba37..3150450648 100644 --- a/lms/djangoapps/courseware/tests/test_module_render.py +++ b/lms/djangoapps/courseware/tests/test_module_render.py @@ -2,7 +2,9 @@ import logging from mock import MagicMock, patch import json import factory -import unittest +import unittest +from nose.tools import set_trace +from nose.plugins.skip import SkipTest from django.http import Http404, HttpResponse, HttpRequest from django.conf import settings @@ -11,22 +13,50 @@ from django.test.client import Client from django.conf import settings from django.test import TestCase from django.test.client import RequestFactory +from django.core.urlresolvers import reverse -from courseware.models import StudentModule +from courseware.models import StudentModule, StudentModuleCache from xmodule.modulestore.exceptions import ItemNotFoundError +from xmodule.exceptions import NotFoundError from xmodule.modulestore import Location import courseware.module_render as render from override_settings import override_settings from xmodule.modulestore.django import modulestore, _MODULESTORES - +from xmodule.seq_module import SequenceModule +from courseware.tests.tests import PageLoader +from student.models import Registration class Stub: def __init__(self): pass -class ModuleRenderTestCase(TestCase): +def xml_store_config(data_dir): + return { + 'default': { + 'ENGINE': 'xmodule.modulestore.xml.XMLModuleStore', + 'OPTIONS': { + 'data_dir': data_dir, + 'default_class': 'xmodule.hidden_module.HiddenDescriptor', + } + } +} + +class UserFactory(factory.Factory): + first_name = 'Test' + last_name = 'Robot' + is_staff = True + is_active = True + +TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT +TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR) + +@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE) +class ModuleRenderTestCase(PageLoader): def setUp(self): - self.location = ['tag', 'org', 'course', 'category', 'name'] + self.location = ['i4x', 'edX', 'toy', 'chapter', 'Overview'] + self._MODULESTORES = {} + self.course_id = 'edX/toy/2012_Fall' + self.toy_course = modulestore().get_course(self.course_id) def test_toc_for_course(self): mock_course = MagicMock() @@ -37,19 +67,26 @@ class ModuleRenderTestCase(TestCase): mock_user.is_authenticated.return_value = False self.assertIsNone(render.toc_for_course(mock_user,'dummy', mock_course, 'dummy', 'dummy')) + # rest of tests are in class TestTOC def test_get_module(self): self.assertIsNone(render.get_module('dummyuser',None,\ 'invalid location',None,None)) - + #done def test__get_module(self): mock_user = MagicMock() - mock_user.is_authenticated.return_value = True - location = ['tag', 'org', 'course', 'category', 'name'] - #render._get_module(mock_user, + mock_user.is_authenticated.return_value = False + location = Location('i4x', 'edX', 'toy', 'chapter', 'Overview') + mock_request = MagicMock() + s = render._get_module(mock_user, mock_request, location, + 'dummy', 'edX/toy/2012_Fall') + self.assertIsInstance(s, SequenceModule) + # Don't know how to generate error in line 260 to test + # Can't tell if sequence module is an error? def test_get_instance_module(self): + # done mock_user = MagicMock() mock_user.is_authenticated.return_value = False self.assertIsNone(render.get_instance_module('dummy', mock_user, 'dummy', @@ -62,16 +99,28 @@ class ModuleRenderTestCase(TestCase): mock_module,'dummy')) def test_get_shared_instance_module(self): + raise SkipTest mock_user = MagicMock(User) mock_user.is_authenticated.return_value = False self.assertIsNone(render.get_shared_instance_module('dummy', mock_user, 'dummy', 'dummy')) mock_user_2 = MagicMock(User) mock_user_2.is_authenticated.return_value = True + mock_module = MagicMock() mock_module.shared_state_key = 'key' - self.assertIsInstance(render.get_shared_instance_module('dummy', mock_user, - mock_module, 'dummy'), StudentModule) + mock_module.location = Location('i4x', 'edX', 'toy', 'chapter', 'Overview') + mock_module.get_shared_state.return_value = '{}' + mock_cache = MagicMock() + mock_cache.lookup.return_value = False + #mock_cache._state = 'dummy' + #set_trace() + print mock_module.get_shared_state() + s = render.get_shared_instance_module(self.course_id, mock_user_2, + mock_module, mock_cache) + self.assertIsInstance(s, StudentModule) + # Problem: can't get code to take branch that creates StudentModule? + # Can't finish testing modx_dispatch def test_xqueue_callback(self): mock_request = MagicMock() @@ -90,9 +139,21 @@ class ModuleRenderTestCase(TestCase): xpackage_2 = {'xqueue_header': json.dumps({'lms_key':'secretkey'}), 'xqueue_body' : 'Message from grader'} mock_request_3.POST.copy.return_value = xpackage_2 -## self.assertRaises(Http404, render.xqueue_callback, mock_request_3, -## 'dummy', 0, 'dummy', 'dummy') - # continue later + # Roadblock: how to get user registered in class? + raise SkipTest + # + # trying alternate way of creating account in hopes of getting valid id + # Problem: Can't activate user + + self.student_name = '12' + self.password = 'foo' + self.email = 'test@mit.edu' + self.create_account(self.student_name, self.email, self.password) + self.activate_user(self.email) + request = RequestFactory().get('stuff') + # This doesn't work to install user + render.xqueue_callback(mock_request_3, self.course_id, + self.student_name, self.password, 'dummy') def test_modx_dispatch(self): self.assertRaises(Http404, render.modx_dispatch, 'dummy', 'dummy', @@ -117,12 +178,23 @@ class ModuleRenderTestCase(TestCase): (inputfile.name, settings.STUDENT_FILEUPLOAD_MAX_SIZE/(1000**2))})) mock_request_3 = MagicMock() mock_request_3.POST.copy.return_value = {} + mock_request_3.FILES = False + mock_request_3.user = UserFactory() inputfile_2 = Stub() inputfile_2.size = 1 inputfile_2.name = 'name' self.assertRaises(ItemNotFoundError, render.modx_dispatch, mock_request_3, 'dummy', self.location, 'toy') - # Deadend + self.assertRaises(Http404,render.modx_dispatch, mock_request_3, 'dummy', + self.location, self.course_id) +## student_module_cache = StudentModuleCache.cache_for_descriptor_descendents(self.course_id, +## mock_request_3.user, modulestore().get_instance(self.course_id, self.location)) +## get_shared_instance_module(course_id, request.user, instance, student_module_cache) + # 'goto_position' is the only dispatch that will work + mock_request_3.POST.copy.return_value = {'position':1} + self.assertIsInstance(render.modx_dispatch(mock_request_3, 'goto_position', + self.location, self.course_id), HttpResponse) + # keep going def test_preview_chemcalc(self): mock_request = MagicMock() @@ -159,33 +231,6 @@ class ModuleRenderTestCase(TestCase): self.assertEquals(render.get_score_bucket(11, 10), 'incorrect') self.assertEquals(render.get_score_bucket(-1, 10), 'incorrect') - -class MagicMockFactory(factory.Factory): - FACTORY_FOR = MagicMock - v = factory.LazyAttribute(i for i in [True, False, False]) - - - -def xml_store_config(data_dir): - return { - 'default': { - 'ENGINE': 'xmodule.modulestore.xml.XMLModuleStore', - 'OPTIONS': { - 'data_dir': data_dir, - 'default_class': 'xmodule.hidden_module.HiddenDescriptor', - } - } -} - -TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT -TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR) - -class UserFactory(factory.Factory): - first_name = 'Test' - last_name = 'Robot' - is_staff = True - is_active = True - @override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE) class TestTOC(TestCase): """Check the Table of Contents for a course""" @@ -245,3 +290,4 @@ class TestTOC(TestCase): actual = render.toc_for_course(self.portal_user, request, self.toy_course, chapter, section) self.assertEqual(expected, actual) + diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index 52496bcb6b..c901f87720 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -32,14 +32,6 @@ def skipped(func): class Stub(): pass -##def render_to_response(template_name, dictionary, context_instance=None, -## namespace='main', **kwargs): -## # The original returns HttpResponse -## print dir() -## print template_name -## print dictionary -## return HttpResponse('foo') - class UserFactory(factory.Factory): first_name = 'Test' last_name = 'Robot' @@ -100,7 +92,7 @@ class ViewsTestCase(TestCase): self.user = User.objects.create(username='dummy', password='123456', email='test@mit.edu') self.date = datetime.datetime(2013,1,22) - self.course_id = 'edx/toy/2012_Fall' + self.course_id = 'edX/toy/2012_Fall' self.enrollment = CourseEnrollment.objects.get_or_create(user = self.user, course_id = self.course_id, created = self.date)[0] @@ -109,6 +101,9 @@ class ViewsTestCase(TestCase): # This is a CourseDescriptor object self.toy_course = modulestore().get_course('edX/toy/2012_Fall') self.request_factory = RequestFactory() + chapter = 'Overview' + self.chapter_url = '%s/%s/%s' % ('/courses', self.course_id, chapter) + def test_user_groups(self): # depreciated function @@ -145,9 +140,19 @@ class ViewsTestCase(TestCase): mock_module, True) def test_index(self): - pass - #print modulestore() - #assert False + assert SkipTest + request = self.request_factory.get(self.chapter_url) + request.user = UserFactory() + response = views.index(request, self.course_id) + self.assertIsInstance(response, HttpResponse) + self.assertEqual(response.status_code, 302) + # views.index does not throw 404 if chapter, section, or position are + # not valid, which doesn't match index's comments + views.index(request, self.course_id, chapter='foo', section='bar', + position='baz') + request_2 = self.request_factory.get(self.chapter_url) + request_2.user = self.user + response = views.index(request_2, self.course_id) def test_registered_for_course(self): self.assertFalse(views.registered_for_course('Basketweaving', None)) @@ -159,9 +164,7 @@ class ViewsTestCase(TestCase): self.assertTrue(views.registered_for_course(mock_course, self.user)) def test_jump_to(self): - chapter = 'Overview' - chapter_url = '%s/%s/%s' % ('/courses', self.course_id, chapter) - request = self.request_factory.get(chapter_url) + request = self.request_factory.get(self.chapter_url) self.assertRaisesRegexp(Http404, 'Invalid location', views.jump_to, request, 'bar', ()) self.assertRaisesRegexp(Http404, 'No data*', views.jump_to, request, @@ -186,16 +189,14 @@ class ViewsTestCase(TestCase): def test_static_university_profile(self): # TODO - # Can't test unless have a valid template file + # Can't test unless havehttp://toastdriven.com/blog/2011/apr/10/guide-to-testing-in-django/ a valid template file raise SkipTest request = self.client.get('university_profile/edX') self.assertIsInstance(views.static_university_profile(request, 'edX'), HttpResponse) def test_university_profile(self): raise SkipTest - chapter = 'Overview' - chapter_url = '%s/%s/%s' % ('/courses', self.course_id, chapter) - request = self.request_factory.get(chapter_url) + request = self.request_factory.get(self.chapter_url) request.user = UserFactory() self.assertRaisesRegexp(Http404, 'University Profile*', views.university_profile, request, 'Harvard') @@ -207,15 +208,14 @@ class ViewsTestCase(TestCase): def test_syllabus(self): raise SkipTest - chapter = 'Overview' - chapter_url = '%s/%s/%s' % ('/courses', self.course_id, chapter) - request = self.request_factory.get(chapter_url) + request = self.request_factory.get(self.chapter_url) request.user = UserFactory() # Can't find valid template # TODO views.syllabus(request, 'edX/toy/2012_Fall') def test_render_notifications(self): + raise SkipTest request = self.request_factory.get('foo') #views.render_notifications(request, self.course_id, 'dummy') # TODO From 74839663ff163576bef15ac951c6eba1a6d6dddf Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Tue, 29 Jan 2013 10:12:39 -0500 Subject: [PATCH 14/76] added cms/djangoapps/contentstore/test/tests.py --- .../contentstore/tests/test_views.py | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 cms/djangoapps/contentstore/tests/test_views.py diff --git a/cms/djangoapps/contentstore/tests/test_views.py b/cms/djangoapps/contentstore/tests/test_views.py new file mode 100644 index 0000000000..f513eef7f9 --- /dev/null +++ b/cms/djangoapps/contentstore/tests/test_views.py @@ -0,0 +1,50 @@ +import logging +from mock import MagicMock, patch +import json +import factory +import unittest +from nose.tools import set_trace +from nose.plugins.skip import SkipTest + +from django.http import Http404, HttpResponse, HttpRequest +from django.conf import settings +from django.contrib.auth.models import User +from django.test.client import Client +from django.conf import settings +from django.test import TestCase +from django.test.client import RequestFactory + +import cms.djangoapps.contentstore.views as views + +def xml_store_config(data_dir): + return { + 'default': { + 'ENGINE': 'xmodule.modulestore.xml.XMLModuleStore', + 'OPTIONS': { + 'data_dir': data_dir, + 'default_class': 'xmodule.hidden_module.HiddenDescriptor', + } + } +} + +class UserFactory(factory.Factory): + first_name = 'Test' + last_name = 'Robot' + is_staff = True + is_active = True + is_authenticated = True + +TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT +TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR) + +@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE) +class ViewsTestCase(TestCase): + def setUp(self): + self.location = ['i4x', 'edX', 'toy', 'chapter', 'Overview'] + self._MODULESTORES = {} + self.course_id = 'edX/toy/2012_Fall' + self.toy_course = modulestore().get_course(self.course_id) + + def test_has_access(self): + user = UserFactory() + views.has_access(user, self.location) From 85c412fc2eafefd0240a3c0081532d633b045a37 Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Tue, 29 Jan 2013 11:06:32 -0500 Subject: [PATCH 15/76] changed cms/env/tests, added test_views.py --- cms/djangoapps/contentstore/tests/test_views.py | 10 +++++++--- cms/envs/test.py | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/cms/djangoapps/contentstore/tests/test_views.py b/cms/djangoapps/contentstore/tests/test_views.py index f513eef7f9..ae6548bd9d 100644 --- a/cms/djangoapps/contentstore/tests/test_views.py +++ b/cms/djangoapps/contentstore/tests/test_views.py @@ -13,8 +13,11 @@ from django.test.client import Client from django.conf import settings from django.test import TestCase from django.test.client import RequestFactory +from override_settings import override_settings + +from xmodule.modulestore.django import modulestore, _MODULESTORES +import contentstore.views as views -import cms.djangoapps.contentstore.views as views def xml_store_config(data_dir): return { @@ -32,7 +35,6 @@ class UserFactory(factory.Factory): last_name = 'Robot' is_staff = True is_active = True - is_authenticated = True TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR) @@ -47,4 +49,6 @@ class ViewsTestCase(TestCase): def test_has_access(self): user = UserFactory() - views.has_access(user, self.location) + user.is_authenticated = True + set_trace() + self.assertTrue(views.has_access(user, self.location)) diff --git a/cms/envs/test.py b/cms/envs/test.py index d55c309827..e31037b04f 100644 --- a/cms/envs/test.py +++ b/cms/envs/test.py @@ -25,6 +25,9 @@ STATIC_ROOT = TEST_ROOT / "staticfiles" GITHUB_REPO_ROOT = TEST_ROOT / "data" COMMON_TEST_DATA_ROOT = COMMON_ROOT / "test" / "data" +# Makes the tests run much faster... +SOUTH_TESTS_MIGRATE = False # To disable migrations and use syncdb instead + # TODO (cpennington): We need to figure out how envs/test.py can inject things into common.py so that we don't have to repeat this sort of thing STATICFILES_DIRS = [ COMMON_ROOT / "static", From d9fdccb567924f04ec420e95bfe5c37e5bee751a Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Tue, 29 Jan 2013 14:23:55 -0500 Subject: [PATCH 16/76] testing testing tests --- .../contentstore/tests/test_views.py | 45 ++++++++++++++++--- .../courseware/tests/test_module_render.py | 9 ++-- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/cms/djangoapps/contentstore/tests/test_views.py b/cms/djangoapps/contentstore/tests/test_views.py index ae6548bd9d..c1fa19624c 100644 --- a/cms/djangoapps/contentstore/tests/test_views.py +++ b/cms/djangoapps/contentstore/tests/test_views.py @@ -6,7 +6,7 @@ import unittest from nose.tools import set_trace from nose.plugins.skip import SkipTest -from django.http import Http404, HttpResponse, HttpRequest +from django.http import Http404, HttpResponse, HttpRequest, HttpResponseRedirect from django.conf import settings from django.contrib.auth.models import User from django.test.client import Client @@ -14,9 +14,12 @@ from django.conf import settings from django.test import TestCase from django.test.client import RequestFactory from override_settings import override_settings +from django.core.exceptions import PermissionDenied from xmodule.modulestore.django import modulestore, _MODULESTORES import contentstore.views as views +import auth.authz as a +from contentstore.tests.factories import XModuleCourseFactory, CourseFactory def xml_store_config(data_dir): @@ -39,16 +42,46 @@ class UserFactory(factory.Factory): TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR) -@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE) class ViewsTestCase(TestCase): def setUp(self): self.location = ['i4x', 'edX', 'toy', 'chapter', 'Overview'] + self.location_2 = ['i4x', 'edX', 'full', 'course', '6.002_Spring_2012'] + # empty Modulestore self._MODULESTORES = {} self.course_id = 'edX/toy/2012_Fall' + self.course_id_2 = 'edx/full/6.002_Spring_2012' self.toy_course = modulestore().get_course(self.course_id) def test_has_access(self): - user = UserFactory() - user.is_authenticated = True - set_trace() - self.assertTrue(views.has_access(user, self.location)) + user = MagicMock(is_staff = True, is_active = True, is_authenticated = True) + m = MagicMock() + m.count.return_value = 1 + user.groups.filter.return_value = m + self.assertTrue(views.has_access(user, self.location_2)) + user.is_authenticated = False + self.assertFalse(views.has_access(user, self.location_2)) + + def test_course_index(self): + # UserFactory doesn't work? + self.user = MagicMock(is_staff = False, is_active = False) + self.user.is_authenticated.return_value = False + request = MagicMock(user = self.user) + # Instead of raising exception when has_access is False, redirects + self.assertIsInstance(views.course_index(request, 'edX', + 'full', '6.002_Spring_2012'), HttpResponseRedirect) + self.user_2 = MagicMock(is_staff = True, is_active = True) + self.user_2.is_authenticated.return_value = True + request_2 = MagicMock(user = self.user_2) + # Bug? Raises error because calls modulestore().get_item(location) + #NotImplementedError: XMLModuleStores can't guarantee that definitions + #are unique. Use get_instance. + print views.course_index(request_2, 'edX', + 'full', '6.002_Spring_2012') + + def test_edit_subsection(self): + self.user = MagicMock(is_staff = False, is_active = False) + self.user.is_authenticated.return_value = False + self.request = MagicMock(user = self.user) + self.assertIsInstance(views.edit_subscription(self.request, self.location_2), + HttpResponseRedirect) + diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py index 3150450648..f419e6f582 100644 --- a/lms/djangoapps/courseware/tests/test_module_render.py +++ b/lms/djangoapps/courseware/tests/test_module_render.py @@ -107,8 +107,7 @@ class ModuleRenderTestCase(PageLoader): mock_user_2 = MagicMock(User) mock_user_2.is_authenticated.return_value = True - mock_module = MagicMock() - mock_module.shared_state_key = 'key' + mock_module = MagicMock(shared_state_key = 'key') mock_module.location = Location('i4x', 'edX', 'toy', 'chapter', 'Overview') mock_module.get_shared_state.return_value = '{}' mock_cache = MagicMock() @@ -197,11 +196,9 @@ class ModuleRenderTestCase(PageLoader): # keep going def test_preview_chemcalc(self): - mock_request = MagicMock() - mock_request.method = 'notGET' + mock_request = MagicMock(method = 'notGET') self.assertRaises(Http404, render.preview_chemcalc, mock_request) - mock_request_2 = MagicMock() - mock_request_2.method = 'GET' + mock_request_2 = MagicMock(method = 'GET') mock_request_2.GET.get.return_value = None self.assertEquals(render.preview_chemcalc(mock_request_2).content, json.dumps({'preview':'', From 3858362157d1638f222fc5e8da893bdbe38262d9 Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Tue, 29 Jan 2013 15:53:20 -0500 Subject: [PATCH 17/76] more tests --- cms/djangoapps/contentstore/tests/test_views.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/cms/djangoapps/contentstore/tests/test_views.py b/cms/djangoapps/contentstore/tests/test_views.py index c1fa19624c..ed18e1cc8c 100644 --- a/cms/djangoapps/contentstore/tests/test_views.py +++ b/cms/djangoapps/contentstore/tests/test_views.py @@ -50,8 +50,14 @@ class ViewsTestCase(TestCase): self._MODULESTORES = {} self.course_id = 'edX/toy/2012_Fall' self.course_id_2 = 'edx/full/6.002_Spring_2012' - self.toy_course = modulestore().get_course(self.course_id) + #self.toy_course = modulestore().get_course(self.course_id) + # Problem: Classes persist, need to delete stuff from modulestore + self.course = CourseFactory.create() + print dir(self.course) + def tearDown(self): + pass + def test_has_access(self): user = MagicMock(is_staff = True, is_active = True, is_authenticated = True) m = MagicMock() @@ -72,16 +78,15 @@ class ViewsTestCase(TestCase): self.user_2 = MagicMock(is_staff = True, is_active = True) self.user_2.is_authenticated.return_value = True request_2 = MagicMock(user = self.user_2) - # Bug? Raises error because calls modulestore().get_item(location) - #NotImplementedError: XMLModuleStores can't guarantee that definitions - #are unique. Use get_instance. + # Need to use XModuleStoreFactory? print views.course_index(request_2, 'edX', 'full', '6.002_Spring_2012') def test_edit_subsection(self): + # Redirects if request.user doesn't have access to location self.user = MagicMock(is_staff = False, is_active = False) self.user.is_authenticated.return_value = False self.request = MagicMock(user = self.user) - self.assertIsInstance(views.edit_subscription(self.request, self.location_2), + self.assertIsInstance(views.edit_subsection(self.request, self.location_2), HttpResponseRedirect) From cdbe9857d67b3c71255be512e47e440a5a0e10b2 Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Wed, 30 Jan 2013 12:30:48 -0500 Subject: [PATCH 18/76] tests in cms.djangoapps.contentstore/tests/test_views.py --- .../contentstore/tests/test_views.py | 64 ++++++++++++++++--- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/cms/djangoapps/contentstore/tests/test_views.py b/cms/djangoapps/contentstore/tests/test_views.py index ed18e1cc8c..87dd7f7ac6 100644 --- a/cms/djangoapps/contentstore/tests/test_views.py +++ b/cms/djangoapps/contentstore/tests/test_views.py @@ -6,7 +6,7 @@ import unittest from nose.tools import set_trace from nose.plugins.skip import SkipTest -from django.http import Http404, HttpResponse, HttpRequest, HttpResponseRedirect +from django.http import Http404, HttpResponse, HttpRequest, HttpResponseRedirect, HttpResponseBadRequest from django.conf import settings from django.contrib.auth.models import User from django.test.client import Client @@ -18,8 +18,8 @@ from django.core.exceptions import PermissionDenied from xmodule.modulestore.django import modulestore, _MODULESTORES import contentstore.views as views -import auth.authz as a -from contentstore.tests.factories import XModuleCourseFactory, CourseFactory +from contentstore.tests.factories import CourseFactory, ItemFactory +from xmodule.modulestore import Location def xml_store_config(data_dir): @@ -46,17 +46,21 @@ class ViewsTestCase(TestCase): def setUp(self): self.location = ['i4x', 'edX', 'toy', 'chapter', 'Overview'] self.location_2 = ['i4x', 'edX', 'full', 'course', '6.002_Spring_2012'] + self.location_3 = ['i4x', 'MITx', '999', 'course', 'Robot_Super_Course'] # empty Modulestore self._MODULESTORES = {} self.course_id = 'edX/toy/2012_Fall' self.course_id_2 = 'edx/full/6.002_Spring_2012' #self.toy_course = modulestore().get_course(self.course_id) # Problem: Classes persist, need to delete stuff from modulestore + # is a CourseDescriptor object? self.course = CourseFactory.create() - print dir(self.course) + # is a sequence descriptor + self.item = ItemFactory.create(template = 'i4x://edx/templates/sequential/Empty') def tearDown(self): - pass + _MODULESTORES = {} + modulestore().collection.drop() def test_has_access(self): user = MagicMock(is_staff = True, is_active = True, is_authenticated = True) @@ -72,15 +76,15 @@ class ViewsTestCase(TestCase): self.user = MagicMock(is_staff = False, is_active = False) self.user.is_authenticated.return_value = False request = MagicMock(user = self.user) - # Instead of raising exception when has_access is False, redirects + # Redirects if request.user doesn't have access to location self.assertIsInstance(views.course_index(request, 'edX', 'full', '6.002_Spring_2012'), HttpResponseRedirect) self.user_2 = MagicMock(is_staff = True, is_active = True) self.user_2.is_authenticated.return_value = True request_2 = MagicMock(user = self.user_2) - # Need to use XModuleStoreFactory? - print views.course_index(request_2, 'edX', - 'full', '6.002_Spring_2012') + # Doesn't work unless we figure out render_to_response +## views.course_index(request_2, 'MITx', +## '999', 'Robot_Super_Course') def test_edit_subsection(self): # Redirects if request.user doesn't have access to location @@ -89,4 +93,44 @@ class ViewsTestCase(TestCase): self.request = MagicMock(user = self.user) self.assertIsInstance(views.edit_subsection(self.request, self.location_2), HttpResponseRedirect) - + # If location isn't for a "sequential", return Bad Request + self.user_2 = MagicMock(is_staff = True, is_active = True) + self.user_2.is_authenticated.return_value = True + self.request_2 = MagicMock(user = self.user_2) + self.assertIsInstance(views.edit_subsection(self.request_2, + self.location_3), HttpResponseBadRequest) + # Need render_to_response + #views.edit_subsection(self.request_2, self.item.location) + + def test_edit_unit(self): + # if user doesn't have access, should redirect + self.user = MagicMock(is_staff = False, is_active = False) + self.user.is_authenticated.return_value = False + self.request = MagicMock(user = self.user) + self.assertIsInstance(views.edit_unit(self.request, self.location_2), + HttpResponseRedirect) + + def test_assignment_type_update(self): + # If user doesn't have access, should redirect + self.user = MagicMock(is_staff = False, is_active = False) + self.user.is_authenticated.return_value = False + self.request = RequestFactory().get('foo') + self.request.user = self.user + self.assertIsInstance(views.assignment_type_update(self.request, + 'MITx', '999', 'course', 'Robot_Super_Course'), + HttpResponseRedirect) + # if user has access, then should return HttpResponse + self.user_2 = MagicMock(is_staff = True, is_active = True) + self.user_2.is_authenticated.return_value = True + self.request.user = self.user_2 + get_response = views.assignment_type_update(self.request,'MITx', '999', + 'course', 'Robot_Super_Course') + self.assertIsInstance(get_response,HttpResponse) + get_response_string = '{"id": 99, "location": ["i4x", "MITx", "999", "course", "Robot_Super_Course", null], "graderType": "Not Graded"}' + self.assertEquals(get_response.content, get_response_string) + self.request_2 = RequestFactory().post('foo') + self.request_2.user = self.user_2 + post_response = views.assignment_type_update(self.request_2,'MITx', '999', + 'course', 'Robot_Super_Course') + self.assertIsInstance(post_response,HttpResponse) + self.assertEquals(post_response.content, 'null') From 40ddaa99766244efb3f46b8b2841aa6f1a076724 Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Wed, 30 Jan 2013 16:03:16 -0500 Subject: [PATCH 19/76] more tests on test_views.py --- .../contentstore/tests/test_views.py | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/cms/djangoapps/contentstore/tests/test_views.py b/cms/djangoapps/contentstore/tests/test_views.py index 87dd7f7ac6..2cb00eac36 100644 --- a/cms/djangoapps/contentstore/tests/test_views.py +++ b/cms/djangoapps/contentstore/tests/test_views.py @@ -5,6 +5,7 @@ import factory import unittest from nose.tools import set_trace from nose.plugins.skip import SkipTest +from collections import defaultdict from django.http import Http404, HttpResponse, HttpRequest, HttpResponseRedirect, HttpResponseBadRequest from django.conf import settings @@ -20,7 +21,11 @@ from xmodule.modulestore.django import modulestore, _MODULESTORES import contentstore.views as views from contentstore.tests.factories import CourseFactory, ItemFactory from xmodule.modulestore import Location +from xmodule.x_module import ModuleSystem +from xmodule.error_module import ErrorModule +class Stub(): + pass def xml_store_config(data_dir): return { @@ -61,6 +66,7 @@ class ViewsTestCase(TestCase): def tearDown(self): _MODULESTORES = {} modulestore().collection.drop() + assert False def test_has_access(self): user = MagicMock(is_staff = True, is_active = True, is_authenticated = True) @@ -134,3 +140,78 @@ class ViewsTestCase(TestCase): 'course', 'Robot_Super_Course') self.assertIsInstance(post_response,HttpResponse) self.assertEquals(post_response.content, 'null') + + def test_load_preview_state(self): + # Tests that function creates empty defaultdict when request.session + # is empty + # location cannot be a list or other mutable type + self.request = RequestFactory().get('foo') + self.request.session = {} + instance_state, shared_state = views.load_preview_state(self.request, + 'foo', 'bar') + self.assertIsNone(instance_state) + self.assertIsNone(shared_state) + + def test_save_preview_state(self): + self.request = RequestFactory().get('foo') + self.request.session = {} + loc = Location(self.location_3) + result = {'preview_states': + {('id', loc):{'instance':None, + 'shared':None, + } + } + } + views.save_preview_state(self.request, 'id', loc, None, None) + self.assertEquals(self.request.session, result) + + def test_get_preview_module(self): + raise SkipTest + self.request = RequestFactory().get('foo') + self.request.user = UserFactory() + mock_descriptor = MagicMock() + mock_descriptor.get_sample_state.return_value = [('foo','bar')] + instance, shared = views.get_preview_module(self.request, 'id', mock_descriptor) + self.assertEquals(instance, 'foo') + + def test_preview_module_system(self): + # Returns a ModuleSystem + self.request = RequestFactory().get('foo') + self.request.user = UserFactory() + self.assertIsInstance(views.preview_module_system(self.request, + 'id', self.course), + ModuleSystem) + + def test_load_preview_module(self): + + self.request = RequestFactory().get('foo') + self.request.user = UserFactory() + self.request.session = {} + self.assertIsInstance(views.load_preview_module(self.request, 'id', + self.course, 'instance', 'shared'), + ErrorModule) + system = views.preview_module_system(self.request, 'id', self.course) + # is a functools.partial object? + # Not sure how to get a valid line 507 + print self.course.xmodule_constructor(system) + print self.course.xmodule_constructor(system).func + print self.course.xmodule_constructor(system).keywords + + def test__xmodule_recurse(self): + raise SkipTest +## mock_item = MagicMock() +## mock_item.get_children.return_value = [] + s = Stub() + s.children.append(Stub()) + views._xmodule_recurse(s, lambda x: return) + #views._xmodule_recurse(s, lambda x: x.n += 1) + self.assertEquals(s.n, 1) + self.assertEquals(s.children[0].n, 1) + +class Stub(): + def __init__(self): + self.n = 0 + self.children = [] + def get_children(self): + return self.children + From abc3e5b09d705d728522b6ffb79da85a5489fe1a Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Thu, 31 Jan 2013 14:47:59 -0500 Subject: [PATCH 20/76] more tests in test_views.py --- .../contentstore/tests/test_views.py | 119 ++++++++++++++++-- 1 file changed, 111 insertions(+), 8 deletions(-) diff --git a/cms/djangoapps/contentstore/tests/test_views.py b/cms/djangoapps/contentstore/tests/test_views.py index 2cb00eac36..9fa16ad4b2 100644 --- a/cms/djangoapps/contentstore/tests/test_views.py +++ b/cms/djangoapps/contentstore/tests/test_views.py @@ -23,6 +23,7 @@ from contentstore.tests.factories import CourseFactory, ItemFactory from xmodule.modulestore import Location from xmodule.x_module import ModuleSystem from xmodule.error_module import ErrorModule +from contentstore.utils import get_course_for_item class Stub(): pass @@ -49,6 +50,7 @@ TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR) class ViewsTestCase(TestCase): def setUp(self): + #modulestore().collection.drop() self.location = ['i4x', 'edX', 'toy', 'chapter', 'Overview'] self.location_2 = ['i4x', 'edX', 'full', 'course', '6.002_Spring_2012'] self.location_3 = ['i4x', 'MITx', '999', 'course', 'Robot_Super_Course'] @@ -56,8 +58,6 @@ class ViewsTestCase(TestCase): self._MODULESTORES = {} self.course_id = 'edX/toy/2012_Fall' self.course_id_2 = 'edx/full/6.002_Spring_2012' - #self.toy_course = modulestore().get_course(self.course_id) - # Problem: Classes persist, need to delete stuff from modulestore # is a CourseDescriptor object? self.course = CourseFactory.create() # is a sequence descriptor @@ -66,7 +66,7 @@ class ViewsTestCase(TestCase): def tearDown(self): _MODULESTORES = {} modulestore().collection.drop() - assert False + #assert False def test_has_access(self): user = MagicMock(is_staff = True, is_active = True, is_authenticated = True) @@ -196,18 +196,121 @@ class ViewsTestCase(TestCase): print self.course.xmodule_constructor(system) print self.course.xmodule_constructor(system).func print self.course.xmodule_constructor(system).keywords + print dir(self.course.xmodule_constructor(system).func) def test__xmodule_recurse(self): - raise SkipTest -## mock_item = MagicMock() -## mock_item.get_children.return_value = [] + #There shouldn't be a difference, but the code works with defined + # function f but not with lambda functions + mock_item = MagicMock() + mock_item.get_children.return_value = [] s = Stub() s.children.append(Stub()) - views._xmodule_recurse(s, lambda x: return) - #views._xmodule_recurse(s, lambda x: x.n += 1) + views._xmodule_recurse(s, f) self.assertEquals(s.n, 1) self.assertEquals(s.children[0].n, 1) + + def test_get_module_previews(self): + # needs a working render_to_string + raise SkipTest + self.request = RequestFactory().get('foo') + self.request.user = UserFactory() + self.request.session = {} + print views.get_module_previews(self.request, self.course) + + def test_delete_item(self): + # If user doesn't have permission, redirect + self.no_permit_user = MagicMock(is_staff = False, is_active = False) + self.no_permit_user.is_authenticated.return_value = True + self.request = RequestFactory().post('i4x://MITx/999/course/Robot_Super_Course') + self.request.POST = self.request.POST.copy() + self.request.POST.update({'id':'i4x://MITx/999/course/Robot_Super_Course'}) + self.request.user = self.no_permit_user + self.assertRaises(PermissionDenied, views.delete_item, self.request) + # Should return an HttpResponse + self.permit_user =MagicMock(is_staff = True, is_active = True) + self.permit_user.is_authenticated.return_value = True + self.request_2 = RequestFactory().post(self.item.location.url()) + self.request_2.POST = self.request_2.POST.copy() + self.request_2.POST.update({'id':self.item.location.url()}) + self.request_2.user = self.permit_user + response = views.delete_item(self.request_2) + self.assertIsInstance(response, HttpResponse) + self.assertEquals(modulestore().get_items(self.item.location.url()), []) + # Set delete_children to True to delete all children + # Create children + self.item_2 = ItemFactory.create() + child_item = ItemFactory.create() +## print type(self.item_2) +## print self.item_2.__dict__ + # Is there better way of adding children? What format are children in? + self.item_2.definition['children'] = [child_item.location.url()] + self.request_3 = RequestFactory().post(self.item_2.location.url()) + self.request_3.POST = self.request_3.POST.copy() + self.request_3.POST.update({'id':self.item_2.location.url(), + 'delete_children':True, + 'delete_all_versions':True}) + self.request_3.user = self.permit_user + print self.item_2.get_children() + self.assertIsInstance(views.delete_item(self.request_3), HttpResponse) + self.assertEquals(modulestore().get_items(self.item_2.location.url()), []) + # Problem: Function doesn't delete child item? + # child_item can be manually deleted, but can't delete it using function + # Not sure if problem with _xmodule_recurse and lambda functions + #store = views.get_modulestore(child_item.location.url()) + #store.delete_item(child_item.location) + self.assertEquals(modulestore().get_items(child_item.location.url()), []) + # Check delete_item on 'vertical' + self.item_3 = ItemFactory.create(template = 'i4x://edx/templates/vertical/Empty') + self.request_4 = RequestFactory().post(self.item_3.location.url()) + self.request_4.POST = self.request_4.POST.copy() + self.request_4.POST.update({'id':self.item_3.location.url(), + 'delete_children':True, + 'delete_all_versions':True}) + self.request_4.user = self.permit_user + self.assertIsInstance(views.delete_item(self.request_4), HttpResponse) + self.assertEquals(modulestore().get_items(self.item_3.location.url()), []) + + def test_save_item(self): + # Test that user with no permissions gets redirected + self.no_permit_user = MagicMock(is_staff = False, is_active = False) + self.no_permit_user.is_authenticated.return_value = True + self.request = RequestFactory().post(self.item.location.url()) + self.request.POST = self.request.POST.copy() + self.request.POST.update({'id':self.item.location.url()}) + self.request.user = self.no_permit_user + self.assertRaises(PermissionDenied, views.save_item, self.request) + # Test user with permissions but nothing in request.POST + self.item_2 = ItemFactory.create() + self.permit_user =MagicMock(is_staff = True, is_active = True) + self.permit_user.is_authenticated.return_value = True + self.request_2 = RequestFactory().post(self.item_2.location.url()) + self.request_2.POST = self.request.POST.copy() + self.request_2.POST.update({'id':self.item_2.location.url()}) + self.request_2.user = self.permit_user + self.assertIsInstance(views.save_item(self.request_2), HttpResponse) + # Test updating data + self.request_3 = RequestFactory().post(self.item_2.location.url()) + self.request_3.POST = self.request.POST.copy() + self.request_3.POST.update({'id':self.item_2.location.url(), + 'data':{'foo':'bar'}}) + self.request_3.user = self.permit_user + self.assertIsInstance(views.save_item(self.request_3), HttpResponse) + self.assertEquals(modulestore().get_item(self.item_2.location.dict()).definition['data'], + {u'foo': u'bar'}) + # Test metadata, which is a dictionary? + self.request_4 = RequestFactory().post(self.item_2.location.url()) + self.request_4.POST = self.request.POST.copy() + self.request_4.POST.update({'id':self.item_2.location.url(), + 'metadata':{'foo':'bar'}}) + self.request_4.user = self.permit_user + self.assertIsInstance(views.save_item(self.request_4), HttpResponse) + self.assertEquals(modulestore().get_item(self.item_2.location.dict()).metadata['foo'], + 'bar') + +def f(x): + x.n += 1 + class Stub(): def __init__(self): self.n = 0 From 7908d87902d0e89faa674eb9383c6ecb00e10f6f Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Thu, 31 Jan 2013 15:58:14 -0500 Subject: [PATCH 21/76] more tests for views.py --- .../contentstore/tests/test_views.py | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/cms/djangoapps/contentstore/tests/test_views.py b/cms/djangoapps/contentstore/tests/test_views.py index 9fa16ad4b2..d5a15a10e3 100644 --- a/cms/djangoapps/contentstore/tests/test_views.py +++ b/cms/djangoapps/contentstore/tests/test_views.py @@ -6,6 +6,7 @@ import unittest from nose.tools import set_trace from nose.plugins.skip import SkipTest from collections import defaultdict +import re from django.http import Http404, HttpResponse, HttpRequest, HttpResponseRedirect, HttpResponseBadRequest from django.conf import settings @@ -24,6 +25,7 @@ from xmodule.modulestore import Location from xmodule.x_module import ModuleSystem from xmodule.error_module import ErrorModule from contentstore.utils import get_course_for_item +from xmodule.templates import update_templates class Stub(): pass @@ -50,12 +52,14 @@ TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR) class ViewsTestCase(TestCase): def setUp(self): - #modulestore().collection.drop() + self._MODULESTORES = {} + modulestore().collection.drop() + update_templates() self.location = ['i4x', 'edX', 'toy', 'chapter', 'Overview'] self.location_2 = ['i4x', 'edX', 'full', 'course', '6.002_Spring_2012'] self.location_3 = ['i4x', 'MITx', '999', 'course', 'Robot_Super_Course'] # empty Modulestore - self._MODULESTORES = {} + self.course_id = 'edX/toy/2012_Fall' self.course_id_2 = 'edx/full/6.002_Spring_2012' # is a CourseDescriptor object? @@ -210,6 +214,7 @@ class ViewsTestCase(TestCase): self.assertEquals(s.children[0].n, 1) def test_get_module_previews(self): + raise SkipTest # needs a working render_to_string raise SkipTest self.request = RequestFactory().get('foo') @@ -218,6 +223,7 @@ class ViewsTestCase(TestCase): print views.get_module_previews(self.request, self.course) def test_delete_item(self): + raise SkipTest # If user doesn't have permission, redirect self.no_permit_user = MagicMock(is_staff = False, is_active = False) self.no_permit_user.is_authenticated.return_value = True @@ -297,7 +303,7 @@ class ViewsTestCase(TestCase): self.assertIsInstance(views.save_item(self.request_3), HttpResponse) self.assertEquals(modulestore().get_item(self.item_2.location.dict()).definition['data'], {u'foo': u'bar'}) - # Test metadata, which is a dictionary? + # Test updating metadata self.request_4 = RequestFactory().post(self.item_2.location.url()) self.request_4.POST = self.request.POST.copy() self.request_4.POST.update({'id':self.item_2.location.url(), @@ -306,7 +312,25 @@ class ViewsTestCase(TestCase): self.assertIsInstance(views.save_item(self.request_4), HttpResponse) self.assertEquals(modulestore().get_item(self.item_2.location.dict()).metadata['foo'], 'bar') - + + def test_clone_item(self): + # Test that user with no permissions gets redirected + self.no_permit_user = MagicMock(is_staff = False, is_active = False) + self.no_permit_user.is_authenticated.return_value = True + self.request = RequestFactory().post(self.item.location.url()) + self.request.POST = self.request.POST.copy() + self.request.POST.update({'id':self.item.location.url(), + 'parent_location':self.course.location.url(), + 'template':self.location_3, + 'display_name':'bar'}) + self.request.user = self.no_permit_user + self.assertRaises(PermissionDenied, views.clone_item, self.request) + self.permit_user = MagicMock(is_staff = True, is_active = True) + self.permit_user.is_authenticated.return_value = True + self.request.user = self.permit_user + response = views.clone_item(self.request) + self.assertIsInstance(response, HttpResponse) + self.assertRegexpMatches(response.content, '{"id": "i4x://MITx/999/course/') def f(x): x.n += 1 From c144b2519a6cad068e88df0f4c50e0937d036b59 Mon Sep 17 00:00:00 2001 From: Deena Wang Date: Fri, 1 Feb 2013 15:57:11 -0500 Subject: [PATCH 22/76] more tests in test_views.py --- .../contentstore/tests/factories.py | 53 ++- .../contentstore/tests/test_views.py | 369 +++++++++++++++++- 2 files changed, 401 insertions(+), 21 deletions(-) diff --git a/cms/djangoapps/contentstore/tests/factories.py b/cms/djangoapps/contentstore/tests/factories.py index cb9f451d38..ab83c52438 100644 --- a/cms/djangoapps/contentstore/tests/factories.py +++ b/cms/djangoapps/contentstore/tests/factories.py @@ -1,10 +1,17 @@ from factory import Factory -from xmodule.modulestore import Location -from xmodule.modulestore.django import modulestore +from datetime import datetime +import uuid from time import gmtime from uuid import uuid4 -from xmodule.timeparse import stringify_time +from django.contrib.auth.models import Group + +from xmodule.modulestore import Location +from xmodule.modulestore.django import modulestore +from xmodule.timeparse import stringify_time +from student.models import (User, UserProfile, Registration, + CourseEnrollmentAllowed) +from django.contrib.auth.models import Group def XMODULE_COURSE_CREATION(class_to_create, **kwargs): return XModuleCourseFactory._create(class_to_create, **kwargs) @@ -114,4 +121,42 @@ class ItemFactory(XModuleItemFactory): parent_location = 'i4x://MITx/999/course/Robot_Super_Course' template = 'i4x://edx/templates/chapter/Empty' - display_name = 'Section One' \ No newline at end of file + display_name = 'Section One' + +class UserProfileFactory(Factory): + FACTORY_FOR = UserProfile + + user = None + name = 'Robot Studio' + courseware = 'course.xml' + +class RegistrationFactory(Factory): + FACTORY_FOR = Registration + + user = None + activation_key = uuid.uuid4().hex + +class UserFactory(Factory): + FACTORY_FOR = User + + username = 'robot' + email = 'robot@edx.org' + password = 'test' + first_name = 'Robot' + last_name = 'Tester' + is_staff = False + is_active = True + is_superuser = False + last_login = datetime.now() + date_joined = datetime.now() + +class GroupFactory(Factory): + FACTORY_FOR = Group + + name = 'test_group' + +class CourseEnrollmentAllowedFactory(Factory): + FACTORY_FOR = CourseEnrollmentAllowed + + email = 'test@edx.org' + course_id = 'edX/test/2012_Fall' diff --git a/cms/djangoapps/contentstore/tests/test_views.py b/cms/djangoapps/contentstore/tests/test_views.py index ae6548bd9d..f4f7fcd964 100644 --- a/cms/djangoapps/contentstore/tests/test_views.py +++ b/cms/djangoapps/contentstore/tests/test_views.py @@ -5,19 +5,32 @@ import factory import unittest from nose.tools import set_trace from nose.plugins.skip import SkipTest +from collections import defaultdict +import re -from django.http import Http404, HttpResponse, HttpRequest +from django.http import (Http404, HttpResponse, HttpRequest, + HttpResponseRedirect, HttpResponseBadRequest, + HttpResponseForbidden) from django.conf import settings from django.contrib.auth.models import User -from django.test.client import Client -from django.conf import settings +from django.test.client import Client, RequestFactory from django.test import TestCase -from django.test.client import RequestFactory +from django.core.exceptions import PermissionDenied from override_settings import override_settings from xmodule.modulestore.django import modulestore, _MODULESTORES +from xmodule.modulestore import Location +from xmodule.x_module import ModuleSystem +from xmodule.error_module import ErrorModule +from xmodule.seq_module import SequenceModule +from xmodule.templates import update_templates +from contentstore.utils import get_course_for_item +from contentstore.tests.factories import UserFactory +from contentstore.tests.factories import CourseFactory, ItemFactory import contentstore.views as views +class Stub(): + pass def xml_store_config(data_dir): return { @@ -30,25 +43,347 @@ def xml_store_config(data_dir): } } -class UserFactory(factory.Factory): - first_name = 'Test' - last_name = 'Robot' - is_staff = True - is_active = True - TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR) -@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE) class ViewsTestCase(TestCase): def setUp(self): - self.location = ['i4x', 'edX', 'toy', 'chapter', 'Overview'] + # empty Modulestore self._MODULESTORES = {} + modulestore().collection.drop() + update_templates() + self.location = ['i4x', 'edX', 'toy', 'chapter', 'Overview'] + self.location_2 = ['i4x', 'edX', 'full', 'course', '6.002_Spring_2012'] + self.location_3 = ['i4x', 'MITx', '999', 'course', 'Robot_Super_Course'] self.course_id = 'edX/toy/2012_Fall' - self.toy_course = modulestore().get_course(self.course_id) + self.course_id_2 = 'edx/full/6.002_Spring_2012' + # is a CourseDescriptor object? + self.course = CourseFactory.create() + # is a sequence descriptor + self.item = ItemFactory.create(template = 'i4x://edx/templates/sequential/Empty') + self.no_permit_user = UserFactory() + self.permit_user = UserFactory(is_staff = True, username = 'Wizardly Herbert') + def tearDown(self): + _MODULESTORES = {} + modulestore().collection.drop() + assert False + def test_has_access(self): - user = UserFactory() - user.is_authenticated = True - set_trace() - self.assertTrue(views.has_access(user, self.location)) + self.assertTrue(views.has_access(self.permit_user, self.location_2)) + self.assertFalse(views.has_access(self.no_permit_user, self.location_2)) + # done + + def test_course_index(self): + request = RequestFactory().get('foo') + request.user = self.no_permit_user + # Redirects if request.user doesn't have access to location + self.assertRaises(PermissionDenied, views.course_index, request, 'edX', + 'full', '6.002_Spring_2012') + request_2 = RequestFactory().get('foo') + request.user = self.permit_user + # Doesn't work unless we figure out render_to_response +## views.course_index(request_2, 'MITx', +## '999', 'Robot_Super_Course') + + def test_edit_subsection(self): + # Redirects if request.user doesn't have access to location + self.request = RequestFactory().get('foo') + self.request.user = self.no_permit_user + self.assertRaises(PermissionDenied, views.edit_subsection, self.request, + self.location_2) + # If location isn't for a "sequential", return Bad Request + self.request_2 = RequestFactory().get('foo') + self.request_2.user = self.permit_user + self.assertIsInstance(views.edit_subsection(self.request_2, + self.location_3), HttpResponseBadRequest) + # Need render_to_response + #views.edit_subsection(self.request_2, self.item.location) + + def test_edit_unit(self): + raise SkipTest + # if user doesn't have access, should redirect + self.request = RequestFactory().get('foo') + self.request.user = self.no_permit_user + self.assertRaises(PermissionDenied, views.edit_unit, self.request, + self.location_2) + self.request_2 = RequestFactory().get('foo') + self.request_2.user = self.permit_user + # Problem: no parent locations, so IndexError + #print modulestore().get_parent_locations(self.location_3, None) + views.edit_unit(self.request_2, self.location_3) + # Needs render_to_response + + def test_assignment_type_update(self): + raise SkipTest + # If user doesn't have access, should return HttpResponseForbidden() + self.request = RequestFactory().get('foo') + self.request.user = self.no_permit_user + self.assertIsInstance(views.assignment_type_update(self.request, + 'MITx', '999', 'course', 'Robot_Super_Course'), + HttpResponseForbidden) +## views.assignment_type_update(self.request, 'MITx', '999', 'course', 'Robot_Super_Course') + # if user has access, then should return HttpResponse + self.request.user = self.permit_user + get_response = views.assignment_type_update(self.request,'MITx', '999', + 'course', 'Robot_Super_Course') + self.assertIsInstance(get_response,HttpResponse) + get_response_string = '{"id": 99, "location": ["i4x", "MITx", "999", "course", "Robot_Super_Course", null], "graderType": "Not Graded"}' + self.assertEquals(get_response.content, get_response_string) + self.request_2 = RequestFactory().post('foo') + self.request_2.user = self.permit_user + post_response = views.assignment_type_update(self.request_2,'MITx', '999', + 'course', 'Robot_Super_Course') + self.assertIsInstance(post_response,HttpResponse) + self.assertEquals(post_response.content, 'null') + + def test_load_preview_state(self): + # Tests that function creates empty defaultdict when request.session + # is empty + # location cannot be a list or other mutable type + self.request = RequestFactory().get('foo') + self.request.session = {} + instance_state, shared_state = views.load_preview_state(self.request, + 'foo', 'bar') + self.assertIsNone(instance_state) + self.assertIsNone(shared_state) + # Done + + def test_save_preview_state(self): + self.request = RequestFactory().get('foo') + self.request.session = {} + loc = Location(self.location_3) + result = {'preview_states': + {('id', loc):{'instance':None, + 'shared':None, + } + } + } + views.save_preview_state(self.request, 'id', loc, None, None) + self.assertEquals(self.request.session, result) + # Done + + def test_get_preview_module(self): + self.request = RequestFactory().get('foo') + self.request.user = self.permit_user + self.request.session = {} + module = views.get_preview_module(self.request, 'id', self.course) + self.assertIsInstance(module, SequenceModule) + # Done + + def test_preview_module_system(self): + # Returns a ModuleSystem + self.request = RequestFactory().get('foo') + self.request.user = self.no_permit_user + self.assertIsInstance(views.preview_module_system(self.request, + 'id', self.course), + ModuleSystem) + # done + + def test_load_preview_module(self): + # if error in getting module, return ErrorModule + self.request = RequestFactory().get('foo') + self.request.user = self.no_permit_user + self.request.session = {} + self.assertIsInstance(views.load_preview_module(self.request, 'id', + self.course, 'instance', 'shared'), + ErrorModule) + instance_state, shared_state = self.course.get_sample_state()[0] + module = views.load_preview_module(self.request,'id', self.course, + instance_state, shared_state) + self.assertIsInstance(module, SequenceModule) + # I'd like to test module.get_html, but it relies on render_to_string + # Test static_tab + self.course_2 = CourseFactory(display_name = 'Intro_to_intros', location = Location('i4x', 'MITx', '666', 'static_tab', 'Intro_to_intros')) + module_2 = views.load_preview_module(self.request,'id', self.course_2, + instance_state, shared_state) + self.assertIsInstance(module, SequenceModule) + # needs render_to_string + + def test__xmodule_recurse(self): + #There shouldn't be a difference, but the code works with defined + # function f but not with lambda functions + mock_item = MagicMock() + mock_item.get_children.return_value = [] + s = Stub() + s.children.append(Stub()) + views._xmodule_recurse(s, f) + self.assertEquals(s.n, 1) + self.assertEquals(s.children[0].n, 1) + + def test_get_module_previews(self): + raise SkipTest + # needs a working render_to_string + self.request = RequestFactory().get('foo') + self.request.user = UserFactory() + self.request.session = {} + print views.get_module_previews(self.request, self.course) + + def test_delete_item(self): + raise SkipTest + # If user doesn't have permission, redirect + self.request = RequestFactory().post('i4x://MITx/999/course/Robot_Super_Course') + self.request.POST = self.request.POST.copy() + self.request.POST.update({'id':'i4x://MITx/999/course/Robot_Super_Course'}) + self.request.user = self.no_permit_user + self.assertRaises(PermissionDenied, views.delete_item, self.request) + # Should return an HttpResponse + self.request_2 = RequestFactory().post(self.item.location.url()) + self.request_2.POST = self.request_2.POST.copy() + self.request_2.POST.update({'id':self.item.location.url()}) + self.request_2.user = self.permit_user + response = views.delete_item(self.request_2) + self.assertIsInstance(response, HttpResponse) + self.assertEquals(modulestore().get_items(self.item.location.url()), []) + # Set delete_children to True to delete all children + # Create children + self.item_2 = ItemFactory.create() + child_item = ItemFactory.create() +## print type(self.item_2) +## print self.item_2.__dict__ + # Is there better way of adding children? What format are children in? + self.item_2.definition['children'] = [child_item.location.url()] + self.request_3 = RequestFactory().post(self.item_2.location.url()) + self.request_3.POST = self.request_3.POST.copy() + self.request_3.POST.update({'id':self.item_2.location.url(), + 'delete_children':True, + 'delete_all_versions':True}) + self.request_3.user = self.permit_user + print self.item_2.get_children() + self.assertIsInstance(views.delete_item(self.request_3), HttpResponse) + self.assertEquals(modulestore().get_items(self.item_2.location.url()), []) + # Problem: Function doesn't delete child item? + # child_item can be manually deleted, but can't delete it using function + # Not sure if problem with _xmodule_recurse and lambda functions + #store = views.get_modulestore(child_item.location.url()) + #store.delete_item(child_item.location) + self.assertEquals(modulestore().get_items(child_item.location.url()), []) + + # Check delete_item on 'vertical' + self.item_3 = ItemFactory.create(template = 'i4x://edx/templates/vertical/Empty') + self.request_4 = RequestFactory().post(self.item_3.location.url()) + self.request_4.POST = self.request_4.POST.copy() + self.request_4.POST.update({'id':self.item_3.location.url(), + 'delete_children':True, + 'delete_all_versions':True}) + self.request_4.user = self.permit_user + self.assertIsInstance(views.delete_item(self.request_4), HttpResponse) + self.assertEquals(modulestore().get_items(self.item_3.location.url()), []) + + def test_save_item(self): + # Test that user with no permissions gets redirected + self.request = RequestFactory().post(self.item.location.url()) + self.request.POST = self.request.POST.copy() + self.request.POST.update({'id':self.item.location.url()}) + self.request.user = self.no_permit_user + self.assertRaises(PermissionDenied, views.save_item, self.request) + # Test user with permissions but nothing in request.POST + self.item_2 = ItemFactory.create() + self.request_2 = RequestFactory().post(self.item_2.location.url()) + self.request_2.POST = self.request.POST.copy() + self.request_2.POST.update({'id':self.item_2.location.url()}) + self.request_2.user = self.permit_user + self.assertIsInstance(views.save_item(self.request_2), HttpResponse) + # Test updating data + self.request_3 = RequestFactory().post(self.item_2.location.url()) + self.request_3.POST = self.request.POST.copy() + self.request_3.POST.update({'id':self.item_2.location.url(), + 'data':{'foo':'bar'}}) + self.request_3.user = self.permit_user + self.assertIsInstance(views.save_item(self.request_3), HttpResponse) + self.assertEquals(modulestore().get_item(self.item_2.location.dict()).definition['data'], + {u'foo': u'bar'}) + # Test updating metadata + self.request_4 = RequestFactory().post(self.item_2.location.url()) + self.request_4.POST = self.request.POST.copy() + self.request_4.POST.update({'id':self.item_2.location.url(), + 'metadata':{'foo':'bar'}}) + self.request_4.user = self.permit_user + self.assertIsInstance(views.save_item(self.request_4), HttpResponse) + self.assertEquals(modulestore().get_item(self.item_2.location.dict()).metadata['foo'], + 'bar') + #done + + def test_clone_item(self): + # Test that user with no permissions gets redirected + self.request = RequestFactory().post(self.item.location.url()) + self.request.POST = self.request.POST.copy() + self.request.POST.update({'id':self.item.location.url(), + 'parent_location':self.course.location.url(), + 'template':self.location_3, + 'display_name':'bar'}) + self.request.user = self.no_permit_user + self.assertRaises(PermissionDenied, views.clone_item, self.request) + self.request.user = self.permit_user + response = views.clone_item(self.request) + self.assertIsInstance(response, HttpResponse) + self.assertRegexpMatches(response.content, '{"id": "i4x://MITx/999/course/') + # Done + + def test_upload_asset(self): + # Test get request + self.request = RequestFactory().get('foo') + self.assertIsInstance(views.upload_asset(self.request,'org', 'course', + 'coursename'), HttpResponseBadRequest) + # Test no permissions + self.request_2 = RequestFactory().post('foo') + self.request_2.user = self.no_permit_user + self.assertIsInstance(views.upload_asset(self.request_2, 'MITx', '999', + 'Robot_Super_Course'), HttpResponseForbidden) + # Test if course exists + + self.request_3 = RequestFactory().post('foo') + self.request_3.user = self.permit_user + # Throws error because of improperly formatted log +## self.assertIsInstance(views.upload_asset(self.request_3,'org', 'course', +## 'coursename'),HttpResponseBadRequest) + # Test response with fake file attached + # Not sure how to create fake file for testing purposes because + # can't override request.FILES +## print self.request_3.FILES +## print type(self.request_3.FILES) +## f = open('file.txt') +## self.request_4 = RequestFactory().post('foo', f) +## print self.request_3.FILES +## mock_file = MagicMock(name = 'Secrets', content_type = 'foo') +## mock_file.read.return_value = 'stuff' +## file_dict = {'file':mock_file} +## self.request_3.FILES = file_dict +## print views.upload_asset(self.request_3, 'MITx', '999', +## 'Robot_Super_Course') + + def test_manage_users(self): + self.request = RequestFactory().get('foo') + self.request.user = self.no_permit_user + self.assertRaises(PermissionDenied, views.manage_users, self.request, + self.location_3) + # Needs render_to_response + + def test_create_json_response(self): + ok_response = views.create_json_response() + self.assertIsInstance(ok_response, HttpResponse) + self.assertEquals(ok_response.content, '{"Status": "OK"}') + bad_response = views.create_json_response('Spacetime collapsing') + self.assertIsInstance(bad_response, HttpResponse) + self.assertEquals(bad_response.content, '{"Status": "Failed", "ErrMsg": "Spacetime collapsing"}') + + def test_reorder_static_tabs(self): + self.request = RequestFactory().get('foo') + self.request.POST = {'tabs':[self.location_3]} + self.request.user = self.no_permit_user + self.assertRaises(PermissionDenied, views.reorder_static_tabs, self.request) + self.request.user = self.permit_user + self.assertIsInstance(views.reorder_static_tabs(self.request), + HttpResponseBadRequest) + # to be continued ... + +def f(x): + x.n += 1 + +class Stub(): + def __init__(self): + self.n = 0 + self.children = [] + def get_children(self): + return self.children + From 6b8edde601a5b89753a11879d37c82371cfe87fb Mon Sep 17 00:00:00 2001 From: John Hess Date: Mon, 4 Feb 2013 19:13:01 -0500 Subject: [PATCH 23/76] removed un-enroll all button for fear of disaster --- lms/djangoapps/instructor/views.py | 5 ----- lms/templates/courseware/instructor_dashboard.html | 1 - 2 files changed, 6 deletions(-) diff --git a/lms/djangoapps/instructor/views.py b/lms/djangoapps/instructor/views.py index ddb31bf871..8a61d78b3b 100644 --- a/lms/djangoapps/instructor/views.py +++ b/lms/djangoapps/instructor/views.py @@ -494,11 +494,6 @@ def instructor_dashboard(request, course_id): msg += "Error! Failed to un-enroll student with email '%s'\n" % student msg += str(err) + '\n' - elif action == 'Un-enroll ALL students': - - ret = _do_enroll_students(course, course_id, '', overload=True) - datatable = ret['datatable'] - elif action == 'Enroll multiple students': students = request.POST.get('enroll_multiple','') diff --git a/lms/templates/courseware/instructor_dashboard.html b/lms/templates/courseware/instructor_dashboard.html index 4d46505705..0eb10c9e02 100644 --- a/lms/templates/courseware/instructor_dashboard.html +++ b/lms/templates/courseware/instructor_dashboard.html @@ -237,7 +237,6 @@ function goto( mode)

Student Email: -


%if settings.MITX_FEATURES.get('REMOTE_GRADEBOOK_URL','') and instructor_access: From b3d0abbd0555f85b0ff7e4717c19a1fcd1d0eaa3 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Fri, 1 Mar 2013 13:31:54 -0500 Subject: [PATCH 24/76] Start to move around templates --- .../combined_open_ended_modulev1.py | 7 +++++-- .../{ => combinedopenended}/combined_open_ended.html | 0 2 files changed, 5 insertions(+), 2 deletions(-) rename lms/templates/{ => combinedopenended}/combined_open_ended.html (100%) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py index 171441c562..4fc1d1fec5 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py @@ -79,6 +79,9 @@ class CombinedOpenEndedV1Module(): INTERMEDIATE_DONE = 'intermediate_done' DONE = 'done' + #Where the templates live for this problem + TEMPLATE_DIR = "combinedopenended" + def __init__(self, system, location, definition, descriptor, instance_state=None, shared_state=None, metadata = None, static_data = None, **kwargs): @@ -343,7 +346,7 @@ class CombinedOpenEndedV1Module(): Output: rendered html """ context = self.get_context() - html = self.system.render_template('combined_open_ended.html', context) + html = self.system.render_template('{0}/combined_open_ended.html'.format(self.TEMPLATE_DIR), context) return html def get_html_nonsystem(self): @@ -354,7 +357,7 @@ class CombinedOpenEndedV1Module(): Output: HTML rendered directly via Mako """ context = self.get_context() - html = self.system.render_template('combined_open_ended.html', context) + html = self.system.render_template('{0}/combined_open_ended.html'.format(self.TEMPLATE_DIR), context) return html def get_html_base(self): diff --git a/lms/templates/combined_open_ended.html b/lms/templates/combinedopenended/combined_open_ended.html similarity index 100% rename from lms/templates/combined_open_ended.html rename to lms/templates/combinedopenended/combined_open_ended.html From b0c2559de8fb8a67da009e9b73432d7e8888f6a0 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Fri, 1 Mar 2013 13:43:01 -0500 Subject: [PATCH 25/76] Switch around template locations --- .../combined_open_ended_modulev1.py | 10 +++++----- .../combined_open_ended_rubric.py | 8 +++++--- .../open_ended_grading_classes/open_ended_module.py | 10 ++++++---- .../combined_open_ended_legend.html | 0 .../combined_open_ended_results.html | 0 .../combined_open_ended_status.html | 0 .../open_ended_result_table.html | 0 .../{ => combinedopenended/openended}/open_ended.html | 0 .../openended}/open_ended_combined_rubric.html | 0 .../openended}/open_ended_error.html | 0 .../openended}/open_ended_evaluation.html | 0 .../openended}/open_ended_feedback.html | 0 .../openended}/open_ended_rubric.html | 0 .../openended}/open_ended_view_only_rubric.html | 0 14 files changed, 16 insertions(+), 12 deletions(-) rename lms/templates/{ => combinedopenended}/combined_open_ended_legend.html (100%) rename lms/templates/{ => combinedopenended}/combined_open_ended_results.html (100%) rename lms/templates/{ => combinedopenended}/combined_open_ended_status.html (100%) rename lms/templates/{ => combinedopenended}/open_ended_result_table.html (100%) rename lms/templates/{ => combinedopenended/openended}/open_ended.html (100%) rename lms/templates/{ => combinedopenended/openended}/open_ended_combined_rubric.html (100%) rename lms/templates/{ => combinedopenended/openended}/open_ended_error.html (100%) rename lms/templates/{ => combinedopenended/openended}/open_ended_evaluation.html (100%) rename lms/templates/{ => combinedopenended/openended}/open_ended_feedback.html (100%) rename lms/templates/{ => combinedopenended/openended}/open_ended_rubric.html (100%) rename lms/templates/{ => combinedopenended/openended}/open_ended_view_only_rubric.html (100%) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py index 4fc1d1fec5..5c3bfa5b2a 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py @@ -534,7 +534,7 @@ class CombinedOpenEndedV1Module(): 'task_name' : 'Scored Rubric', 'class_name' : 'combined-rubric-container' } - html = self.system.render_template('combined_open_ended_results.html', context) + html = self.system.render_template('{0}/combined_open_ended_results.html'.format(self.TEMPLATE_DIR), context) return {'html': html, 'success': True} def get_legend(self, get): @@ -546,7 +546,7 @@ class CombinedOpenEndedV1Module(): context = { 'legend_list' : LEGEND_LIST, } - html = self.system.render_template('combined_open_ended_legend.html', context) + html = self.system.render_template('{0}/combined_open_ended_legend.html'.format(self.TEMPLATE_DIR), context) return {'html': html, 'success': True} def get_results(self, get): @@ -577,7 +577,7 @@ class CombinedOpenEndedV1Module(): 'submission_id' : ri['submission_ids'][i], } context_list.append(context) - feedback_table = self.system.render_template('open_ended_result_table.html', { + feedback_table = self.system.render_template('{0}/open_ended_result_table.html'.format(self.TEMPLATE_DIR), { 'context_list' : context_list, 'grader_type_image_dict' : GRADER_TYPE_IMAGE_DICT, 'human_grader_types' : HUMAN_GRADER_TYPE, @@ -589,7 +589,7 @@ class CombinedOpenEndedV1Module(): 'task_name' : "Feedback", 'class_name' : "result-container", } - html = self.system.render_template('combined_open_ended_results.html', context) + html = self.system.render_template('{0}/combined_open_ended_results.html'.format(self.TEMPLATE_DIR), context) return {'html': html, 'success': True} def get_status_ajax(self, get): @@ -703,7 +703,7 @@ class CombinedOpenEndedV1Module(): 'legend_list' : LEGEND_LIST, 'render_via_ajax' : render_via_ajax, } - status_html = self.system.render_template("combined_open_ended_status.html", context) + status_html = self.system.render_template("{0}/combined_open_ended_status.html".format(self.TEMPLATE_DIR), context) return status_html diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py index f756b2b853..8d1bd376fb 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py @@ -30,6 +30,8 @@ class RubricParsingError(Exception): class CombinedOpenEndedRubric(object): + TEMPLATE_DIR = "combinedopenended/openended" + def __init__ (self, system, view_only = False): self.has_score = False self.view_only = view_only @@ -57,9 +59,9 @@ class CombinedOpenEndedRubric(object): rubric_scores = [cat['score'] for cat in rubric_categories] max_scores = map((lambda cat: cat['options'][-1]['points']), rubric_categories) max_score = max(max_scores) - rubric_template = 'open_ended_rubric.html' + rubric_template = '{0}/open_ended_rubric.html'.format(self.TEMPLATE_DIR) if self.view_only: - rubric_template = 'open_ended_view_only_rubric.html' + rubric_template = '{0}/open_ended_view_only_rubric.html'.format(self.TEMPLATE_DIR) html = self.system.render_template(rubric_template, {'categories': rubric_categories, 'has_score': self.has_score, @@ -207,7 +209,7 @@ class CombinedOpenEndedRubric(object): for grader_type in tuple[3]: rubric_categories[i]['options'][j]['grader_types'].append(grader_type) - html = self.system.render_template('open_ended_combined_rubric.html', + html = self.system.render_template('{0}/open_ended_combined_rubric.html'.format(self.TEMPLATE_DIR), {'categories': rubric_categories, 'has_score': True, 'view_only': True, diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py index 974d23965f..aa805a5290 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py @@ -40,6 +40,8 @@ class OpenEndedModule(openendedchild.OpenEndedChild): """ + TEMPLATE_DIR = "combinedopenended/openended" + def setup_response(self, system, location, definition, descriptor): """ Sets up the response type. @@ -397,10 +399,10 @@ class OpenEndedModule(openendedchild.OpenEndedChild): rubric_scores = rubric_dict['rubric_scores'] if not response_items['success']: - return system.render_template("open_ended_error.html", + return system.render_template("{0}/open_ended_error.html".format(self.TEMPLATE_DIR), {'errors': feedback}) - feedback_template = system.render_template("open_ended_feedback.html", { + feedback_template = system.render_template("{0}/open_ended_feedback.html".format(self.TEMPLATE_DIR), { 'grader_type': response_items['grader_type'], 'score': "{0} / {1}".format(response_items['score'], self.max_score()), 'feedback': feedback, @@ -558,7 +560,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): @return: Rendered html """ context = {'msg': feedback, 'id': "1", 'rows': 50, 'cols': 50} - html = system.render_template('open_ended_evaluation.html', context) + html = system.render_template('{0}/open_ended_evaluation.html'.format(self.TEMPLATE_DIR), context) return html def handle_ajax(self, dispatch, get, system): @@ -692,7 +694,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): 'accept_file_upload': self.accept_file_upload, 'eta_message' : eta_string, } - html = system.render_template('open_ended.html', context) + html = system.render_template('{0}/open_ended.html'.format(self.TEMPLATE_DIR), context) return html diff --git a/lms/templates/combined_open_ended_legend.html b/lms/templates/combinedopenended/combined_open_ended_legend.html similarity index 100% rename from lms/templates/combined_open_ended_legend.html rename to lms/templates/combinedopenended/combined_open_ended_legend.html diff --git a/lms/templates/combined_open_ended_results.html b/lms/templates/combinedopenended/combined_open_ended_results.html similarity index 100% rename from lms/templates/combined_open_ended_results.html rename to lms/templates/combinedopenended/combined_open_ended_results.html diff --git a/lms/templates/combined_open_ended_status.html b/lms/templates/combinedopenended/combined_open_ended_status.html similarity index 100% rename from lms/templates/combined_open_ended_status.html rename to lms/templates/combinedopenended/combined_open_ended_status.html diff --git a/lms/templates/open_ended_result_table.html b/lms/templates/combinedopenended/open_ended_result_table.html similarity index 100% rename from lms/templates/open_ended_result_table.html rename to lms/templates/combinedopenended/open_ended_result_table.html diff --git a/lms/templates/open_ended.html b/lms/templates/combinedopenended/openended/open_ended.html similarity index 100% rename from lms/templates/open_ended.html rename to lms/templates/combinedopenended/openended/open_ended.html diff --git a/lms/templates/open_ended_combined_rubric.html b/lms/templates/combinedopenended/openended/open_ended_combined_rubric.html similarity index 100% rename from lms/templates/open_ended_combined_rubric.html rename to lms/templates/combinedopenended/openended/open_ended_combined_rubric.html diff --git a/lms/templates/open_ended_error.html b/lms/templates/combinedopenended/openended/open_ended_error.html similarity index 100% rename from lms/templates/open_ended_error.html rename to lms/templates/combinedopenended/openended/open_ended_error.html diff --git a/lms/templates/open_ended_evaluation.html b/lms/templates/combinedopenended/openended/open_ended_evaluation.html similarity index 100% rename from lms/templates/open_ended_evaluation.html rename to lms/templates/combinedopenended/openended/open_ended_evaluation.html diff --git a/lms/templates/open_ended_feedback.html b/lms/templates/combinedopenended/openended/open_ended_feedback.html similarity index 100% rename from lms/templates/open_ended_feedback.html rename to lms/templates/combinedopenended/openended/open_ended_feedback.html diff --git a/lms/templates/open_ended_rubric.html b/lms/templates/combinedopenended/openended/open_ended_rubric.html similarity index 100% rename from lms/templates/open_ended_rubric.html rename to lms/templates/combinedopenended/openended/open_ended_rubric.html diff --git a/lms/templates/open_ended_view_only_rubric.html b/lms/templates/combinedopenended/openended/open_ended_view_only_rubric.html similarity index 100% rename from lms/templates/open_ended_view_only_rubric.html rename to lms/templates/combinedopenended/openended/open_ended_view_only_rubric.html From c24ba23e548666e063a563eadb70cc67d27c7443 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Fri, 1 Mar 2013 13:45:55 -0500 Subject: [PATCH 26/76] Move self assessment templates --- .../open_ended_grading_classes/self_assessment_module.py | 8 +++++--- .../selfassessment}/self_assessment_hint.html | 0 .../selfassessment}/self_assessment_prompt.html | 0 .../selfassessment}/self_assessment_rubric.html | 0 4 files changed, 5 insertions(+), 3 deletions(-) rename lms/templates/{ => combinedopenended/selfassessment}/self_assessment_hint.html (100%) rename lms/templates/{ => combinedopenended/selfassessment}/self_assessment_prompt.html (100%) rename lms/templates/{ => combinedopenended/selfassessment}/self_assessment_rubric.html (100%) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py index 7ecb3c4d5e..f4be426667 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py @@ -32,6 +32,8 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): """ + TEMPLATE_DIR = "combinedopenended/selfassessment" + def setup_response(self, system, location, definition, descriptor): """ Sets up the module @@ -68,7 +70,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): 'accept_file_upload': self.accept_file_upload, } - html = system.render_template('self_assessment_prompt.html', context) + html = system.render_template('{0}/self_assessment_prompt.html'.format(self.TEMPLATE_DIR), context) return html @@ -129,7 +131,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): #This is a dev_facing_error raise ValueError("Self assessment module is in an illegal state '{0}'".format(self.state)) - return system.render_template('self_assessment_rubric.html', context) + return system.render_template('{0}/self_assessment_rubric.html'.format(self.TEMPLATE_DIR), context) def get_hint_html(self, system): """ @@ -155,7 +157,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): #This is a dev_facing_error raise ValueError("Self Assessment module is in an illegal state '{0}'".format(self.state)) - return system.render_template('self_assessment_hint.html', context) + return system.render_template('{0}/self_assessment_hint.html'.format(self.TEMPLATE_DIR), context) def save_answer(self, get, system): diff --git a/lms/templates/self_assessment_hint.html b/lms/templates/combinedopenended/selfassessment/self_assessment_hint.html similarity index 100% rename from lms/templates/self_assessment_hint.html rename to lms/templates/combinedopenended/selfassessment/self_assessment_hint.html diff --git a/lms/templates/self_assessment_prompt.html b/lms/templates/combinedopenended/selfassessment/self_assessment_prompt.html similarity index 100% rename from lms/templates/self_assessment_prompt.html rename to lms/templates/combinedopenended/selfassessment/self_assessment_prompt.html diff --git a/lms/templates/self_assessment_rubric.html b/lms/templates/combinedopenended/selfassessment/self_assessment_rubric.html similarity index 100% rename from lms/templates/self_assessment_rubric.html rename to lms/templates/combinedopenended/selfassessment/self_assessment_rubric.html From a81819129719c311c0547810d91eb3c3ff3ce736 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Fri, 1 Mar 2013 14:12:04 -0500 Subject: [PATCH 27/76] Prelim support for combinedopenended and peergrading types --- cms/djangoapps/contentstore/views.py | 3 +- .../templates/combinedopenended/default.yaml | 54 +++++++++++++++++++ .../templates/peer_grading/default.yaml | 12 +++++ 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 common/lib/xmodule/xmodule/templates/combinedopenended/default.yaml create mode 100644 common/lib/xmodule/xmodule/templates/peer_grading/default.yaml diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py index b55dc13e58..700f7b6ad4 100644 --- a/cms/djangoapps/contentstore/views.py +++ b/cms/djangoapps/contentstore/views.py @@ -66,7 +66,7 @@ from cms.djangoapps.models.settings.course_metadata import CourseMetadata log = logging.getLogger(__name__) -COMPONENT_TYPES = ['customtag', 'discussion', 'html', 'problem', 'video'] +COMPONENT_TYPES = ['customtag', 'discussion', 'html', 'problem', 'video', 'peergrading', 'combinedopenended'] # cdodge: these are categories which should not be parented, they are detached from the hierarchy DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info'] @@ -282,6 +282,7 @@ def edit_unit(request, location): component_templates = defaultdict(list) templates = modulestore().get_items(Location('i4x', 'edx', 'templates')) + log.debug(templates) for template in templates: if template.location.category in COMPONENT_TYPES: component_templates[template.location.category].append(( diff --git a/common/lib/xmodule/xmodule/templates/combinedopenended/default.yaml b/common/lib/xmodule/xmodule/templates/combinedopenended/default.yaml new file mode 100644 index 0000000000..48daa6f139 --- /dev/null +++ b/common/lib/xmodule/xmodule/templates/combinedopenended/default.yaml @@ -0,0 +1,54 @@ +--- +metadata: + display_name: Open Ended Response + rerandomize: never + showanswer: never + weight: "" + attempts: 10 + max_score: 2 + is_graded: False + version: 1 + display_name: Test Question +data: | + + + + + Category 1 + + + + + Hydrophobia + + + + + + +

Why is the sky blue?

+
+ + + + + + + Enter essay here. + This is the answer. + {"grader_settings" : "peer_grading.conf", "problem_id" : "700x/Demo"} + + + +
+ + +children: [] diff --git a/common/lib/xmodule/xmodule/templates/peer_grading/default.yaml b/common/lib/xmodule/xmodule/templates/peer_grading/default.yaml new file mode 100644 index 0000000000..3ffb5cff2f --- /dev/null +++ b/common/lib/xmodule/xmodule/templates/peer_grading/default.yaml @@ -0,0 +1,12 @@ +--- +metadata: + display_name: Peer Grading Interface + rerandomize: never + showanswer: never + weight: "" + attempts: "" +data: | + + + +children: [] From 9f020e228b9c9d39cb7c25db46a726b854b10185 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Fri, 1 Mar 2013 14:50:40 -0500 Subject: [PATCH 28/76] Add in some dummy values --- cms/djangoapps/contentstore/views.py | 15 +++++++++++++-- cms/static/sass/_graphics.scss | 8 ++++++++ common/lib/xmodule/xmodule/peer_grading_module.py | 4 ++++ .../templates/combinedopenended/default.yaml | 13 ++----------- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py index 700f7b6ad4..d300562358 100644 --- a/cms/djangoapps/contentstore/views.py +++ b/cms/djangoapps/contentstore/views.py @@ -66,7 +66,11 @@ from cms.djangoapps.models.settings.course_metadata import CourseMetadata log = logging.getLogger(__name__) -COMPONENT_TYPES = ['customtag', 'discussion', 'html', 'problem', 'video', 'peergrading', 'combinedopenended'] +COMPONENT_TYPES = ['customtag', 'discussion', 'html', 'problem', 'video', 'combinedopenended', 'peergrading'] + +ADVANCED_COMPONENT_TYPES = { + 'openended' : ['combinedopenended', 'peergrading'] +} # cdodge: these are categories which should not be parented, they are detached from the hierarchy DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info'] @@ -285,7 +289,14 @@ def edit_unit(request, location): log.debug(templates) for template in templates: if template.location.category in COMPONENT_TYPES: - component_templates[template.location.category].append(( + #This is a hack to create categories for different xmodules + category = template.location.category + for key in ADVANCED_COMPONENT_TYPES: + if template.location.category in ADVANCED_COMPONENT_TYPES[key]: + category = key + break + + component_templates[category].append(( template.display_name, template.location.url(), 'markdown' in template.metadata, diff --git a/cms/static/sass/_graphics.scss b/cms/static/sass/_graphics.scss index 4ed22c570d..619c66a919 100644 --- a/cms/static/sass/_graphics.scss +++ b/cms/static/sass/_graphics.scss @@ -254,6 +254,14 @@ background: url(../img/html-icon.png) center no-repeat; } +.large-openended-icon { + display: inline-block; + width: 100px; + height: 60px; + margin-right: 5px; + background: url(../img/large-problem-icon.png) center no-repeat; +} + .large-textbook-icon { display: inline-block; width: 100px; diff --git a/common/lib/xmodule/xmodule/peer_grading_module.py b/common/lib/xmodule/xmodule/peer_grading_module.py index 16c118a434..067834a7a0 100644 --- a/common/lib/xmodule/xmodule/peer_grading_module.py +++ b/common/lib/xmodule/xmodule/peer_grading_module.py @@ -456,6 +456,7 @@ class PeerGradingModule(XModule): try: problem_list_json = self.peer_gs.get_problem_list(self.system.course_id, self.system.anonymous_student_id) problem_list_dict = problem_list_json + log.debug(problem_list_dict) success = problem_list_dict['success'] if 'error' in problem_list_dict: error_text = problem_list_dict['error'] @@ -471,6 +472,9 @@ class PeerGradingModule(XModule): #This is a student_facing_error error_text = "Could not get list of problems to peer grade. Please notify course staff." success = False + except: + log.exception("Could not contact peer grading service.") + success = False def _find_corresponding_module_for_location(location): diff --git a/common/lib/xmodule/xmodule/templates/combinedopenended/default.yaml b/common/lib/xmodule/xmodule/templates/combinedopenended/default.yaml index 48daa6f139..d60e576a21 100644 --- a/common/lib/xmodule/xmodule/templates/combinedopenended/default.yaml +++ b/common/lib/xmodule/xmodule/templates/combinedopenended/default.yaml @@ -5,12 +5,12 @@ metadata: showanswer: never weight: "" attempts: 10 - max_score: 2 + max_score: 1 is_graded: False version: 1 display_name: Test Question data: | - + @@ -22,15 +22,6 @@ data: | The response is correct for category 1. - - Hydrophobia - - - From 67b73242ea1d2789d284159ef39edf1c0f2e2c37 Mon Sep 17 00:00:00 2001 From: Arthur Barrett Date: Fri, 1 Mar 2013 15:11:42 -0500 Subject: [PATCH 29/76] added advanced component button with ability to specify components in course policy. --- cms/djangoapps/contentstore/views.py | 33 +++++++++++++++++++++------- cms/templates/unit.html | 2 +- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py index b55dc13e58..4b925d53e0 100644 --- a/cms/djangoapps/contentstore/views.py +++ b/cms/djangoapps/contentstore/views.py @@ -66,7 +66,12 @@ from cms.djangoapps.models.settings.course_metadata import CourseMetadata log = logging.getLogger(__name__) -COMPONENT_TYPES = ['customtag', 'discussion', 'html', 'problem', 'video'] +COMPONENT_TYPES = ['customtag', 'discussion', 'html', 'problem', 'video', 'advanced'] + +# advanced/beta components that can be enabled for all courses or per-course in the policy file +ADVANCED_COMPONENT_TYPES = [] +ADVANCED_COMPONENT_CATEGORY = 'advanced' +ADVANCED_COMPONENT_POLICY_KEY = 'enable_advanced_modules' # cdodge: these are categories which should not be parented, they are detached from the hierarchy DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info'] @@ -281,15 +286,26 @@ def edit_unit(request, location): component_templates = defaultdict(list) + # check if there are any advanced modules specified in the course policy + advanced_component_types = list(ADVANCED_COMPONENT_TYPES) + course_metadata = CourseMetadata.fetch(course.location) + advanced_component_types.extend(course_metadata.get(ADVANCED_COMPONENT_POLICY_KEY, [])) + templates = modulestore().get_items(Location('i4x', 'edx', 'templates')) for template in templates: - if template.location.category in COMPONENT_TYPES: - component_templates[template.location.category].append(( - template.display_name, - template.location.url(), - 'markdown' in template.metadata, - 'empty' in template.metadata - )) + component_template = ( + template.display_name, + template.location.url(), + 'markdown' in template.metadata, + 'empty' in template.metadata + ) + if template.location.category in COMPONENT_TYPES: + component_templates[template.location.category].append(component_template) + elif template.location.category in advanced_component_types: + component_templates[ADVANCED_COMPONENT_CATEGORY].append(component_template) + + # order of component types for display purposes + component_template_types = [type for type in COMPONENT_TYPES if type in component_templates.keys()] components = [ component.location.url() @@ -341,6 +357,7 @@ def edit_unit(request, location): 'unit_location': location, 'components': components, 'component_templates': component_templates, + 'component_template_types': component_template_types, 'draft_preview_link': preview_lms_link, 'published_preview_link': lms_link, 'subsection': containing_subsection, diff --git a/cms/templates/unit.html b/cms/templates/unit.html index c529f5863a..686aaa9ba1 100644 --- a/cms/templates/unit.html +++ b/cms/templates/unit.html @@ -53,7 +53,7 @@
Add New Component