Fix tests post-merge
This commit is contained in:
@@ -44,9 +44,8 @@ from collections import namedtuple
|
||||
|
||||
from courseware.courses import get_courses, sort_by_announcement
|
||||
from courseware.access import has_access
|
||||
from courseware.models import StudentModuleCache
|
||||
from courseware.views import get_module_for_descriptor, jump_to
|
||||
from courseware.module_render import get_instance_module
|
||||
from courseware.model_data import ModelDataCache
|
||||
|
||||
from statsd import statsd
|
||||
|
||||
@@ -1071,14 +1070,14 @@ def accept_name_change(request):
|
||||
|
||||
@csrf_exempt
|
||||
def test_center_login(request):
|
||||
# errors are returned by navigating to the error_url, adding a query parameter named "code"
|
||||
# errors are returned by navigating to the error_url, adding a query parameter named "code"
|
||||
# which contains the error code describing the exceptional condition.
|
||||
def makeErrorURL(error_url, error_code):
|
||||
log.error("generating error URL with error code {}".format(error_code))
|
||||
return "{}?code={}".format(error_url, error_code);
|
||||
|
||||
|
||||
# get provided error URL, which will be used as a known prefix for returning error messages to the
|
||||
# Pearson shell.
|
||||
# Pearson shell.
|
||||
error_url = request.POST.get("errorURL")
|
||||
|
||||
# TODO: check that the parameters have not been tampered with, by comparing the code provided by Pearson
|
||||
@@ -1089,12 +1088,12 @@ def test_center_login(request):
|
||||
|
||||
# calculate SHA for query string
|
||||
# TODO: figure out how to get the original query string, so we can hash it and compare.
|
||||
|
||||
|
||||
|
||||
|
||||
if 'clientCandidateID' not in request.POST:
|
||||
return HttpResponseRedirect(makeErrorURL(error_url, "missingClientCandidateID"));
|
||||
client_candidate_id = request.POST.get("clientCandidateID")
|
||||
|
||||
|
||||
# TODO: check remaining parameters, and maybe at least log if they're not matching
|
||||
# expected values....
|
||||
# registration_id = request.POST.get("registrationID")
|
||||
@@ -1108,12 +1107,12 @@ def test_center_login(request):
|
||||
return HttpResponseRedirect(makeErrorURL(error_url, "invalidClientCandidateID"));
|
||||
|
||||
# find testcenter_registration that matches the provided exam code:
|
||||
# Note that we could rely in future on either the registrationId or the exam code,
|
||||
# or possibly both. But for now we know what to do with an ExamSeriesCode,
|
||||
# Note that we could rely in future on either the registrationId or the exam code,
|
||||
# or possibly both. But for now we know what to do with an ExamSeriesCode,
|
||||
# while we currently have no record of RegistrationID values at all.
|
||||
if 'vueExamSeriesCode' not in request.POST:
|
||||
# we are not allowed to make up a new error code, according to Pearson,
|
||||
# so instead of "missingExamSeriesCode", we use a valid one that is
|
||||
# we are not allowed to make up a new error code, according to Pearson,
|
||||
# so instead of "missingExamSeriesCode", we use a valid one that is
|
||||
# inaccurate but at least distinct. (Sigh.)
|
||||
log.error("missing exam series code for cand ID {}".format(client_candidate_id))
|
||||
return HttpResponseRedirect(makeErrorURL(error_url, "missingPartnerID"));
|
||||
@@ -1127,11 +1126,11 @@ def test_center_login(request):
|
||||
if not registrations:
|
||||
log.error("not able to find exam registration for exam {} and cand ID {}".format(exam_series_code, client_candidate_id))
|
||||
return HttpResponseRedirect(makeErrorURL(error_url, "noTestsAssigned"));
|
||||
|
||||
|
||||
# TODO: figure out what to do if there are more than one registrations....
|
||||
# for now, just take the first...
|
||||
registration = registrations[0]
|
||||
|
||||
|
||||
course_id = registration.course_id
|
||||
course = course_from_id(course_id) # assume it will be found....
|
||||
if not course:
|
||||
@@ -1149,19 +1148,19 @@ def test_center_login(request):
|
||||
if not timelimit_descriptor:
|
||||
log.error("cand {} on exam {} for course {}: descriptor not found for location {}".format(client_candidate_id, exam_series_code, course_id, location))
|
||||
return HttpResponseRedirect(makeErrorURL(error_url, "missingClientProgram"));
|
||||
|
||||
timelimit_module_cache = StudentModuleCache.cache_for_descriptor_descendents(course_id, testcenteruser.user,
|
||||
timelimit_descriptor, depth=None)
|
||||
timelimit_module = get_module_for_descriptor(request.user, request, timelimit_descriptor,
|
||||
|
||||
timelimit_module_cache = ModelDataCache.cache_for_descriptor_descendents(course_id, testcenteruser.user,
|
||||
timelimit_descriptor, depth=None)
|
||||
timelimit_module = get_module_for_descriptor(request.user, request, timelimit_descriptor,
|
||||
timelimit_module_cache, course_id, position=None)
|
||||
if not timelimit_module.category == 'timelimit':
|
||||
log.error("cand {} on exam {} for course {}: non-timelimit module at location {}".format(client_candidate_id, exam_series_code, course_id, location))
|
||||
return HttpResponseRedirect(makeErrorURL(error_url, "missingClientProgram"));
|
||||
|
||||
|
||||
if timelimit_module and timelimit_module.has_ended:
|
||||
log.warning("cand {} on exam {} for course {}: test already over at {}".format(client_candidate_id, exam_series_code, course_id, timelimit_module.ending_at))
|
||||
return HttpResponseRedirect(makeErrorURL(error_url, "allTestsTaken"));
|
||||
|
||||
|
||||
# check if we need to provide an accommodation:
|
||||
time_accommodation_mapping = {'ET12ET' : 'ADDHALFTIME',
|
||||
'ET30MN' : 'ADD30MIN',
|
||||
@@ -1174,27 +1173,24 @@ def test_center_login(request):
|
||||
# special, hard-coded client ID used by Pearson shell for testing:
|
||||
if client_candidate_id == "edX003671291147":
|
||||
time_accommodation_code = 'TESTING'
|
||||
|
||||
|
||||
if time_accommodation_code:
|
||||
timelimit_module.accommodation_code = time_accommodation_code
|
||||
instance_module = get_instance_module(course_id, testcenteruser.user, timelimit_module, timelimit_module_cache)
|
||||
instance_module.state = timelimit_module.get_instance_state()
|
||||
instance_module.save()
|
||||
log.info("cand {} on exam {} for course {}: receiving accommodation {}".format(client_candidate_id, exam_series_code, course_id, time_accommodation_code))
|
||||
|
||||
|
||||
# UGLY HACK!!!
|
||||
# Login assumes that authentication has occurred, and that there is a
|
||||
# Login assumes that authentication has occurred, and that there is a
|
||||
# backend annotation on the user object, indicating which backend
|
||||
# against which the user was authenticated. We're authenticating here
|
||||
# against the registration entry, and assuming that the request given
|
||||
# this information is correct, we allow the user to be logged in
|
||||
# without a password. This could all be formalized in a backend object
|
||||
# that does the above checking.
|
||||
# that does the above checking.
|
||||
# TODO: (brian) create a backend class to do this.
|
||||
# testcenteruser.user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__)
|
||||
testcenteruser.user.backend = "%s.%s" % ("TestcenterAuthenticationModule", "TestcenterAuthenticationClass")
|
||||
# testcenteruser.user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__)
|
||||
testcenteruser.user.backend = "%s.%s" % ("TestcenterAuthenticationModule", "TestcenterAuthenticationClass")
|
||||
login(request, testcenteruser.user)
|
||||
|
||||
|
||||
# And start the test:
|
||||
return jump_to(request, course_id, location)
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ class CombinedOpenEndedDescriptor(XmlDescriptor, EditingDescriptor):
|
||||
}
|
||||
"""
|
||||
|
||||
return {'xml_string' : etree.tostring(xml_object), 'metadata' : xml_object.attrib}
|
||||
return {'xml_string' : etree.tostring(xml_object), 'metadata' : xml_object.attrib}, []
|
||||
|
||||
|
||||
def definition_to_xml(self, resource_fs):
|
||||
|
||||
@@ -696,7 +696,7 @@ class CombinedOpenEndedV1Descriptor(XmlDescriptor, EditingDescriptor):
|
||||
"""Assumes that xml_object has child k"""
|
||||
return xml_object.xpath(k)[0]
|
||||
|
||||
return {'task_xml': parse_task('task'), 'prompt': parse('prompt'), 'rubric': parse('rubric')}
|
||||
return {'task_xml': parse_task('task'), 'prompt': parse('prompt'), 'rubric': parse('rubric')}, []
|
||||
|
||||
|
||||
def definition_to_xml(self, resource_fs):
|
||||
|
||||
@@ -4,6 +4,7 @@ import logging
|
||||
from xmodule.x_module import XModule
|
||||
from xmodule.modulestore import Location
|
||||
from xmodule.seq_module import SequenceDescriptor
|
||||
from xblock.core import String, Scope
|
||||
|
||||
from pkg_resources import resource_string
|
||||
|
||||
@@ -34,6 +35,7 @@ class ConditionalModule(XModule):
|
||||
js_module_name = "Conditional"
|
||||
css = {'scss': [resource_string(__name__, 'css/capa/display.scss')]}
|
||||
|
||||
condition = String(help="Condition for this module", default='', scope=Scope.settings)
|
||||
|
||||
def __init__(self, system, location, definition, descriptor, instance_state=None, shared_state=None, **kwargs):
|
||||
"""
|
||||
@@ -44,7 +46,6 @@ class ConditionalModule(XModule):
|
||||
"""
|
||||
XModule.__init__(self, system, location, definition, descriptor, instance_state, shared_state, **kwargs)
|
||||
self.contents = None
|
||||
self.condition = self.metadata.get('condition', '')
|
||||
self._get_required_modules()
|
||||
children = self.get_display_items()
|
||||
if children:
|
||||
@@ -128,16 +129,18 @@ class ConditionalDescriptor(SequenceDescriptor):
|
||||
stores_state = True
|
||||
has_score = False
|
||||
|
||||
required = String(help="List of required xmodule locations, separated by &", default='', scope=Scope.settings)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ConditionalDescriptor, self).__init__(*args, **kwargs)
|
||||
|
||||
required_module_list = [tuple(x.split('/', 1)) for x in self.metadata.get('required', '').split('&')]
|
||||
required_module_list = [tuple(x.split('/', 1)) for x in self.required.split('&')]
|
||||
self.required_module_locations = []
|
||||
for rm in required_module_list:
|
||||
try:
|
||||
(tag, name) = rm
|
||||
except Exception as err:
|
||||
msg = "Specification of required module in conditional is broken: %s" % self.metadata.get('required')
|
||||
msg = "Specification of required module in conditional is broken: %s" % self.required
|
||||
log.warning(msg)
|
||||
self.system.error_tracker(msg)
|
||||
continue
|
||||
|
||||
@@ -169,6 +169,11 @@ class CourseDescriptor(SequenceDescriptor):
|
||||
computed_default=lambda c: {'General': {'id': c.location.html_id()}},
|
||||
)
|
||||
testcenter_info = Object(help="Dictionary of Test Center info", scope=Scope.settings)
|
||||
announcement = Date(help="Date this course is announced", scope=Scope.settings)
|
||||
cohort_config = Object(help="Dictionary defining cohort configuration", scope=Scope.settings)
|
||||
is_new = Boolean(help="Whether this course should be flagged as new", scope=Scope.settings)
|
||||
no_grade = Boolean(help="True if this course isn't graded", default=False, scope=Scope.settings)
|
||||
disable_progress_graph = Boolean(help="True if this course shouldn't display the progress graph", default=False, scope=Scope.settings)
|
||||
has_children = True
|
||||
|
||||
info_sidebar_name = String(scope=Scope.settings, default='Course Handouts')
|
||||
@@ -409,27 +414,12 @@ class CourseDescriptor(SequenceDescriptor):
|
||||
def lowest_passing_grade(self):
|
||||
return min(self._grading_policy['GRADE_CUTOFFS'].values())
|
||||
|
||||
@property
|
||||
def tabs(self):
|
||||
"""
|
||||
Return the tabs config, as a python object, or None if not specified.
|
||||
"""
|
||||
return self.metadata.get('tabs')
|
||||
|
||||
@tabs.setter
|
||||
def tabs(self, value):
|
||||
self.metadata['tabs'] = value
|
||||
|
||||
@property
|
||||
def show_calculator(self):
|
||||
return self.metadata.get("show_calculator", None) == "Yes"
|
||||
|
||||
@property
|
||||
def is_cohorted(self):
|
||||
"""
|
||||
Return whether the course is cohorted.
|
||||
"""
|
||||
config = self.metadata.get("cohort_config")
|
||||
config = self.cohort_config
|
||||
if config is None:
|
||||
return False
|
||||
|
||||
@@ -440,7 +430,7 @@ class CourseDescriptor(SequenceDescriptor):
|
||||
"""
|
||||
Return list of topic ids defined in course policy.
|
||||
"""
|
||||
topics = self.metadata.get("discussion_topics", {})
|
||||
topics = self.discussion_topics
|
||||
return [d["id"] for d in topics.values()]
|
||||
|
||||
|
||||
@@ -451,7 +441,7 @@ class CourseDescriptor(SequenceDescriptor):
|
||||
the empty set. Note that all inline discussions are automatically
|
||||
cohorted based on the course's is_cohorted setting.
|
||||
"""
|
||||
config = self.metadata.get("cohort_config")
|
||||
config = self.cohort_config
|
||||
if config is None:
|
||||
return set()
|
||||
|
||||
@@ -460,13 +450,13 @@ class CourseDescriptor(SequenceDescriptor):
|
||||
|
||||
|
||||
@property
|
||||
def is_new(self):
|
||||
def is_newish(self):
|
||||
"""
|
||||
Returns if the course has been flagged as new in the metadata. If
|
||||
Returns if the course has been flagged as new. If
|
||||
there is no flag, return a heuristic value considering the
|
||||
announcement and the start dates.
|
||||
"""
|
||||
flag = self.metadata.get('is_new', None)
|
||||
flag = self.is_new
|
||||
if flag is None:
|
||||
# Use a heuristic if the course has not been flagged
|
||||
announcement, start, now = self._sorting_dates()
|
||||
@@ -512,12 +502,10 @@ class CourseDescriptor(SequenceDescriptor):
|
||||
def to_datetime(timestamp):
|
||||
return datetime(*timestamp[:6])
|
||||
|
||||
def get_date(field):
|
||||
timetuple = self._try_parse_time(field)
|
||||
return to_datetime(timetuple) if timetuple else None
|
||||
|
||||
announcement = get_date('announcement')
|
||||
start = get_date('advertised_start') or to_datetime(self.start)
|
||||
announcement = self.announcement
|
||||
if announcement is not None:
|
||||
announcement = to_datetime(announcement)
|
||||
start = self.advertised_start or to_datetime(self.start)
|
||||
now = to_datetime(time.gmtime())
|
||||
|
||||
return announcement, start, now
|
||||
@@ -719,7 +707,7 @@ class CourseDescriptor(SequenceDescriptor):
|
||||
def get_test_center_exam(self, exam_series_code):
|
||||
exams = [exam for exam in self.test_center_exams if exam.exam_series_code == exam_series_code]
|
||||
return exams[0] if len(exams) == 1 else None
|
||||
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
return self.display_name
|
||||
|
||||
@@ -177,7 +177,7 @@ class GraphicalSliderToolDescriptor(MakoModuleDescriptor, XmlDescriptor):
|
||||
return {
|
||||
'render': parse('render'),
|
||||
'configuration': parse('configuration')
|
||||
}
|
||||
}, []
|
||||
|
||||
def definition_to_xml(self, resource_fs):
|
||||
'''Return an xml element representing this definition.'''
|
||||
|
||||
@@ -456,6 +456,9 @@ class XMLModuleStore(ModuleStoreBase):
|
||||
def _load_extra_content(self, system, course_descriptor, category, path, course_dir):
|
||||
|
||||
for filepath in glob.glob(path / '*'):
|
||||
if not os.path.isfile(filepath):
|
||||
continue
|
||||
|
||||
with open(filepath) as f:
|
||||
try:
|
||||
html = f.read().decode('utf-8')
|
||||
|
||||
@@ -199,16 +199,13 @@ def import_from_xml(store, data_dir, course_dirs=None,
|
||||
course_items = []
|
||||
for course_id in module_store.modules.keys():
|
||||
|
||||
course_data_path = None
|
||||
course_location = None
|
||||
|
||||
# Import course modules first, because importing some of the children requires the course to exist
|
||||
for module in module_store.modules[course_id].itervalues():
|
||||
if module.category == 'course':
|
||||
import_course_from_xml(
|
||||
store,
|
||||
static_content_store,
|
||||
course_data_path,
|
||||
data_dir / module.data_dir,
|
||||
module,
|
||||
target_location_namespace,
|
||||
verbose=verbose
|
||||
@@ -220,7 +217,7 @@ def import_from_xml(store, data_dir, course_dirs=None,
|
||||
import_module_from_xml(
|
||||
store,
|
||||
static_content_store,
|
||||
course_data_path,
|
||||
data_dir / module.data_dir,
|
||||
module,
|
||||
target_location_namespace,
|
||||
verbose=verbose
|
||||
|
||||
@@ -20,7 +20,6 @@ import capa.xqueue_interface as xqueue_interface
|
||||
|
||||
from pkg_resources import resource_string
|
||||
|
||||
from .capa_module import only_one, ComplexEncoder
|
||||
from .editing_module import EditingDescriptor
|
||||
from .html_checker import check_html
|
||||
from progress import Progress
|
||||
@@ -656,7 +655,7 @@ class OpenEndedDescriptor(XmlDescriptor, EditingDescriptor):
|
||||
"""Assumes that xml_object has child k"""
|
||||
return xml_object.xpath(k)[0]
|
||||
|
||||
return {'oeparam': parse('openendedparam'), }
|
||||
return {'oeparam': parse('openendedparam')}, []
|
||||
|
||||
|
||||
def definition_to_xml(self, resource_fs):
|
||||
|
||||
@@ -32,6 +32,7 @@ from .stringify import stringify_children
|
||||
from .x_module import XModule
|
||||
from .xml_module import XmlDescriptor
|
||||
from xmodule.modulestore import Location
|
||||
from xblock.core import Scope, Object, Integer, Boolean, String
|
||||
|
||||
from peer_grading_service import peer_grading_service, GradingServiceError
|
||||
|
||||
@@ -56,32 +57,26 @@ class PeerGradingModule(XModule):
|
||||
|
||||
css = {'scss': [resource_string(__name__, 'css/combinedopenended/display.scss')]}
|
||||
|
||||
def __init__(self, system, location, definition, descriptor,
|
||||
instance_state=None, shared_state=None, **kwargs):
|
||||
XModule.__init__(self, system, location, definition, descriptor,
|
||||
instance_state, shared_state, **kwargs)
|
||||
student_data_for_location = Object(scope=Scope.student_state)
|
||||
max_grade = Integer(default=MAX_SCORE, scope=Scope.student_state)
|
||||
use_for_single_location = Boolean(default=USE_FOR_SINGLE_LOCATION, scope=Scope.settings)
|
||||
is_graded = Boolean(default=IS_GRADED, scope=Scope.settings)
|
||||
link_to_location = String(default=LINK_TO_LOCATION, scope=Scope.settings)
|
||||
|
||||
# Load instance state
|
||||
if instance_state is not None:
|
||||
instance_state = json.loads(instance_state)
|
||||
else:
|
||||
instance_state = {}
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PeerGradingModule, self).__init__(*args, **kwargs)
|
||||
|
||||
#We need to set the location here so the child modules can use it
|
||||
system.set('location', location)
|
||||
self.system = system
|
||||
self.system.set('location', self.location)
|
||||
self.peer_gs = peer_grading_service(self.system)
|
||||
|
||||
self.use_for_single_location = self.metadata.get('use_for_single_location', USE_FOR_SINGLE_LOCATION)
|
||||
if isinstance(self.use_for_single_location, basestring):
|
||||
self.use_for_single_location = (self.use_for_single_location in TRUE_DICT)
|
||||
|
||||
self.is_graded = self.metadata.get('is_graded', IS_GRADED)
|
||||
if isinstance(self.is_graded, basestring):
|
||||
self.is_graded = (self.is_graded in TRUE_DICT)
|
||||
|
||||
self.link_to_location = self.metadata.get('link_to_location', USE_FOR_SINGLE_LOCATION)
|
||||
if self.use_for_single_location == True:
|
||||
if self.use_for_single_location:
|
||||
#This will raise an exception if the location is invalid
|
||||
link_to_location_object = Location(self.link_to_location)
|
||||
|
||||
@@ -89,8 +84,6 @@ class PeerGradingModule(XModule):
|
||||
if not self.ajax_url.endswith("/"):
|
||||
self.ajax_url = self.ajax_url + "/"
|
||||
|
||||
self.student_data_for_location = instance_state.get('student_data_for_location', {})
|
||||
self.max_grade = instance_state.get('max_grade', MAX_SCORE)
|
||||
if not isinstance(self.max_grade, (int, long)):
|
||||
#This could result in an exception, but not wrapping in a try catch block so it moves up the stack
|
||||
self.max_grade = int(self.max_grade)
|
||||
@@ -521,7 +514,7 @@ class PeerGradingDescriptor(XmlDescriptor, EditingDescriptor):
|
||||
"""Assumes that xml_object has child k"""
|
||||
return xml_object.xpath(k)[0]
|
||||
|
||||
return {}
|
||||
return {}, []
|
||||
|
||||
|
||||
def definition_to_xml(self, resource_fs):
|
||||
|
||||
@@ -58,10 +58,10 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
|
||||
|
||||
# Used for progress / grading. Currently get credit just for
|
||||
# completion (doesn't matter if you self-assessed correct/incorrect).
|
||||
max_score = Integer(scope=Scope.settings, default=MAX_SCORE)
|
||||
max_score = Integer(scope=Scope.settings, default=openendedchild.MAX_SCORE)
|
||||
max_attempts = Integer(scope=Scope.settings, default=openendedchild.MAX_ATTEMPTS)
|
||||
|
||||
attempts = Integer(scope=Scope.student_state, default=0)
|
||||
max_attempts = Integer(scope=Scope.settings, default=MAX_ATTEMPTS)
|
||||
rubric = String(scope=Scope.content)
|
||||
prompt = String(scope=Scope.content)
|
||||
submitmessage = String(scope=Scope.content)
|
||||
@@ -318,9 +318,9 @@ class SelfAssessmentDescriptor(XmlDescriptor, EditingDescriptor):
|
||||
|
||||
# Used for progress / grading. Currently get credit just for
|
||||
# completion (doesn't matter if you self-assessed correct/incorrect).
|
||||
max_score = Integer(scope=Scope.settings, default=MAX_SCORE)
|
||||
max_score = Integer(scope=Scope.settings, default=openendedchild.MAX_SCORE)
|
||||
|
||||
max_attempts = Integer(scope=Scope.settings, default=MAX_ATTEMPTS)
|
||||
max_attempts = Integer(scope=Scope.settings, default=openendedchild.MAX_ATTEMPTS)
|
||||
rubric = String(scope=Scope.content)
|
||||
prompt = String(scope=Scope.content)
|
||||
submitmessage = String(scope=Scope.content)
|
||||
|
||||
@@ -94,26 +94,26 @@ class IsNewCourseTestCase(unittest.TestCase):
|
||||
|
||||
|
||||
@patch('xmodule.course_module.time.gmtime')
|
||||
def test_is_new(self, gmtime_mock):
|
||||
def test_is_newish(self, gmtime_mock):
|
||||
gmtime_mock.return_value = NOW
|
||||
|
||||
descriptor = self.get_dummy_course(start='2012-12-02T12:00', is_new=True)
|
||||
assert(descriptor.is_new is True)
|
||||
assert(descriptor.is_newish is True)
|
||||
|
||||
descriptor = self.get_dummy_course(start='2013-02-02T12:00', is_new=False)
|
||||
assert(descriptor.is_new is False)
|
||||
|
||||
descriptor = self.get_dummy_course(start='2013-02-02T12:00', is_new=True)
|
||||
assert(descriptor.is_new is True)
|
||||
assert(descriptor.is_newish is True)
|
||||
|
||||
descriptor = self.get_dummy_course(start='2013-01-15T12:00')
|
||||
assert(descriptor.is_new is True)
|
||||
assert(descriptor.is_newish is True)
|
||||
|
||||
descriptor = self.get_dummy_course(start='2013-03-00T12:00')
|
||||
assert(descriptor.is_new is True)
|
||||
assert(descriptor.is_newish is True)
|
||||
|
||||
descriptor = self.get_dummy_course(start='2012-10-15T12:00')
|
||||
assert(descriptor.is_new is False)
|
||||
assert(descriptor.is_newish is False)
|
||||
|
||||
descriptor = self.get_dummy_course(start='2012-12-31T12:00')
|
||||
assert(descriptor.is_new is True)
|
||||
assert(descriptor.is_newish is True)
|
||||
|
||||
@@ -442,7 +442,7 @@ def _adjust_start_date_for_beta_testers(user, descriptor):
|
||||
NOTE: If testing manually, make sure MITX_FEATURES['DISABLE_START_DATES'] = False
|
||||
in envs/dev.py!
|
||||
"""
|
||||
if descriptor.days_early_for_beta is None:
|
||||
if descriptor.lms.days_early_for_beta is None:
|
||||
# bail early if no beta testing is set up
|
||||
return descriptor.lms.start
|
||||
|
||||
@@ -456,12 +456,12 @@ def _adjust_start_date_for_beta_testers(user, descriptor):
|
||||
# (fun fact: datetime(*a_time_struct[:6]) is the beautiful syntax for
|
||||
# converting time_structs into datetimes)
|
||||
start_as_datetime = datetime(*descriptor.lms.start[:6])
|
||||
delta = timedelta(descriptor.days_early_for_beta)
|
||||
delta = timedelta(descriptor.lms.days_early_for_beta)
|
||||
effective = start_as_datetime - delta
|
||||
# ...and back to time_struct
|
||||
return effective.timetuple()
|
||||
|
||||
return descriptor.start
|
||||
return descriptor.lms.start
|
||||
|
||||
|
||||
def _has_instructor_access_to_location(user, location, course_context=None):
|
||||
|
||||
@@ -89,7 +89,7 @@ def course_image_url(course):
|
||||
"""Try to look up the image url for the course. If it's not found,
|
||||
log an error and return the dead link"""
|
||||
if isinstance(modulestore(), XMLModuleStore):
|
||||
return '/static/' + course.metadata['data_dir'] + "/images/course_image.jpg"
|
||||
return '/static/' + course.data_dir + "/images/course_image.jpg"
|
||||
else:
|
||||
loc = course.location._replace(tag='c4x', category='asset', name='images_course_image.jpg')
|
||||
path = StaticContent.get_url_path_from_location(loc)
|
||||
|
||||
@@ -145,7 +145,7 @@ def get_module(user, request, location, model_data_cache, course_id,
|
||||
location = Location(location)
|
||||
descriptor = modulestore().get_instance(course_id, location, depth=depth)
|
||||
return get_module_for_descriptor(user, request, descriptor, model_data_cache, course_id,
|
||||
position=position, not_found_ok=not_found_ok,
|
||||
position=position,
|
||||
wrap_xmodule_display=wrap_xmodule_display,
|
||||
grade_bucket_type=grade_bucket_type)
|
||||
except ItemNotFoundError:
|
||||
@@ -205,14 +205,14 @@ def get_module_for_descriptor(user, request, descriptor, model_data_cache, cours
|
||||
Delegate to get_module. It does an access check, so may return None
|
||||
"""
|
||||
return get_module_for_descriptor(user, request, descriptor,
|
||||
student_module_cache, course_id, position)
|
||||
model_data_cache, course_id, position)
|
||||
|
||||
def xblock_model_data(descriptor):
|
||||
return DbModel(
|
||||
LmsKeyValueStore(descriptor._model_data, model_data_cache),
|
||||
descriptor.module_class,
|
||||
user.id,
|
||||
LmsUsage(location, location)
|
||||
LmsUsage(descriptor.location, descriptor.location)
|
||||
)
|
||||
|
||||
def publish(event):
|
||||
@@ -260,7 +260,7 @@ def get_module_for_descriptor(user, request, descriptor, model_data_cache, cours
|
||||
# by the replace_static_urls code below
|
||||
replace_urls=partial(
|
||||
static_replace.replace_static_urls,
|
||||
data_directory=descriptor.metadata.get('data_dir', ''),
|
||||
data_directory=descriptor.data_dir,
|
||||
course_namespace=descriptor.location._replace(category=None, name=None),
|
||||
),
|
||||
node_path=settings.NODE_PATH,
|
||||
@@ -282,7 +282,7 @@ def get_module_for_descriptor(user, request, descriptor, model_data_cache, cours
|
||||
log.exception("Error creating module from descriptor {0}".format(descriptor))
|
||||
|
||||
# make an ErrorDescriptor -- assuming that the descriptor's system is ok
|
||||
if has_access(user, location, 'staff', course_id):
|
||||
if has_access(user, descriptor.location, 'staff', course_id):
|
||||
err_descriptor_class = ErrorDescriptor
|
||||
else:
|
||||
err_descriptor_class = NonStaffErrorDescriptor
|
||||
|
||||
@@ -28,6 +28,7 @@ from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.xml import XMLModuleStore
|
||||
from xmodule.x_module import XModule
|
||||
from student.models import unique_id_for_user
|
||||
from courseware.model_data import ModelDataCache
|
||||
|
||||
from open_ended_grading import open_ended_notifications
|
||||
|
||||
@@ -321,10 +322,12 @@ def get_static_tab_by_slug(course, tab_slug):
|
||||
return None
|
||||
|
||||
|
||||
def get_static_tab_contents(request, cache, course, tab):
|
||||
def get_static_tab_contents(request, course, tab):
|
||||
|
||||
loc = Location(course.location.tag, course.location.org, course.location.course, 'static_tab', tab['url_slug'])
|
||||
tab_module = get_module(request.user, request, loc, cache, course.id)
|
||||
model_data_cache = ModelDataCache.cache_for_descriptor_descendents(course.id,
|
||||
request.user, modulestore().get_instance(course.id, loc), depth=0)
|
||||
tab_module = get_module(request.user, request, loc, model_data_cache, course.id)
|
||||
|
||||
logging.debug('course_module = {0}'.format(tab_module))
|
||||
|
||||
|
||||
@@ -742,11 +742,11 @@ class TestViewAuth(PageLoader):
|
||||
yesterday = time.time() - 24 * 3600
|
||||
|
||||
# toy course's hasn't started
|
||||
self.toy.metadata['start'] = stringify_time(time.gmtime(tomorrow))
|
||||
self.toy.lms.start = time.gmtime(tomorrow)
|
||||
self.assertFalse(self.toy.has_started())
|
||||
|
||||
# but should be accessible for beta testers
|
||||
self.toy.days_early_for_beta = 2
|
||||
self.toy.lms.days_early_for_beta = 2
|
||||
|
||||
# student user shouldn't see it
|
||||
student_user = user(self.student)
|
||||
|
||||
@@ -443,7 +443,11 @@ def static_tab(request, course_id, tab_slug):
|
||||
if tab is None:
|
||||
raise Http404
|
||||
|
||||
contents = tabs.get_static_tab_contents(request, None, course, tab)
|
||||
contents = tabs.get_static_tab_contents(
|
||||
request,
|
||||
course,
|
||||
tab
|
||||
)
|
||||
if contents is None:
|
||||
raise Http404
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class ControllerQueryService(GradingService):
|
||||
Interface to staff grading backend.
|
||||
"""
|
||||
def __init__(self, config):
|
||||
config['system'] = ModuleSystem(None, None, None, render_to_string, None)
|
||||
config['system'] = ModuleSystem(None, None, None, render_to_string, None, None)
|
||||
super(ControllerQueryService, self).__init__(config)
|
||||
self.check_eta_url = self.url + '/get_submission_eta/'
|
||||
self.is_unique_url = self.url + '/is_name_unique/'
|
||||
|
||||
@@ -143,10 +143,10 @@ class TestPeerGradingService(ct.PageLoader):
|
||||
location = "i4x://edX/toy/peergrading/init"
|
||||
|
||||
self.mock_service = peer_grading_service.MockPeerGradingService()
|
||||
self.system = ModuleSystem(location, None, None, render_to_string, None)
|
||||
self.descriptor = peer_grading_module.PeerGradingDescriptor(self.system)
|
||||
self.system = ModuleSystem(location, None, None, render_to_string, None, None)
|
||||
self.descriptor = peer_grading_module.PeerGradingDescriptor(self.system, location, {})
|
||||
|
||||
self.peer_module = peer_grading_module.PeerGradingModule(self.system, location, "<peergrading/>", self.descriptor)
|
||||
self.peer_module = peer_grading_module.PeerGradingModule(self.system, location, self.descriptor, {})
|
||||
self.peer_module.peer_gs = self.mock_service
|
||||
self.logout()
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%>
|
||||
<%page args="course" />
|
||||
<article id="${course.id}" class="course">
|
||||
%if course.is_new:
|
||||
%if course.is_newish:
|
||||
<span class="status">New</span>
|
||||
%endif
|
||||
<a href="${reverse('about_course', args=[course.id])}">
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<script type="text/javascript" src="${static.url('js/vendor/flot/jquery.flot.stack.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/vendor/flot/jquery.flot.symbol.js')}"></script>
|
||||
<script>
|
||||
${progress_graph.body(grade_summary, course.grade_cutoffs, "grade-detail-graph", not course.metadata.get("no_grade", False), not course.metadata.get("no_grade", False))}
|
||||
${progress_graph.body(grade_summary, course.grade_cutoffs, "grade-detail-graph", not course.no_grade, not course.no_grade)}
|
||||
</script>
|
||||
</%block>
|
||||
|
||||
@@ -32,7 +32,7 @@ ${progress_graph.body(grade_summary, course.grade_cutoffs, "grade-detail-graph",
|
||||
<h1>Course Progress</h1>
|
||||
</header>
|
||||
|
||||
%if not course.metadata.get('disable_progress_graph',False):
|
||||
%if not course.disable_progress_graph:
|
||||
<div id="grade-detail-graph"></div>
|
||||
%endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user