Commit 1f5beba3 by Diana Huang

Merge branch 'master' into diana/rubric-ui-improvements

parents f9304a88 e89948a5
from factory import Factory from factory import Factory
from xmodule.modulestore import Location from datetime import datetime
from xmodule.modulestore.django import modulestore
from time import gmtime
from uuid import uuid4 from uuid import uuid4
from xmodule.timeparse import stringify_time from student.models import (User, UserProfile, Registration,
CourseEnrollmentAllowed)
from django.contrib.auth.models import Group
class UserProfileFactory(Factory):
FACTORY_FOR = UserProfile
def XMODULE_COURSE_CREATION(class_to_create, **kwargs): user = None
return XModuleCourseFactory._create(class_to_create, **kwargs) name = 'Robot Studio'
courseware = 'course.xml'
def XMODULE_ITEM_CREATION(class_to_create, **kwargs): class RegistrationFactory(Factory):
return XModuleItemFactory._create(class_to_create, **kwargs) FACTORY_FOR = Registration
class XModuleCourseFactory(Factory): user = None
""" activation_key = uuid4().hex
Factory for XModule courses.
"""
ABSTRACT_FACTORY = True class UserFactory(Factory):
_creation_function = (XMODULE_COURSE_CREATION,) FACTORY_FOR = User
@classmethod username = 'robot'
def _create(cls, target_class, *args, **kwargs): 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()
template = Location('i4x', 'edx', 'templates', 'course', 'Empty') class GroupFactory(Factory):
org = kwargs.get('org') FACTORY_FOR = Group
number = kwargs.get('number')
display_name = kwargs.get('display_name')
location = Location('i4x', org, number,
'course', Location.clean(display_name))
store = modulestore('direct') name = 'test_group'
# Write the data to the mongo datastore class CourseEnrollmentAllowedFactory(Factory):
new_course = store.clone_item(template, location) FACTORY_FOR = CourseEnrollmentAllowed
# This metadata code was copied from cms/djangoapps/contentstore/views.py email = 'test@edx.org'
if display_name is not None: course_id = 'edX/test/2012_Fall'
new_course.metadata['display_name'] = display_name
new_course.metadata['data_dir'] = uuid4().hex
new_course.metadata['start'] = stringify_time(gmtime())
new_course.tabs = [{"type": "courseware"},
{"type": "course_info", "name": "Course Info"},
{"type": "discussion", "name": "Discussion"},
{"type": "wiki", "name": "Wiki"},
{"type": "progress", "name": "Progress"}]
# Update the data in the mongo datastore
store.update_metadata(new_course.location.url(), new_course.own_metadata)
return new_course
class Course:
pass
class CourseFactory(XModuleCourseFactory):
FACTORY_FOR = Course
template = 'i4x://edx/templates/course/Empty'
org = 'MITx'
number = '999'
display_name = 'Robot Super Course'
class XModuleItemFactory(Factory):
"""
Factory for XModule items.
"""
ABSTRACT_FACTORY = True
_creation_function = (XMODULE_ITEM_CREATION,)
@classmethod
def _create(cls, target_class, *args, **kwargs):
"""
kwargs must include parent_location, template. Can contain display_name
target_class is ignored
"""
DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info']
parent_location = Location(kwargs.get('parent_location'))
template = Location(kwargs.get('template'))
display_name = kwargs.get('display_name')
store = modulestore('direct')
# This code was based off that in cms/djangoapps/contentstore/views.py
parent = store.get_item(parent_location)
dest_location = parent_location._replace(category=template.category, name=uuid4().hex)
new_item = store.clone_item(template, dest_location)
# TODO: This needs to be deleted when we have proper storage for static content
new_item.metadata['data_dir'] = parent.metadata['data_dir']
# replace the display name with an optional parameter passed in from the caller
if display_name is not None:
new_item.metadata['display_name'] = display_name
store.update_metadata(new_item.location.url(), new_item.own_metadata)
if new_item.location.category not in DETACHED_CATEGORIES:
store.update_children(parent_location, parent.definition.get('children', []) + [new_item.location.url()])
return new_item
class Item:
pass
class ItemFactory(XModuleItemFactory):
FACTORY_FOR = Item
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
from django.test.testcases import TestCase
from cache_toolbox.core import get_cached_content, set_cached_content, del_cached_content from cache_toolbox.core import get_cached_content, set_cached_content, del_cached_content
from xmodule.modulestore import Location from xmodule.modulestore import Location
from xmodule.contentstore.content import StaticContent from xmodule.contentstore.content import StaticContent
from django.test import TestCase
class Content: class Content:
def __init__(self, location, content): def __init__(self, location, content):
...@@ -32,7 +32,3 @@ class CachingTestCase(TestCase): ...@@ -32,7 +32,3 @@ class CachingTestCase(TestCase):
'should not be stored in cache with unicodeLocation') 'should not be stored in cache with unicodeLocation')
self.assertEqual(None, get_cached_content(self.nonUnicodeLocation), self.assertEqual(None, get_cached_content(self.nonUnicodeLocation),
'should not be stored in cache with nonUnicodeLocation') 'should not be stored in cache with nonUnicodeLocation')
from django.test.testcases import TestCase
import datetime import datetime
import time import time
from django.contrib.auth.models import User
import xmodule
from django.test.client import Client
from django.core.urlresolvers import reverse
from xmodule.modulestore import Location
from cms.djangoapps.models.settings.course_details import CourseDetails,\
CourseSettingsEncoder
import json import json
from util import converters
import calendar import calendar
import copy
from util import converters
from util.converters import jsdate_to_time from util.converters import jsdate_to_time
from django.contrib.auth.models import User
from django.test.client import Client
from django.core.urlresolvers import reverse
from django.utils.timezone import UTC from django.utils.timezone import UTC
import xmodule
from xmodule.modulestore import Location
from cms.djangoapps.models.settings.course_details import (CourseDetails,
CourseSettingsEncoder)
from cms.djangoapps.models.settings.course_grading import CourseGradingModel from cms.djangoapps.models.settings.course_grading import CourseGradingModel
from cms.djangoapps.contentstore.utils import get_modulestore from cms.djangoapps.contentstore.utils import get_modulestore
import copy
from django.test import TestCase
from utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
# YYYY-MM-DDThh:mm:ss.s+/-HH:MM # YYYY-MM-DDThh:mm:ss.s+/-HH:MM
class ConvertersTestCase(TestCase): class ConvertersTestCase(TestCase):
...@@ -36,8 +42,15 @@ class ConvertersTestCase(TestCase): ...@@ -36,8 +42,15 @@ class ConvertersTestCase(TestCase):
self.compare_dates(converters.jsdate_to_time("2013-01-01T00:00:00"), converters.jsdate_to_time("2012-12-31T23:59:59"), datetime.timedelta(seconds=1)) self.compare_dates(converters.jsdate_to_time("2013-01-01T00:00:00"), converters.jsdate_to_time("2012-12-31T23:59:59"), datetime.timedelta(seconds=1))
class CourseTestCase(TestCase): class CourseTestCase(ModuleStoreTestCase):
def setUp(self): def setUp(self):
"""
These tests need a user in the DB so that the django Test Client
can log them in.
They inherit from the ModuleStoreTestCase class so that the mongodb collection
will be cleared out before each test case execution and deleted
afterwards.
"""
uname = 'testuser' uname = 'testuser'
email = 'test+courses@edx.org' email = 'test+courses@edx.org'
password = 'foo' password = 'foo'
...@@ -52,36 +65,15 @@ class CourseTestCase(TestCase): ...@@ -52,36 +65,15 @@ class CourseTestCase(TestCase):
self.user.is_staff = True self.user.is_staff = True
self.user.save() self.user.save()
# Flush and initialize the module store
# It needs the templates because it creates new records
# by cloning from the template.
# Note that if your test module gets in some weird state
# (though it shouldn't), do this manually
# from the bash shell to drop it:
# $ mongo test_xmodule --eval "db.dropDatabase()"
xmodule.modulestore.django._MODULESTORES = {}
xmodule.modulestore.django.modulestore().collection.drop()
xmodule.templates.update_templates()
self.client = Client() self.client = Client()
self.client.login(username=uname, password=password) self.client.login(username=uname, password=password)
self.course_data = { t='i4x://edx/templates/course/Empty'
'template': 'i4x://edx/templates/course/Empty', o='MITx'
'org': 'MITx', n='999'
'number': '999', dn='Robot Super Course'
'display_name': 'Robot Super Course', self.course_location = Location('i4x', o, n, 'course', 'Robot_Super_Course')
} CourseFactory.create(template=t, org=o, number=n, display_name=dn)
self.course_location = Location('i4x', 'MITx', '999', 'course', 'Robot_Super_Course')
self.create_course()
def tearDown(self):
xmodule.modulestore.django._MODULESTORES = {}
xmodule.modulestore.django.modulestore().collection.drop()
def create_course(self):
"""Create new course"""
self.client.post(reverse('create_new_course'), self.course_data)
class CourseDetailsTestCase(CourseTestCase): class CourseDetailsTestCase(CourseTestCase):
def test_virgin_fetch(self): def test_virgin_fetch(self):
...@@ -145,7 +137,6 @@ class CourseDetailsViewTest(CourseTestCase): ...@@ -145,7 +137,6 @@ class CourseDetailsViewTest(CourseTestCase):
return datetime.isoformat("T") return datetime.isoformat("T")
else: else:
return None return None
def test_update_and_fetch(self): def test_update_and_fetch(self):
details = CourseDetails.fetch(self.course_location) details = CourseDetails.fetch(self.course_location)
...@@ -271,5 +262,3 @@ class CourseGradingTest(CourseTestCase): ...@@ -271,5 +262,3 @@ class CourseGradingTest(CourseTestCase):
test_grader.graders[1]['drop_count'] = test_grader.graders[1].get('drop_count') + 1 test_grader.graders[1]['drop_count'] = test_grader.graders[1].get('drop_count') + 1
altered_grader = CourseGradingModel.update_grader_from_json(test_grader.course_location, test_grader.graders[1]) altered_grader = CourseGradingModel.update_grader_from_json(test_grader.course_location, test_grader.graders[1])
self.assertDictEqual(test_grader.graders[1], altered_grader, "drop_count[1] + 2") self.assertDictEqual(test_grader.graders[1], altered_grader, "drop_count[1] + 2")
from django.test.testcases import TestCase
from cms.djangoapps.contentstore import utils from cms.djangoapps.contentstore import utils
import mock import mock
from django.test import TestCase
class LMSLinksTestCase(TestCase): class LMSLinksTestCase(TestCase):
def about_page_test(self): def about_page_test(self):
......
import json
import copy
from time import time
from django.test import TestCase
from override_settings import override_settings
from django.conf import settings
from student.models import Registration
from django.contrib.auth.models import User
import xmodule.modulestore.django
from xmodule.templates import update_templates
class ModuleStoreTestCase(TestCase):
""" Subclass for any test case that uses the mongodb
module store. This populates a uniquely named modulestore
collection with templates before running the TestCase
and drops it they are finished. """
def _pre_setup(self):
super(ModuleStoreTestCase, self)._pre_setup()
# Use the current seconds since epoch to differentiate
# the mongo collections on jenkins.
sec_since_epoch = '%s' % int(time()*100)
self.orig_MODULESTORE = copy.deepcopy(settings.MODULESTORE)
self.test_MODULESTORE = self.orig_MODULESTORE
self.test_MODULESTORE['default']['OPTIONS']['collection'] = 'modulestore_%s' % sec_since_epoch
self.test_MODULESTORE['direct']['OPTIONS']['collection'] = 'modulestore_%s' % sec_since_epoch
settings.MODULESTORE = self.test_MODULESTORE
# Flush and initialize the module store
# It needs the templates because it creates new records
# by cloning from the template.
# Note that if your test module gets in some weird state
# (though it shouldn't), do this manually
# from the bash shell to drop it:
# $ mongo test_xmodule --eval "db.dropDatabase()"
xmodule.modulestore.django._MODULESTORES = {}
update_templates()
def _post_teardown(self):
# Make sure you flush out the modulestore.
# Drop the collection at the end of the test,
# otherwise there will be lingering collections leftover
# from executing the tests.
xmodule.modulestore.django._MODULESTORES = {}
xmodule.modulestore.django.modulestore().collection.drop()
settings.MODULESTORE = self.orig_MODULESTORE
super(ModuleStoreTestCase, self)._post_teardown()
def parse_json(response):
"""Parse response, which is assumed to be json"""
return json.loads(response.content)
def user(email):
"""look up a user by email"""
return User.objects.get(email=email)
def registration(email):
"""look up registration object by email"""
return Registration.objects.get(user__email=email)
...@@ -31,7 +31,7 @@ from xmodule.modulestore.exceptions import ItemNotFoundError, InvalidLocationErr ...@@ -31,7 +31,7 @@ from xmodule.modulestore.exceptions import ItemNotFoundError, InvalidLocationErr
from xmodule.x_module import ModuleSystem from xmodule.x_module import ModuleSystem
from xmodule.error_module import ErrorDescriptor from xmodule.error_module import ErrorDescriptor
from xmodule.errortracker import exc_info_to_str from xmodule.errortracker import exc_info_to_str
from static_replace import replace_static_urls import static_replace
from external_auth.views import ssl_login_shortcut from external_auth.views import ssl_login_shortcut
from mitxmako.shortcuts import render_to_response, render_to_string from mitxmako.shortcuts import render_to_response, render_to_string
...@@ -473,7 +473,7 @@ def preview_module_system(request, preview_id, descriptor): ...@@ -473,7 +473,7 @@ def preview_module_system(request, preview_id, descriptor):
get_module=partial(get_preview_module, request, preview_id), get_module=partial(get_preview_module, request, preview_id),
render_template=render_from_lms, render_template=render_from_lms,
debug=True, debug=True,
replace_urls=partial(replace_static_urls, data_directory=None, course_namespace=descriptor.location), replace_urls=partial(static_replace.replace_static_urls, data_directory=None, course_namespace=descriptor.location),
user=request.user, user=request.user,
) )
...@@ -1240,6 +1240,11 @@ def edge(request): ...@@ -1240,6 +1240,11 @@ def edge(request):
@login_required @login_required
@expect_json @expect_json
def create_new_course(request): def create_new_course(request):
# This logic is repeated in xmodule/modulestore/tests/factories.py
# so if you change anything here, you need to also change it there.
# TODO: write a test that creates two courses, one with the factory and
# the other with this method, then compare them to make sure they are
# equivalent.
template = Location(request.POST['template']) template = Location(request.POST['template'])
org = request.POST.get('org') org = request.POST.get('org')
number = request.POST.get('number') number = request.POST.get('number')
...@@ -1289,8 +1294,11 @@ def initialize_course_tabs(course): ...@@ -1289,8 +1294,11 @@ def initialize_course_tabs(course):
# at least a list populated with the minimal times # at least a list populated with the minimal times
# @TODO: I don't like the fact that the presentation tier is away of these data related constraints, let's find a better # @TODO: I don't like the fact that the presentation tier is away of these data related constraints, let's find a better
# place for this. Also rather than using a simple list of dictionaries a nice class model would be helpful here # place for this. Also rather than using a simple list of dictionaries a nice class model would be helpful here
course.tabs = [{"type": "courseware"},
{"type": "course_info", "name": "Course Info"}, # This logic is repeated in xmodule/modulestore/tests/factories.py
# so if you change anything here, you need to also change it there.
course.tabs = [{"type": "courseware"},
{"type": "course_info", "name": "Course Info"},
{"type": "discussion", "name": "Discussion"}, {"type": "discussion", "name": "Discussion"},
{"type": "wiki", "name": "Wiki"}, {"type": "wiki", "name": "Wiki"},
{"type": "progress", "name": "Progress"}] {"type": "progress", "name": "Progress"}]
......
...@@ -11,7 +11,6 @@ from .common import * ...@@ -11,7 +11,6 @@ from .common import *
import os import os
from path import path from path import path
# Nose Test Runner # Nose Test Runner
INSTALLED_APPS += ('django_nose',) INSTALLED_APPS += ('django_nose',)
NOSE_ARGS = ['--with-xunit'] NOSE_ARGS = ['--with-xunit']
...@@ -72,17 +71,6 @@ DATABASES = { ...@@ -72,17 +71,6 @@ DATABASES = {
'ENGINE': 'django.db.backends.sqlite3', 'ENGINE': 'django.db.backends.sqlite3',
'NAME': ENV_ROOT / "db" / "cms.db", 'NAME': ENV_ROOT / "db" / "cms.db",
}, },
# The following are for testing purposes...
'edX/toy/2012_Fall': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ENV_ROOT / "db" / "course1.db",
},
'edx/full/6.002_Spring_2012': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ENV_ROOT / "db" / "course2.db",
}
} }
LMS_BASE = "localhost:8000" LMS_BASE = "localhost:8000"
......
...@@ -21,7 +21,9 @@ class StaticContentServer(object): ...@@ -21,7 +21,9 @@ class StaticContentServer(object):
try: try:
content = contentstore().find(loc) content = contentstore().find(loc)
except NotFoundError: except NotFoundError:
raise Http404 response = HttpResponse()
response.status_code = 404
return response
# since we fetched it from DB, let's cache it going forward # since we fetched it from DB, let's cache it going forward
set_cached_content(content) set_cached_content(content)
......
from factory import Factory
from time import gmtime
from uuid import uuid4
from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
from xmodule.timeparse import stringify_time
def XMODULE_COURSE_CREATION(class_to_create, **kwargs):
return XModuleCourseFactory._create(class_to_create, **kwargs)
def XMODULE_ITEM_CREATION(class_to_create, **kwargs):
return XModuleItemFactory._create(class_to_create, **kwargs)
class XModuleCourseFactory(Factory):
"""
Factory for XModule courses.
"""
ABSTRACT_FACTORY = True
_creation_function = (XMODULE_COURSE_CREATION,)
@classmethod
def _create(cls, target_class, *args, **kwargs):
# This logic was taken from the create_new_course method in
# cms/djangoapps/contentstore/views.py
template = Location('i4x', 'edx', 'templates', 'course', 'Empty')
org = kwargs.get('org')
number = kwargs.get('number')
display_name = kwargs.get('display_name')
location = Location('i4x', org, number,
'course', Location.clean(display_name))
store = modulestore('direct')
# Write the data to the mongo datastore
new_course = store.clone_item(template, location)
# This metadata code was copied from cms/djangoapps/contentstore/views.py
if display_name is not None:
new_course.metadata['display_name'] = display_name
new_course.metadata['data_dir'] = uuid4().hex
new_course.metadata['start'] = stringify_time(gmtime())
new_course.tabs = [{"type": "courseware"},
{"type": "course_info", "name": "Course Info"},
{"type": "discussion", "name": "Discussion"},
{"type": "wiki", "name": "Wiki"},
{"type": "progress", "name": "Progress"}]
# Update the data in the mongo datastore
store.update_metadata(new_course.location.url(), new_course.own_metadata)
return new_course
class Course:
pass
class CourseFactory(XModuleCourseFactory):
FACTORY_FOR = Course
template = 'i4x://edx/templates/course/Empty'
org = 'MITx'
number = '999'
display_name = 'Robot Super Course'
class XModuleItemFactory(Factory):
"""
Factory for XModule items.
"""
ABSTRACT_FACTORY = True
_creation_function = (XMODULE_ITEM_CREATION,)
@classmethod
def _create(cls, target_class, *args, **kwargs):
"""
kwargs must include parent_location, template. Can contain display_name
target_class is ignored
"""
DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info']
parent_location = Location(kwargs.get('parent_location'))
template = Location(kwargs.get('template'))
display_name = kwargs.get('display_name')
store = modulestore('direct')
# This code was based off that in cms/djangoapps/contentstore/views.py
parent = store.get_item(parent_location)
dest_location = parent_location._replace(category=template.category, name=uuid4().hex)
new_item = store.clone_item(template, dest_location)
# TODO: This needs to be deleted when we have proper storage for static content
new_item.metadata['data_dir'] = parent.metadata['data_dir']
# replace the display name with an optional parameter passed in from the caller
if display_name is not None:
new_item.metadata['display_name'] = display_name
store.update_metadata(new_item.location.url(), new_item.own_metadata)
if new_item.location.category not in DETACHED_CATEGORIES:
store.update_children(parent_location, parent.definition.get('children', []) + [new_item.location.url()])
return new_item
class Item:
pass
class ItemFactory(XModuleItemFactory):
FACTORY_FOR = Item
parent_location = 'i4x://MITx/999/course/Robot_Super_Course'
template = 'i4x://edx/templates/chapter/Empty'
display_name = 'Section One'
...@@ -268,6 +268,7 @@ Supported fields at the course level: ...@@ -268,6 +268,7 @@ Supported fields at the course level:
* "start" -- specify the start date for the course. Format-by-example: "2012-09-05T12:00". * "start" -- specify the start date for the course. Format-by-example: "2012-09-05T12:00".
* "advertised_start" -- specify what you want displayed as the start date of the course in the course listing and course about pages. This can be useful if you want to let people in early before the formal start. Format-by-example: "2012-09-05T12:00". * "advertised_start" -- specify what you want displayed as the start date of the course in the course listing and course about pages. This can be useful if you want to let people in early before the formal start. Format-by-example: "2012-09-05T12:00".
* "disable_policy_graph" -- set to true (or "Yes"), if the policy graph should be disabled (ie not shown).
* "enrollment_start", "enrollment_end" -- when can students enroll? (if not specified, can enroll anytime). Same format as "start". * "enrollment_start", "enrollment_end" -- when can students enroll? (if not specified, can enroll anytime). Same format as "start".
* "end" -- specify the end date for the course. Format-by-example: "2012-11-05T12:00". * "end" -- specify the end date for the course. Format-by-example: "2012-11-05T12:00".
* "end_of_course_survey_url" -- a url for an end of course survey -- shown after course is over, next to certificate download links. * "end_of_course_survey_url" -- a url for an end of course survey -- shown after course is over, next to certificate download links.
......
...@@ -46,13 +46,11 @@ class Role(models.Model): ...@@ -46,13 +46,11 @@ class Role(models.Model):
def add_permission(self, permission): def add_permission(self, permission):
self.permissions.add(Permission.objects.get_or_create(name=permission)[0]) self.permissions.add(Permission.objects.get_or_create(name=permission)[0])
def has_permission(self, permission): def has_permission(self, permission):
course = get_course_by_id(self.course_id) course = get_course_by_id(self.course_id)
changing_comments = permission.startswith('edit') or \ if self.name == FORUM_ROLE_STUDENT and \
permission.startswith('update') or permission.startswith('create') (permission.startswith('edit') or permission.startswith('update') or permission.startswith('create')) and \
in_blackout_period = not course.forum_posts_allowed (not course.forum_posts_allowed):
if (self.name == FORUM_ROLE_STUDENT) and in_blackout_period and changing_comments:
return False return False
return self.permissions.filter(name=permission).exists() return self.permissions.filter(name=permission).exists()
......
...@@ -29,7 +29,6 @@ def has_permission(user, permission, course_id=None): ...@@ -29,7 +29,6 @@ def has_permission(user, permission, course_id=None):
CONDITIONS = ['is_open', 'is_author'] CONDITIONS = ['is_open', 'is_author']
# data may be a json file
def check_condition(user, condition, course_id, data): def check_condition(user, condition, course_id, data):
def check_open(user, condition, course_id, data): def check_open(user, condition, course_id, data):
try: try:
...@@ -62,10 +61,8 @@ def check_conditions_permissions(user, permissions, course_id, **kwargs): ...@@ -62,10 +61,8 @@ def check_conditions_permissions(user, permissions, course_id, **kwargs):
def test(user, per, operator="or"): def test(user, per, operator="or"):
if isinstance(per, basestring): if isinstance(per, basestring):
if per in CONDITIONS: if per in CONDITIONS:
return check_condition(user, per, course_id, kwargs['data']) return check_condition(user, per, course_id, kwargs)
return cached_has_permission(user, per, course_id=course_id) return cached_has_permission(user, per, course_id=course_id)
# TODO: refactor this to be more clear.
# e.g. the "and operator in" bit on the next line is not needed?
elif isinstance(per, list) and operator in ["and", "or"]: elif isinstance(per, list) and operator in ["and", "or"]:
results = [test(user, x, operator="and") for x in per] results = [test(user, x, operator="and") for x in per]
if operator == "or": if operator == "or":
...@@ -105,4 +102,4 @@ def check_permissions_by_view(user, course_id, content, name): ...@@ -105,4 +102,4 @@ def check_permissions_by_view(user, course_id, content, name):
p = VIEW_PERMISSIONS[name] p = VIEW_PERMISSIONS[name]
except KeyError: except KeyError:
logging.warning("Permission for view named %s does not exist in permissions.py" % name) logging.warning("Permission for view named %s does not exist in permissions.py" % name)
return check_conditions_permissions(user, p, course_id, data=content) return check_conditions_permissions(user, p, course_id, content=content)
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 override_settings 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 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
#@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.metadata["cohort_config"] = {"cohorted": True}
#
# # call the view again ...
#
# # assert that different things happened
class PermissionsTestCase(TestCase):
def random_str(self, length=15, chars=string.ascii_uppercase + string.digits):
return ''.join(random.choice(chars) for x in range(length))
def setUp(self):
self.course_id = "edX/toy/2012_Fall"
self.moderator_role = Role.objects.get_or_create(name="Moderator", course_id=self.course_id)[0]
self.student_role = Role.objects.get_or_create(name="Student", course_id=self.course_id)[0]
self.student = User.objects.create(username=self.random_str(),
password="123456", email="john@yahoo.com")
self.moderator = User.objects.create(username=self.random_str(),
password="123456", email="staff@edx.org")
self.moderator.is_staff = True
self.moderator.save()
self.student_enrollment = CourseEnrollment.objects.create(user=self.student, course_id=self.course_id)
self.moderator_enrollment = CourseEnrollment.objects.create(user=self.moderator, course_id=self.course_id)
def tearDown(self):
self.student_enrollment.delete()
self.moderator_enrollment.delete()
# Do we need to have this? We shouldn't be deleting students, ever
# self.student.delete()
# self.moderator.delete()
def testDefaultRoles(self):
self.assertTrue(self.student_role in self.student.roles.all())
self.assertTrue(self.moderator_role in self.moderator.roles.all())
def testPermission(self):
name = self.random_str()
self.moderator_role.add_permission(name)
self.assertTrue(has_permission(self.moderator, name, self.course_id))
self.student_role.add_permission(name)
self.assertTrue(has_permission(self.student, name, self.course_id))
import django_comment_client.models as models
import django_comment_client.permissions as permissions
from django.test import TestCase
from nose.plugins.skip import SkipTest
from courseware.courses import get_course_by_id
class RoleClassTestCase(TestCase):
def setUp(self):
self.course_id = "edX/toy/2012_Fall"
self.student_role = models.Role.objects.create(name="Student",
course_id=self.course_id)
def test_unicode(self):
self.assertEqual(str(self.student_role), "Student for edX/toy/2012_Fall")
self.admin_for_all = models.Role.objects.create(name="Administrator")
self.assertEqual(str(self.admin_for_all), "Administrator for all courses")
def test_has_permission(self):
self.student_role.add_permission("delete_thread")
self.TA_role = models.Role.objects.create(name="Community TA",
course_id=self.course_id)
self.assertTrue(self.student_role.has_permission("delete_thread"))
self.assertFalse(self.TA_role.has_permission("delete_thread"))
# Toy course does not have a blackout period defined.
def test_students_can_create_if_not_during_blackout(self):
self.student_role.add_permission("create_comment")
self.assertTrue(self.student_role.has_permission("create_comment"))
def test_students_cannot_create_during_blackout(self):
# Not sure how to set up these conditions
raise SkipTest()
def test_inherit_permissions(self):
self.student_role.add_permission("delete_thread")
self.TA_role = models.Role.objects.create(name="Community TA",
course_id=self.course_id)
self.TA_role.inherit_permissions(self.student_role)
self.assertTrue(self.TA_role.has_permission("delete_thread"))
# TODO: You should not be able to inherit permissions across courses?
def test_inherit_permissions_across_courses(self):
raise SkipTest()
self.student_role.add_permission("delete_thread")
self.course_id_2 = "MITx/6.002x/2012_Fall"
self.admin_role = models.Role.objects.create(name="Administrator",
course_id=self.course_id_2)
self.admin_role.inherit_permissions(self.student_role)
class PermissionClassTestCase(TestCase):
def test_unicode(self):
self.permission = permissions.Permission.objects.create(name="test")
self.assertEqual(str(self.permission), "test")
import string
import random
import collections
import factory
from django.test import TestCase
from django.contrib.auth.models import User
from student.models import UserProfile, CourseEnrollment
from django_comment_client.models import Role, Permission
import django_comment_client.permissions as p
class UserFactory(factory.Factory):
FACTORY_FOR = User
username = 'robot'
password = '123456'
email = 'robot@edx.org'
is_active = True
is_staff = False
class CourseEnrollmentFactory(factory.Factory):
FACTORY_FOR = CourseEnrollment
user = factory.SubFactory(UserFactory)
course_id = 'edX/toy/2012_Fall'
class RoleFactory(factory.Factory):
FACTORY_FOR = Role
name = 'Student'
course_id = 'edX/toy/2012_Fall'
class PermissionFactory(factory.Factory):
FACTORY_FOR = Permission
name = 'create_comment'
class PermissionsTestCase(TestCase):
def setUp(self):
self.course_id = "edX/toy/2012_Fall"
self.student_role = RoleFactory(name='Student')
self.moderator_role = RoleFactory(name='Moderator')
self.student = UserFactory(username='student', email='student@edx.org')
self.moderator = UserFactory(username='moderator', email='staff@edx.org', is_staff=True)
self.update_thread_permission = PermissionFactory(name='update_thread')
self.update_thread_permission.roles.add(self.student_role)
self.update_thread_permission.roles.add(self.moderator_role)
self.manage_moderator_permission = PermissionFactory(name='manage_moderator')
self.manage_moderator_permission.roles.add(self.moderator_role)
self.student_enrollment = CourseEnrollmentFactory(user=self.student)
self.moderator_enrollment = CourseEnrollmentFactory(user=self.moderator)
self.student_open_thread = {'content': {
'closed': False,
'user_id': str(self.student.id)}
}
self.student_closed_thread = {'content': {
'closed': True,
'user_id': str(self.student.id)}
}
def test_user_has_permission(self):
s_ut = p.has_permission(self.student, 'update_thread', self.course_id)
m_ut = p.has_permission(self.moderator, 'update_thread', self.course_id)
s_mm = p.has_permission(self.student, 'manage_moderator', self.course_id)
m_mm = p.has_permission(self.moderator, 'manage_moderator', self.course_id)
self.assertTrue(s_ut)
self.assertTrue(m_ut)
self.assertFalse(s_mm)
self.assertTrue(m_mm)
def test_check_conditions(self):
# Checks whether the discussion thread is open, or whether the author is user
s_o = p.check_condition(self.student, 'is_open', self.course_id, self.student_open_thread)
s_a = p.check_condition(self.student, 'is_author', self.course_id, self.student_open_thread)
m_c = p.check_condition(self.moderator, 'is_open', self.course_id, self.student_closed_thread)
m_a = p.check_condition(self.moderator,'is_author', self.course_id, self.student_open_thread)
self.assertTrue(s_o)
self.assertTrue(s_a)
self.assertFalse(m_c)
self.assertFalse(m_a)
def test_check_conditions_and_permissions(self):
# Check conditions
ret = p.check_conditions_permissions(self.student,
'is_open',
self.course_id,
data=self.student_open_thread)
self.assertTrue(ret)
# Check permissions
ret = p.check_conditions_permissions(self.student,
'update_thread',
self.course_id,
data=self.student_open_thread)
self.assertTrue(ret)
# Check that a list of permissions/conditions will be OR'd
ret = p.check_conditions_permissions(self.moderator,
['is_open','manage_moderator'],
self.course_id,
data=self.student_open_thread)
self.assertTrue(ret)
# Check that a list of permissions will be OR'd
ret = p.check_conditions_permissions(self.student,
['update_thread','manage_moderator'],
self.course_id,
data=self.student_open_thread)
self.assertTrue(ret)
# Check that a list of list of permissions will be AND'd
ret = p.check_conditions_permissions(self.student,
[['update_thread','manage_moderator']],
self.course_id,
data=self.student_open_thread)
self.assertFalse(ret)
def test_check_permissions_by_view(self):
ret = p.check_permissions_by_view(self.student, self.course_id,
self.student_open_thread, 'openclose_thread')
self.assertFalse(ret)
# Check a view permission that includes both a condition and a permission
self.vote_permission = PermissionFactory(name='vote')
self.vote_permission.roles.add(self.student_role)
ret = p.check_permissions_by_view(self.student, self.course_id,
self.student_open_thread, 'vote_for_comment')
self.assertTrue(ret)
\ No newline at end of file
from django.conf import settings from django.conf import settings
from staff_grading_service import StaffGradingService from staff_grading_service import StaffGradingService
from open_ended_grading.controller_query_service import ControllerQueryService from open_ended_grading.controller_query_service import ControllerQueryService
from xmodule import peer_grading_service
import json import json
from student.models import unique_id_for_user from student.models import unique_id_for_user
import open_ended_util import open_ended_util
...@@ -10,6 +11,8 @@ from courseware.access import has_access ...@@ -10,6 +11,8 @@ from courseware.access import has_access
from util.cache import cache from util.cache import cache
import datetime import datetime
from xmodule import peer_grading_service from xmodule import peer_grading_service
from xmodule.x_module import ModuleSystem
from mitxmako.shortcuts import render_to_string
log=logging.getLogger(__name__) log=logging.getLogger(__name__)
...@@ -55,7 +58,8 @@ def staff_grading_notifications(course, user): ...@@ -55,7 +58,8 @@ def staff_grading_notifications(course, user):
return notification_dict return notification_dict
def peer_grading_notifications(course, user): def peer_grading_notifications(course, user):
peer_gs = PeerGradingService(settings.PEER_GRADING_INTERFACE) system = ModuleSystem(None,None,None,render_to_string,None)
peer_gs = peer_grading_service.PeerGradingService(settings.PEER_GRADING_INTERFACE, system)
pending_grading=False pending_grading=False
img_path= "" img_path= ""
course_id = course.id course_id = course.id
......
...@@ -512,7 +512,7 @@ PIPELINE_COMPILERS = [ ...@@ -512,7 +512,7 @@ PIPELINE_COMPILERS = [
'pipeline.compilers.coffee.CoffeeScriptCompiler', 'pipeline.compilers.coffee.CoffeeScriptCompiler',
] ]
PIPELINE_SASS_ARGUMENTS = '-t expanded -r {proj_dir}/static/sass/bourbon/lib/bourbon.rb'.format(proj_dir=PROJECT_ROOT) PIPELINE_SASS_ARGUMENTS = '-t compressed -r {proj_dir}/static/sass/bourbon/lib/bourbon.rb'.format(proj_dir=PROJECT_ROOT)
PIPELINE_CSS_COMPRESSOR = None PIPELINE_CSS_COMPRESSOR = None
PIPELINE_JS_COMPRESSOR = None PIPELINE_JS_COMPRESSOR = None
...@@ -522,7 +522,7 @@ STATICFILES_IGNORE_PATTERNS = ( ...@@ -522,7 +522,7 @@ STATICFILES_IGNORE_PATTERNS = (
"coffee/*", "coffee/*",
) )
# PIPELINE_YUI_BINARY = 'yui-compressor' PIPELINE_YUI_BINARY = 'yui-compressor'
PIPELINE_SASS_BINARY = 'sass' PIPELINE_SASS_BINARY = 'sass'
PIPELINE_COFFEE_SCRIPT_BINARY = 'coffee' PIPELINE_COFFEE_SCRIPT_BINARY = 'coffee'
......
...@@ -32,7 +32,9 @@ ${progress_graph.body(grade_summary, course.grade_cutoffs, "grade-detail-graph", ...@@ -32,7 +32,9 @@ ${progress_graph.body(grade_summary, course.grade_cutoffs, "grade-detail-graph",
<h1>Course Progress</h1> <h1>Course Progress</h1>
</header> </header>
<div id="grade-detail-graph"></div> %if not course.metadata.get('disable_progress_graph',False):
<div id="grade-detail-graph"></div>
%endif
<ol class="chapters"> <ol class="chapters">
%for chapter in courseware_summary: %for chapter in courseware_summary:
......
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