Commit a79a2907 by Victor Shnayder

Merge pull request #1755 from MITx/fix/brian/fix500v1

Fix/brian/fix500v1
parents 6d63d13f c639799a
...@@ -3,13 +3,11 @@ from django.test.utils import override_settings ...@@ -3,13 +3,11 @@ from django.test.utils import override_settings
import xmodule.modulestore.django import xmodule.modulestore.django
from courseware.tests.tests import PageLoader, TEST_DATA_XML_MODULESTORE from courseware.tests.tests import LoginEnrollmentTestCase, TEST_DATA_XML_MODULESTORE
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from xmodule.modulestore.xml_importer import import_from_xml
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE) @override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
class WikiRedirectTestCase(PageLoader): class WikiRedirectTestCase(LoginEnrollmentTestCase):
def setUp(self): def setUp(self):
xmodule.modulestore.django._MODULESTORES = {} xmodule.modulestore.django._MODULESTORES = {}
courses = modulestore().get_courses() courses = modulestore().get_courses()
...@@ -30,8 +28,6 @@ class WikiRedirectTestCase(PageLoader): ...@@ -30,8 +28,6 @@ class WikiRedirectTestCase(PageLoader):
self.activate_user(self.student) self.activate_user(self.student)
self.activate_user(self.instructor) self.activate_user(self.instructor)
def test_wiki_redirect(self): def test_wiki_redirect(self):
""" """
Test that requesting wiki URLs redirect properly to or out of classes. Test that requesting wiki URLs redirect properly to or out of classes.
...@@ -69,7 +65,6 @@ class WikiRedirectTestCase(PageLoader): ...@@ -69,7 +65,6 @@ class WikiRedirectTestCase(PageLoader):
self.assertEqual(resp.status_code, 302) self.assertEqual(resp.status_code, 302)
self.assertEqual(resp['Location'], 'http://testserver' + destination) self.assertEqual(resp['Location'], 'http://testserver' + destination)
def create_course_page(self, course): def create_course_page(self, course):
""" """
Test that loading the course wiki page creates the wiki page. Test that loading the course wiki page creates the wiki page.
...@@ -98,7 +93,6 @@ class WikiRedirectTestCase(PageLoader): ...@@ -98,7 +93,6 @@ class WikiRedirectTestCase(PageLoader):
self.assertTrue("course info" in resp.content.lower()) self.assertTrue("course info" in resp.content.lower())
self.assertTrue("courseware" in resp.content.lower()) self.assertTrue("courseware" in resp.content.lower())
def test_course_navigator(self): def test_course_navigator(self):
"""" """"
Test that going from a course page to a wiki page contains the course navigator. Test that going from a course page to a wiki page contains the course navigator.
...@@ -108,7 +102,6 @@ class WikiRedirectTestCase(PageLoader): ...@@ -108,7 +102,6 @@ class WikiRedirectTestCase(PageLoader):
self.enroll(self.toy) self.enroll(self.toy)
self.create_course_page(self.toy) self.create_course_page(self.toy)
course_wiki_page = reverse('wiki:get', kwargs={'path': self.toy.wiki_slug + '/'}) course_wiki_page = reverse('wiki:get', kwargs={'path': self.toy.wiki_slug + '/'})
referer = reverse("courseware", kwargs={'course_id': self.toy.id}) referer = reverse("courseware", kwargs={'course_id': self.toy.id})
......
...@@ -2,16 +2,16 @@ from mock import MagicMock ...@@ -2,16 +2,16 @@ from mock import MagicMock
import json import json
from django.http import Http404, HttpResponse from django.http import Http404, HttpResponse
from django.core.urlresolvers import reverse
from django.conf import settings from django.conf import settings
from django.test import TestCase from django.test import TestCase
from django.test.client import RequestFactory from django.test.client import RequestFactory
from django.core.urlresolvers import reverse
from django.test.utils import override_settings from django.test.utils import override_settings
from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.modulestore.exceptions import ItemNotFoundError
import courseware.module_render as render
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from courseware.tests.tests import PageLoader import courseware.module_render as render
from courseware.tests.tests import LoginEnrollmentTestCase
from courseware.model_data import ModelDataCache from courseware.model_data import ModelDataCache
from .factories import UserFactory from .factories import UserFactory
...@@ -38,7 +38,7 @@ TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR) ...@@ -38,7 +38,7 @@ TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR)
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE) @override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
class ModuleRenderTestCase(PageLoader): class ModuleRenderTestCase(LoginEnrollmentTestCase):
def setUp(self): def setUp(self):
self.location = ['i4x', 'edX', 'toy', 'chapter', 'Overview'] self.location = ['i4x', 'edX', 'toy', 'chapter', 'Overview']
self.course_id = 'edX/toy/2012_Fall' self.course_id = 'edX/toy/2012_Fall'
...@@ -54,10 +54,9 @@ class ModuleRenderTestCase(PageLoader): ...@@ -54,10 +54,9 @@ class ModuleRenderTestCase(PageLoader):
mock_request = MagicMock() mock_request = MagicMock()
mock_request.FILES.keys.return_value = ['file_id'] mock_request.FILES.keys.return_value = ['file_id']
mock_request.FILES.getlist.return_value = ['file'] * (settings.MAX_FILEUPLOADS_PER_INPUT + 1) mock_request.FILES.getlist.return_value = ['file'] * (settings.MAX_FILEUPLOADS_PER_INPUT + 1)
self.assertEquals(render.modx_dispatch(mock_request, 'dummy', self.location, self.assertEquals(render.modx_dispatch(mock_request, 'dummy', self.location, 'dummy').content,
'dummy').content, json.dumps({'success': 'Submission aborted! Maximum %d files may be submitted at once' %
json.dumps({'success': 'Submission aborted! Maximum %d files may be submitted at once' % settings.MAX_FILEUPLOADS_PER_INPUT}))
settings.MAX_FILEUPLOADS_PER_INPUT}))
mock_request_2 = MagicMock() mock_request_2 = MagicMock()
mock_request_2.FILES.keys.return_value = ['file_id'] mock_request_2.FILES.keys.return_value = ['file_id']
inputfile = Stub() inputfile = Stub()
...@@ -68,7 +67,7 @@ class ModuleRenderTestCase(PageLoader): ...@@ -68,7 +67,7 @@ class ModuleRenderTestCase(PageLoader):
self.assertEquals(render.modx_dispatch(mock_request_2, 'dummy', self.location, self.assertEquals(render.modx_dispatch(mock_request_2, 'dummy', self.location,
'dummy').content, 'dummy').content,
json.dumps({'success': 'Submission aborted! Your file "%s" is too large (max size: %d MB)' % json.dumps({'success': 'Submission aborted! Your file "%s" is too large (max size: %d MB)' %
(inputfile.name, settings.STUDENT_FILEUPLOAD_MAX_SIZE / (1000 ** 2))})) (inputfile.name, settings.STUDENT_FILEUPLOAD_MAX_SIZE / (1000 ** 2))}))
mock_request_3 = MagicMock() mock_request_3 = MagicMock()
mock_request_3.POST.copy.return_value = {} mock_request_3.POST.copy.return_value = {}
mock_request_3.FILES = False mock_request_3.FILES = False
...@@ -79,10 +78,10 @@ class ModuleRenderTestCase(PageLoader): ...@@ -79,10 +78,10 @@ class ModuleRenderTestCase(PageLoader):
self.assertRaises(ItemNotFoundError, render.modx_dispatch, self.assertRaises(ItemNotFoundError, render.modx_dispatch,
mock_request_3, 'dummy', self.location, 'toy') mock_request_3, 'dummy', self.location, 'toy')
self.assertRaises(Http404, render.modx_dispatch, mock_request_3, 'dummy', self.assertRaises(Http404, render.modx_dispatch, mock_request_3, 'dummy',
self.location, self.course_id) self.location, self.course_id)
mock_request_3.POST.copy.return_value = {'position': 1} mock_request_3.POST.copy.return_value = {'position': 1}
self.assertIsInstance(render.modx_dispatch(mock_request_3, 'goto_position', self.assertIsInstance(render.modx_dispatch(mock_request_3, 'goto_position',
self.location, self.course_id), HttpResponse) self.location, self.course_id), HttpResponse)
def test_get_score_bucket(self): def test_get_score_bucket(self):
self.assertEquals(render.get_score_bucket(0, 10), 'incorrect') self.assertEquals(render.get_score_bucket(0, 10), 'incorrect')
...@@ -124,19 +123,19 @@ class TestTOC(TestCase): ...@@ -124,19 +123,19 @@ class TestTOC(TestCase):
self.toy_course.id, self.portal_user, self.toy_course, depth=2) self.toy_course.id, self.portal_user, self.toy_course, depth=2)
expected = ([{'active': True, 'sections': expected = ([{'active': True, 'sections':
[{'url_name': 'Toy_Videos', 'display_name': u'Toy Videos', 'graded': True, [{'url_name': 'Toy_Videos', 'display_name': u'Toy Videos', 'graded': True,
'format': u'Lecture Sequence', 'due': '', 'active': False}, 'format': u'Lecture Sequence', 'due': '', 'active': False},
{'url_name': 'Welcome', 'display_name': u'Welcome', 'graded': True, {'url_name': 'Welcome', 'display_name': u'Welcome', 'graded': True,
'format': '', 'due': '', 'active': False}, 'format': '', 'due': '', 'active': False},
{'url_name': 'video_123456789012', 'display_name': 'video 123456789012', 'graded': True, {'url_name': 'video_123456789012', 'display_name': 'video 123456789012', 'graded': True,
'format': '', 'due': '', 'active': False}, 'format': '', 'due': '', 'active': False},
{'url_name': 'video_4f66f493ac8f', 'display_name': 'video 4f66f493ac8f', 'graded': True, {'url_name': 'video_4f66f493ac8f', 'display_name': 'video 4f66f493ac8f', 'graded': True,
'format': '', 'due': '', 'active': False}], 'format': '', 'due': '', 'active': False}],
'url_name': 'Overview', 'display_name': u'Overview'}, 'url_name': 'Overview', 'display_name': u'Overview'},
{'active': False, 'sections': {'active': False, 'sections':
[{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True, [{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True,
'format': '', 'due': '', 'active': False}], 'format': '', 'due': '', 'active': False}],
'url_name': 'secret:magic', 'display_name': 'secret:magic'}]) 'url_name': 'secret:magic', 'display_name': 'secret:magic'}])
actual = render.toc_for_course(self.portal_user, request, self.toy_course, chapter, None, model_data_cache) actual = render.toc_for_course(self.portal_user, request, self.toy_course, chapter, None, model_data_cache)
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
...@@ -151,19 +150,19 @@ class TestTOC(TestCase): ...@@ -151,19 +150,19 @@ class TestTOC(TestCase):
self.toy_course.id, self.portal_user, self.toy_course, depth=2) self.toy_course.id, self.portal_user, self.toy_course, depth=2)
expected = ([{'active': True, 'sections': expected = ([{'active': True, 'sections':
[{'url_name': 'Toy_Videos', 'display_name': u'Toy Videos', 'graded': True, [{'url_name': 'Toy_Videos', 'display_name': u'Toy Videos', 'graded': True,
'format': u'Lecture Sequence', 'due': '', 'active': False}, 'format': u'Lecture Sequence', 'due': '', 'active': False},
{'url_name': 'Welcome', 'display_name': u'Welcome', 'graded': True, {'url_name': 'Welcome', 'display_name': u'Welcome', 'graded': True,
'format': '', 'due': '', 'active': True}, 'format': '', 'due': '', 'active': True},
{'url_name': 'video_123456789012', 'display_name': 'video 123456789012', 'graded': True, {'url_name': 'video_123456789012', 'display_name': 'video 123456789012', 'graded': True,
'format': '', 'due': '', 'active': False}, 'format': '', 'due': '', 'active': False},
{'url_name': 'video_4f66f493ac8f', 'display_name': 'video 4f66f493ac8f', 'graded': True, {'url_name': 'video_4f66f493ac8f', 'display_name': 'video 4f66f493ac8f', 'graded': True,
'format': '', 'due': '', 'active': False}], 'format': '', 'due': '', 'active': False}],
'url_name': 'Overview', 'display_name': u'Overview'}, 'url_name': 'Overview', 'display_name': u'Overview'},
{'active': False, 'sections': {'active': False, 'sections':
[{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True, [{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True,
'format': '', 'due': '', 'active': False}], 'format': '', 'due': '', 'active': False}],
'url_name': 'secret:magic', 'display_name': 'secret:magic'}]) 'url_name': 'secret:magic', 'display_name': 'secret:magic'}])
actual = render.toc_for_course(self.portal_user, request, self.toy_course, chapter, section, model_data_cache) actual = render.toc_for_course(self.portal_user, request, self.toy_course, chapter, section, model_data_cache)
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
import json import json
import logging import logging
import xml.sax.saxutils as saxutils
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_POST from django.http import Http404
from django.http import HttpResponse, Http404
from django.utils import simplejson
from django.core.context_processors import csrf from django.core.context_processors import csrf
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User from django.contrib.auth.models import User
from mitxmako.shortcuts import render_to_response, render_to_string from mitxmako.shortcuts import render_to_response, render_to_string
from courseware.courses import get_course_with_access from courseware.courses import get_course_with_access
from course_groups.cohorts import is_course_cohorted, get_cohort_id, is_commentable_cohorted, get_cohorted_commentables, get_cohort, get_course_cohorts, get_cohort_by_id from course_groups.cohorts import (is_course_cohorted, get_cohort_id, is_commentable_cohorted,
get_cohorted_commentables, get_course_cohorts, get_cohort_by_id)
from courseware.access import has_access from courseware.access import has_access
from urllib import urlencode from django_comment_client.permissions import cached_has_permission
from operator import methodcaller from django_comment_client.utils import (merge_dict, extract, strip_none, get_courseware_context)
from django_comment_client.permissions import check_permissions_by_view, cached_has_permission
from django_comment_client.utils import (merge_dict, extract, strip_none,
strip_blank, get_courseware_context)
import django_comment_client.utils as utils import django_comment_client.utils as utils
import comment_client as cc import comment_client as cc
import xml.sax.saxutils as saxutils
THREADS_PER_PAGE = 20 THREADS_PER_PAGE = 20
INLINE_THREADS_PER_PAGE = 20 INLINE_THREADS_PER_PAGE = 20
...@@ -31,6 +25,7 @@ escapedict = {'"': '"'} ...@@ -31,6 +25,7 @@ escapedict = {'"': '"'}
log = logging.getLogger("edx.discussions") log = logging.getLogger("edx.discussions")
@login_required
def get_threads(request, course_id, discussion_id=None, per_page=THREADS_PER_PAGE): def get_threads(request, course_id, discussion_id=None, per_page=THREADS_PER_PAGE):
""" """
This may raise cc.utils.CommentClientError or This may raise cc.utils.CommentClientError or
...@@ -60,7 +55,6 @@ def get_threads(request, course_id, discussion_id=None, per_page=THREADS_PER_PAG ...@@ -60,7 +55,6 @@ def get_threads(request, course_id, discussion_id=None, per_page=THREADS_PER_PAG
cc_user.default_sort_key = request.GET.get('sort_key') cc_user.default_sort_key = request.GET.get('sort_key')
cc_user.save() cc_user.save()
#there are 2 dimensions to consider when executing a search with respect to group id #there are 2 dimensions to consider when executing a search with respect to group id
#is user a moderator #is user a moderator
#did the user request a group #did the user request a group
...@@ -91,18 +85,17 @@ def get_threads(request, course_id, discussion_id=None, per_page=THREADS_PER_PAG ...@@ -91,18 +85,17 @@ def get_threads(request, course_id, discussion_id=None, per_page=THREADS_PER_PAG
#now add the group name if the thread has a group id #now add the group name if the thread has a group id
for thread in threads: for thread in threads:
if thread.get('group_id'): if thread.get('group_id'):
thread['group_name'] = get_cohort_by_id(course_id, thread.get('group_id')).name thread['group_name'] = get_cohort_by_id(course_id, thread.get('group_id')).name
thread['group_string'] = "This post visible only to Group %s." % (thread['group_name']) thread['group_string'] = "This post visible only to Group %s." % (thread['group_name'])
else: else:
thread['group_name'] = "" thread['group_name'] = ""
thread['group_string'] = "This post visible to everyone." thread['group_string'] = "This post visible to everyone."
#patch for backward compatibility to comments service #patch for backward compatibility to comments service
if not 'pinned' in thread: if not 'pinned' in thread:
thread['pinned'] = False thread['pinned'] = False
query_params['page'] = page query_params['page'] = page
query_params['num_pages'] = num_pages query_params['num_pages'] = num_pages
...@@ -110,6 +103,7 @@ def get_threads(request, course_id, discussion_id=None, per_page=THREADS_PER_PAG ...@@ -110,6 +103,7 @@ def get_threads(request, course_id, discussion_id=None, per_page=THREADS_PER_PAG
return threads, query_params return threads, query_params
@login_required
def inline_discussion(request, course_id, discussion_id): def inline_discussion(request, course_id, discussion_id):
""" """
Renders JSON for DiscussionModules Renders JSON for DiscussionModules
...@@ -142,14 +136,14 @@ def inline_discussion(request, course_id, discussion_id): ...@@ -142,14 +136,14 @@ def inline_discussion(request, course_id, discussion_id):
cohorts_list = list() cohorts_list = list()
if is_cohorted: if is_cohorted:
cohorts_list.append({'name':'All Groups','id':None}) cohorts_list.append({'name': 'All Groups', 'id': None})
#if you're a mod, send all cohorts and let you pick #if you're a mod, send all cohorts and let you pick
if is_moderator: if is_moderator:
cohorts = get_course_cohorts(course_id) cohorts = get_course_cohorts(course_id)
for c in cohorts: for c in cohorts:
cohorts_list.append({'name':c.name, 'id':c.id}) cohorts_list.append({'name': c.name, 'id': c.id})
else: else:
#students don't get to choose #students don't get to choose
...@@ -216,9 +210,6 @@ def forum_form_discussion(request, course_id): ...@@ -216,9 +210,6 @@ def forum_form_discussion(request, course_id):
user_cohort_id = get_cohort_id(request.user, course_id) user_cohort_id = get_cohort_id(request.user, course_id)
context = { context = {
'csrf': csrf(request)['csrf_token'], 'csrf': csrf(request)['csrf_token'],
'course': course, 'course': course,
...@@ -242,6 +233,7 @@ def forum_form_discussion(request, course_id): ...@@ -242,6 +233,7 @@ def forum_form_discussion(request, course_id):
return render_to_response('discussion/index.html', context) return render_to_response('discussion/index.html', context)
@login_required @login_required
def single_thread(request, course_id, discussion_id, thread_id): def single_thread(request, course_id, discussion_id, thread_id):
course = get_course_with_access(request.user, course_id, 'load') course = get_course_with_access(request.user, course_id, 'load')
...@@ -250,11 +242,11 @@ def single_thread(request, course_id, discussion_id, thread_id): ...@@ -250,11 +242,11 @@ def single_thread(request, course_id, discussion_id, thread_id):
try: try:
thread = cc.Thread.find(thread_id).retrieve(recursive=True, user_id=request.user.id) thread = cc.Thread.find(thread_id).retrieve(recursive=True, user_id=request.user.id)
#patch for backward compatibility with comments service #patch for backward compatibility with comments service
if not 'pinned' in thread.attributes: if not 'pinned' in thread.attributes:
thread['pinned'] = False thread['pinned'] = False
except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError) as err: except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError) as err:
log.error("Error loading single thread.") log.error("Error loading single thread.")
raise Http404 raise Http404
...@@ -352,7 +344,7 @@ def user_profile(request, course_id, user_id): ...@@ -352,7 +344,7 @@ def user_profile(request, course_id, user_id):
query_params = { query_params = {
'page': request.GET.get('page', 1), 'page': request.GET.get('page', 1),
'per_page': THREADS_PER_PAGE, # more than threads_per_page to show more activities 'per_page': THREADS_PER_PAGE, # more than threads_per_page to show more activities
} }
threads, page, num_pages = profiled_user.active_threads(query_params) threads, page, num_pages = profiled_user.active_threads(query_params)
query_params['page'] = page query_params['page'] = page
...@@ -369,8 +361,6 @@ def user_profile(request, course_id, user_id): ...@@ -369,8 +361,6 @@ def user_profile(request, course_id, user_id):
'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict), 'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict),
}) })
else: else:
context = { context = {
'course': course, 'course': course,
'user': request.user, 'user': request.user,
...@@ -426,5 +416,5 @@ def followed_threads(request, course_id, user_id): ...@@ -426,5 +416,5 @@ def followed_threads(request, course_id, user_id):
} }
return render_to_response('discussion/user_profile.html', context) return render_to_response('discussion/user_profile.html', context)
except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError) as err: except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError):
raise Http404 raise Http404
from django.contrib.auth.models import User, Group
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.test.client import RequestFactory
from django.conf import settings
from mock import Mock
from django.test.utils import override_settings
import xmodule.modulestore.django
from student.models import CourseEnrollment
from django.db.models.signals import m2m_changed, pre_delete, pre_save, post_delete, post_save
from django.dispatch.dispatcher import _make_id
import string import string
import random import random
from .permissions import has_permission
from .models import Role, Permission
from xmodule.modulestore.django import modulestore
from xmodule.modulestore import Location
from xmodule.modulestore.xml_importer import import_from_xml
from xmodule.modulestore.xml import XMLModuleStore
import comment_client
from courseware.tests.tests import PageLoader, TEST_DATA_XML_MODULESTORE from django.contrib.auth.models import User
from django.test import TestCase
#@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
#class TestCohorting(PageLoader):
# """Check that cohorting works properly"""
#
# def setUp(self):
# xmodule.modulestore.django._MODULESTORES = {}
#
# # Assume courses are there
# self.toy = modulestore().get_course("edX/toy/2012_Fall")
#
# # Create two accounts
# self.student = 'view@test.com'
# self.student2 = 'view2@test.com'
# self.password = 'foo'
# self.create_account('u1', self.student, self.password)
# self.create_account('u2', self.student2, self.password)
# self.activate_user(self.student)
# self.activate_user(self.student2)
#
# def test_create_thread(self):
# my_save = Mock()
# comment_client.perform_request = my_save
#
# resp = self.client.post(
# reverse('django_comment_client.base.views.create_thread',
# kwargs={'course_id': 'edX/toy/2012_Fall',
# 'commentable_id': 'General'}),
# {'some': "some",
# 'data': 'data'})
# self.assertTrue(my_save.called)
#
# #self.assertEqual(resp.status_code, 200)
# #self.assertEqual(my_save.something, "expected", "complaint if not true")
#
# self.toy.cohort_config = {"cohorted": True}
#
# # call the view again ...
#
# # assert that different things happened
from student.models import CourseEnrollment
from django_comment_client.permissions import has_permission
from django_comment_client.models import Role
class PermissionsTestCase(TestCase): class PermissionsTestCase(TestCase):
......
...@@ -8,13 +8,6 @@ Notes for running by hand: ...@@ -8,13 +8,6 @@ Notes for running by hand:
django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/instructor django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/instructor
""" """
import courseware.tests.tests as ct
import json
from nose import SkipTest
from mock import patch, Mock
from django.test.utils import override_settings from django.test.utils import override_settings
# Need access to internal func to put users in the right group # Need access to internal func to put users in the right group
...@@ -26,13 +19,13 @@ from django_comment_client.models import Role, FORUM_ROLE_ADMINISTRATOR, \ ...@@ -26,13 +19,13 @@ from django_comment_client.models import Role, FORUM_ROLE_ADMINISTRATOR, \
from django_comment_client.utils import has_forum_access from django_comment_client.utils import has_forum_access
from courseware.access import _course_staff_group_name from courseware.access import _course_staff_group_name
import courseware.tests.tests as ct from courseware.tests.tests import LoginEnrollmentTestCase, TEST_DATA_XML_MODULESTORE, get_user
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
import xmodule.modulestore.django import xmodule.modulestore.django
@override_settings(MODULESTORE=ct.TEST_DATA_XML_MODULESTORE) @override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
class TestInstructorDashboardGradeDownloadCSV(ct.PageLoader): class TestInstructorDashboardGradeDownloadCSV(LoginEnrollmentTestCase):
''' '''
Check for download of csv Check for download of csv
''' '''
...@@ -55,7 +48,7 @@ class TestInstructorDashboardGradeDownloadCSV(ct.PageLoader): ...@@ -55,7 +48,7 @@ class TestInstructorDashboardGradeDownloadCSV(ct.PageLoader):
def make_instructor(course): def make_instructor(course):
group_name = _course_staff_group_name(course.location) group_name = _course_staff_group_name(course.location)
g = Group.objects.create(name=group_name) g = Group.objects.create(name=group_name)
g.user_set.add(ct.user(self.instructor)) g.user_set.add(get_user(self.instructor))
make_instructor(self.toy) make_instructor(self.toy)
...@@ -63,7 +56,6 @@ class TestInstructorDashboardGradeDownloadCSV(ct.PageLoader): ...@@ -63,7 +56,6 @@ class TestInstructorDashboardGradeDownloadCSV(ct.PageLoader):
self.login(self.instructor, self.password) self.login(self.instructor, self.password)
self.enroll(self.toy) self.enroll(self.toy)
def test_download_grades_csv(self): def test_download_grades_csv(self):
course = self.toy course = self.toy
url = reverse('instructor_dashboard', kwargs={'course_id': course.id}) url = reverse('instructor_dashboard', kwargs={'course_id': course.id})
...@@ -101,9 +93,8 @@ def action_name(operation, rolename): ...@@ -101,9 +93,8 @@ def action_name(operation, rolename):
return '{0} forum {1}'.format(operation, FORUM_ADMIN_ACTION_SUFFIX[rolename]) return '{0} forum {1}'.format(operation, FORUM_ADMIN_ACTION_SUFFIX[rolename])
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
@override_settings(MODULESTORE=ct.TEST_DATA_XML_MODULESTORE) class TestInstructorDashboardForumAdmin(LoginEnrollmentTestCase):
class TestInstructorDashboardForumAdmin(ct.PageLoader):
''' '''
Check for change in forum admin role memberships Check for change in forum admin role memberships
''' '''
...@@ -112,7 +103,6 @@ class TestInstructorDashboardForumAdmin(ct.PageLoader): ...@@ -112,7 +103,6 @@ class TestInstructorDashboardForumAdmin(ct.PageLoader):
xmodule.modulestore.django._MODULESTORES = {} xmodule.modulestore.django._MODULESTORES = {}
courses = modulestore().get_courses() courses = modulestore().get_courses()
self.course_id = "edX/toy/2012_Fall" self.course_id = "edX/toy/2012_Fall"
self.toy = modulestore().get_course(self.course_id) self.toy = modulestore().get_course(self.course_id)
...@@ -127,14 +117,12 @@ class TestInstructorDashboardForumAdmin(ct.PageLoader): ...@@ -127,14 +117,12 @@ class TestInstructorDashboardForumAdmin(ct.PageLoader):
group_name = _course_staff_group_name(self.toy.location) group_name = _course_staff_group_name(self.toy.location)
g = Group.objects.create(name=group_name) g = Group.objects.create(name=group_name)
g.user_set.add(ct.user(self.instructor)) g.user_set.add(get_user(self.instructor))
self.logout() self.logout()
self.login(self.instructor, self.password) self.login(self.instructor, self.password)
self.enroll(self.toy) self.enroll(self.toy)
def initialize_roles(self, course_id): def initialize_roles(self, course_id):
self.admin_role = Role.objects.get_or_create(name=FORUM_ROLE_ADMINISTRATOR, course_id=course_id)[0] self.admin_role = Role.objects.get_or_create(name=FORUM_ROLE_ADMINISTRATOR, course_id=course_id)[0]
self.moderator_role = Role.objects.get_or_create(name=FORUM_ROLE_MODERATOR, course_id=course_id)[0] self.moderator_role = Role.objects.get_or_create(name=FORUM_ROLE_MODERATOR, course_id=course_id)[0]
......
"""Tests for License package"""
import logging import logging
import json
from uuid import uuid4 from uuid import uuid4
from random import shuffle from random import shuffle
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
from factory import Factory, SubFactory
from django.test import TestCase from django.test import TestCase
from django.core.management import call_command from django.core.management import call_command
from django.core.urlresolvers import reverse
from .models import CourseSoftware, UserLicense from licenses.models import CourseSoftware, UserLicense
from courseware.tests.tests import LoginEnrollmentTestCase, get_user
COURSE_1 = 'edX/toy/2012_Fall' COURSE_1 = 'edX/toy/2012_Fall'
SOFTWARE_1 = 'matlab' SOFTWARE_1 = 'matlab'
SOFTWARE_2 = 'stata' SOFTWARE_2 = 'stata'
SERIAL_1 = '123456abcde'
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class CourseSoftwareFactory(Factory):
'''Factory for generating CourseSoftware objects in database'''
FACTORY_FOR = CourseSoftware
name = SOFTWARE_1
full_name = SOFTWARE_1
url = SOFTWARE_1
course_id = COURSE_1
class UserLicenseFactory(Factory):
'''
Factory for generating UserLicense objects in database
By default, the user assigned is null, indicating that the
serial number has not yet been assigned.
'''
FACTORY_FOR = UserLicense
software = SubFactory(CourseSoftwareFactory)
serial = SERIAL_1
class LicenseTestCase(LoginEnrollmentTestCase):
'''Tests for licenses.views'''
def setUp(self):
'''creates a user and logs in'''
self.setup_viewtest_user()
self.software = CourseSoftwareFactory()
def test_get_license(self):
UserLicenseFactory(user=get_user(self.viewtest_email), software=self.software)
response = self.client.post(reverse('user_software_license'),
{'software': SOFTWARE_1, 'generate': 'false'},
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
self.assertEqual(200, response.status_code)
json_returned = json.loads(response.content)
self.assertFalse('error' in json_returned)
self.assertTrue('serial' in json_returned)
self.assertEquals(json_returned['serial'], SERIAL_1)
def test_get_nonexistent_license(self):
response = self.client.post(reverse('user_software_license'),
{'software': SOFTWARE_1, 'generate': 'false'},
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
self.assertEqual(200, response.status_code)
json_returned = json.loads(response.content)
self.assertFalse('serial' in json_returned)
self.assertTrue('error' in json_returned)
def test_create_nonexistent_license(self):
'''Should not assign a license to an unlicensed user when none are available'''
response = self.client.post(reverse('user_software_license'),
{'software': SOFTWARE_1, 'generate': 'true'},
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
self.assertEqual(200, response.status_code)
json_returned = json.loads(response.content)
self.assertFalse('serial' in json_returned)
self.assertTrue('error' in json_returned)
def test_create_license(self):
'''Should assign a license to an unlicensed user if one is unassigned'''
# create an unassigned license
UserLicenseFactory(software=self.software)
response = self.client.post(reverse('user_software_license'),
{'software': SOFTWARE_1, 'generate': 'true'},
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
self.assertEqual(200, response.status_code)
json_returned = json.loads(response.content)
self.assertFalse('error' in json_returned)
self.assertTrue('serial' in json_returned)
self.assertEquals(json_returned['serial'], SERIAL_1)
def test_get_license_from_wrong_course(self):
response = self.client.post(reverse('user_software_license'),
{'software': SOFTWARE_1, 'generate': 'false'},
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
HTTP_REFERER='/courses/{0}/some_page'.format('some/other/course'))
self.assertEqual(404, response.status_code)
def test_get_license_from_non_ajax(self):
response = self.client.post(reverse('user_software_license'),
{'software': SOFTWARE_1, 'generate': 'false'},
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
self.assertEqual(404, response.status_code)
def test_get_license_without_software(self):
response = self.client.post(reverse('user_software_license'),
{'generate': 'false'},
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
self.assertEqual(404, response.status_code)
def test_get_license_without_login(self):
self.logout()
response = self.client.post(reverse('user_software_license'),
{'software': SOFTWARE_1, 'generate': 'false'},
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
# if we're not logged in, we should be referred to the login page
self.assertEqual(302, response.status_code)
class CommandTest(TestCase): class CommandTest(TestCase):
'''Test management command for importing serial numbers'''
def test_import_serial_numbers(self): def test_import_serial_numbers(self):
size = 20 size = 20
...@@ -51,31 +167,33 @@ class CommandTest(TestCase): ...@@ -51,31 +167,33 @@ class CommandTest(TestCase):
licenses_count = UserLicense.objects.all().count() licenses_count = UserLicense.objects.all().count()
self.assertEqual(3 * size, licenses_count) self.assertEqual(3 * size, licenses_count)
cs = CourseSoftware.objects.get(pk=1) software = CourseSoftware.objects.get(pk=1)
lics = UserLicense.objects.filter(software=cs)[:size] lics = UserLicense.objects.filter(software=software)[:size]
known_serials = list(l.serial for l in lics) known_serials = list(l.serial for l in lics)
known_serials.extend(generate_serials(10)) known_serials.extend(generate_serials(10))
shuffle(known_serials) shuffle(known_serials)
log.debug('Adding some new and old serials to {0}'.format(SOFTWARE_1)) log.debug('Adding some new and old serials to {0}'.format(SOFTWARE_1))
with NamedTemporaryFile() as f: with NamedTemporaryFile() as tmpfile:
f.write('\n'.join(known_serials)) tmpfile.write('\n'.join(known_serials))
f.flush() tmpfile.flush()
args = [COURSE_1, SOFTWARE_1, f.name] args = [COURSE_1, SOFTWARE_1, tmpfile.name]
call_command('import_serial_numbers', *args) call_command('import_serial_numbers', *args)
log.debug('Check if we added only the new ones') log.debug('Check if we added only the new ones')
licenses_count = UserLicense.objects.filter(software=cs).count() licenses_count = UserLicense.objects.filter(software=software).count()
self.assertEqual((2 * size) + 10, licenses_count) self.assertEqual((2 * size) + 10, licenses_count)
def generate_serials(size=20): def generate_serials(size=20):
'''generate a list of serial numbers'''
return [str(uuid4()) for _ in range(size)] return [str(uuid4()) for _ in range(size)]
def generate_serials_file(size=20): def generate_serials_file(size=20):
'''output list of generated serial numbers to a temp file'''
serials = generate_serials(size) serials = generate_serials(size)
temp_file = NamedTemporaryFile() temp_file = NamedTemporaryFile()
......
...@@ -7,12 +7,13 @@ from collections import namedtuple, defaultdict ...@@ -7,12 +7,13 @@ from collections import namedtuple, defaultdict
from mitxmako.shortcuts import render_to_string from mitxmako.shortcuts import render_to_string
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.http import HttpResponse, Http404 from django.http import HttpResponse, Http404
from django.views.decorators.csrf import requires_csrf_token, csrf_protect from django.views.decorators.csrf import requires_csrf_token
from .models import CourseSoftware from licenses.models import CourseSoftware
from .models import get_courses_licenses, get_or_create_license, get_license from licenses.models import get_courses_licenses, get_or_create_license, get_license
log = logging.getLogger("mitx.licenses") log = logging.getLogger("mitx.licenses")
...@@ -44,6 +45,7 @@ def get_licenses_by_course(user, courses): ...@@ -44,6 +45,7 @@ def get_licenses_by_course(user, courses):
return data_by_course return data_by_course
@login_required
@requires_csrf_token @requires_csrf_token
def user_software_license(request): def user_software_license(request):
if request.method != 'POST' or not request.is_ajax(): if request.method != 'POST' or not request.is_ajax():
...@@ -65,19 +67,21 @@ def user_software_license(request): ...@@ -65,19 +67,21 @@ def user_software_license(request):
try: try:
software = CourseSoftware.objects.get(name=software_name, software = CourseSoftware.objects.get(name=software_name,
course_id=course_id) course_id=course_id)
print software
except CourseSoftware.DoesNotExist: except CourseSoftware.DoesNotExist:
raise Http404 raise Http404
user = User.objects.get(id=user_id) try:
user = User.objects.get(id=user_id)
except User.DoesNotExist:
raise Http404
if generate: if generate:
license = get_or_create_license(user, software) software_license = get_or_create_license(user, software)
else: else:
license = get_license(user, software) software_license = get_license(user, software)
if license: if software_license:
response = {'serial': license.serial} response = {'serial': software_license.serial}
else: else:
response = {'error': 'No serial number found'} response = {'error': 'No serial number found'}
......
...@@ -4,22 +4,22 @@ Tests for open ended grading interfaces ...@@ -4,22 +4,22 @@ Tests for open ended grading interfaces
django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/open_ended_grading django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/open_ended_grading
""" """
from django.test import TestCase import json
from open_ended_grading import staff_grading_service from mock import MagicMock
from xmodule.open_ended_grading_classes import peer_grading_service
from xmodule import peer_grading_module
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from mitxmako.shortcuts import render_to_string
from courseware.access import _course_staff_group_name from xmodule.open_ended_grading_classes import peer_grading_service
import courseware.tests.tests as ct from xmodule import peer_grading_module
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
import xmodule.modulestore.django import xmodule.modulestore.django
from nose import SkipTest
from mock import patch, Mock, MagicMock
import json
from xmodule.x_module import ModuleSystem from xmodule.x_module import ModuleSystem
from mitxmako.shortcuts import render_to_string
from open_ended_grading import staff_grading_service
from courseware.access import _course_staff_group_name
from courseware.tests.tests import LoginEnrollmentTestCase, TEST_DATA_XML_MODULESTORE, get_user
import logging import logging
...@@ -30,8 +30,8 @@ from django.http import QueryDict ...@@ -30,8 +30,8 @@ from django.http import QueryDict
from xmodule.tests import test_util_open_ended from xmodule.tests import test_util_open_ended
@override_settings(MODULESTORE=ct.TEST_DATA_XML_MODULESTORE) @override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
class TestStaffGradingService(ct.PageLoader): class TestStaffGradingService(LoginEnrollmentTestCase):
''' '''
Check that staff grading service proxy works. Basically just checking the Check that staff grading service proxy works. Basically just checking the
access control and error handling logic -- all the actual work is on the access control and error handling logic -- all the actual work is on the
...@@ -56,7 +56,7 @@ class TestStaffGradingService(ct.PageLoader): ...@@ -56,7 +56,7 @@ class TestStaffGradingService(ct.PageLoader):
def make_instructor(course): def make_instructor(course):
group_name = _course_staff_group_name(course.location) group_name = _course_staff_group_name(course.location)
g = Group.objects.create(name=group_name) g = Group.objects.create(name=group_name)
g.user_set.add(ct.user(self.instructor)) g.user_set.add(get_user(self.instructor))
make_instructor(self.toy) make_instructor(self.toy)
...@@ -126,8 +126,8 @@ class TestStaffGradingService(ct.PageLoader): ...@@ -126,8 +126,8 @@ class TestStaffGradingService(ct.PageLoader):
self.assertIsNotNone(d['problem_list']) self.assertIsNotNone(d['problem_list'])
@override_settings(MODULESTORE=ct.TEST_DATA_XML_MODULESTORE) @override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
class TestPeerGradingService(ct.PageLoader): class TestPeerGradingService(LoginEnrollmentTestCase):
''' '''
Check that staff grading service proxy works. Basically just checking the Check that staff grading service proxy works. Basically just checking the
access control and error handling logic -- all the actual work is on the access control and error handling logic -- all the actual work is on the
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment