Integration of edx_proctoring into the LMS
This commit is contained in:
@@ -28,7 +28,7 @@ def register_proctored_exams(course_key):
|
||||
This is typically called on a course published signal. The course is examined for sequences
|
||||
that are marked as timed exams. Then these are registered with the edx-proctoring
|
||||
subsystem. Likewise, if formerly registered exams are unmarked, then those
|
||||
registred exams are marked as inactive
|
||||
registered exams are marked as inactive
|
||||
"""
|
||||
|
||||
if not settings.FEATURES.get('ENABLE_PROCTORED_EXAMS'):
|
||||
@@ -76,6 +76,7 @@ def register_proctored_exams(course_key):
|
||||
exam_name=timed_exam.display_name,
|
||||
time_limit_mins=timed_exam.default_time_limit_minutes,
|
||||
is_proctored=timed_exam.is_proctored_enabled,
|
||||
is_practice_exam=timed_exam.is_practice_exam,
|
||||
is_active=True
|
||||
)
|
||||
msg = 'Updated timed exam {exam_id}'.format(exam_id=exam['id'])
|
||||
@@ -87,6 +88,7 @@ def register_proctored_exams(course_key):
|
||||
exam_name=timed_exam.display_name,
|
||||
time_limit_mins=timed_exam.default_time_limit_minutes,
|
||||
is_proctored=timed_exam.is_proctored_enabled,
|
||||
is_practice_exam=timed_exam.is_practice_exam,
|
||||
is_active=True
|
||||
)
|
||||
msg = 'Created new timed exam {exam_id}'.format(exam_id=exam_id)
|
||||
|
||||
@@ -862,7 +862,8 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
|
||||
xblock_info.update({
|
||||
"is_proctored_enabled": xblock.is_proctored_enabled,
|
||||
"is_time_limited": xblock.is_time_limited,
|
||||
"default_time_limit_minutes": xblock.default_time_limit_minutes
|
||||
"default_time_limit_minutes": xblock.default_time_limit_minutes,
|
||||
"is_practice_exam": xblock.is_practice_exam
|
||||
})
|
||||
|
||||
# Entrance exam subsection should be hidden. in_entrance_exam is inherited metadata, all children will have it.
|
||||
|
||||
@@ -352,7 +352,6 @@ XBLOCK_SETTINGS = ENV_TOKENS.get('XBLOCK_SETTINGS', {})
|
||||
XBLOCK_SETTINGS.setdefault("VideoDescriptor", {})["licensing_enabled"] = FEATURES.get("LICENSING", False)
|
||||
XBLOCK_SETTINGS.setdefault("VideoModule", {})['YOUTUBE_API_KEY'] = AUTH_TOKENS.get('YOUTUBE_API_KEY', YOUTUBE_API_KEY)
|
||||
|
||||
|
||||
################# PROCTORING CONFIGURATION ##################
|
||||
|
||||
PROCTORING_BACKEND_PROVIDER = AUTH_TOKENS.get("PROCTORING_BACKEND_PROVIDER", PROCTORING_BACKEND_PROVIDER)
|
||||
|
||||
@@ -77,7 +77,8 @@
|
||||
"SUBDOMAIN_BRANDING": false,
|
||||
"SUBDOMAIN_COURSE_LISTINGS": false,
|
||||
"ALLOW_ALL_ADVANCED_COMPONENTS": true,
|
||||
"ENABLE_CONTENT_LIBRARIES": true
|
||||
"ENABLE_CONTENT_LIBRARIES": true,
|
||||
"ENABLE_PROCTORED_EXAMS": true
|
||||
},
|
||||
"FEEDBACK_SUBMISSION_EMAIL": "",
|
||||
"GITHUB_REPO_ROOT": "** OVERRIDDEN **",
|
||||
|
||||
@@ -101,6 +101,8 @@ FEATURES['ENABLE_VIDEO_BUMPER'] = True # Enable video bumper in Studio settings
|
||||
########################### Entrance Exams #################################
|
||||
FEATURES['ENTRANCE_EXAMS'] = True
|
||||
|
||||
FEATURES['ENABLE_PROCTORED_EXAMS'] = True
|
||||
|
||||
# Point the URL used to test YouTube availability to our stub YouTube server
|
||||
YOUTUBE_PORT = 9080
|
||||
YOUTUBE['API'] = "http://127.0.0.1:{0}/get_youtube_api/".format(YOUTUBE_PORT)
|
||||
|
||||
@@ -173,6 +173,9 @@ FEATURES = {
|
||||
# Show video bumper in Studio
|
||||
'ENABLE_VIDEO_BUMPER': False,
|
||||
|
||||
# Timed Proctored Exams
|
||||
'ENABLE_PROCTORED_EXAMS': False,
|
||||
|
||||
# How many seconds to show the bumper again, default is 7 days:
|
||||
'SHOW_BUMPER_PERIODICITY': 7 * 24 * 3600,
|
||||
|
||||
@@ -775,6 +778,9 @@ INSTALLED_APPS = (
|
||||
'openedx.core.djangoapps.credit',
|
||||
|
||||
'xblock_django',
|
||||
|
||||
# edX Proctoring
|
||||
'edx_proctoring',
|
||||
)
|
||||
|
||||
|
||||
@@ -1040,11 +1046,10 @@ CREDIT_PROVIDER_TIMESTAMP_EXPIRATION = 15 * 60
|
||||
|
||||
DEPRECATED_BLOCK_TYPES = ['peergrading', 'combinedopenended']
|
||||
|
||||
|
||||
#### PROCTORING CONFIGURATION DEFAULTS
|
||||
|
||||
PROCTORING_BACKEND_PROVIDER = {
|
||||
'class': 'edx_proctoring.backends.NullBackendProvider',
|
||||
'class': 'edx_proctoring.backends.null.NullBackendProvider',
|
||||
'options': {},
|
||||
}
|
||||
PROCTORING_SETTINGS = {}
|
||||
|
||||
@@ -620,6 +620,7 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "js/views/utils/view_ut
|
||||
has_explicit_staff_lock: true,
|
||||
staff_only_message: true,
|
||||
"is_time_limited": true,
|
||||
"is_practice_exam": false,
|
||||
"is_proctored_enabled": true,
|
||||
"default_time_limit_minutes": 150
|
||||
}, [
|
||||
@@ -706,6 +707,7 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "js/views/utils/view_ut
|
||||
"start":"2014-07-09T00:00:00.000Z",
|
||||
"due":"2014-07-10T00:00:00.000Z",
|
||||
"is_time_limited": true,
|
||||
"is_practice_exam": false,
|
||||
"is_proctored_enabled": true,
|
||||
"default_time_limit_minutes": 150
|
||||
}
|
||||
@@ -740,6 +742,7 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "js/views/utils/view_ut
|
||||
expect($("#staff_lock").is(":checked")).toBe(true);
|
||||
expect($("#id_timed_examination").is(":checked")).toBe(true);
|
||||
expect($("#id_exam_proctoring").is(":checked")).toBe(true);
|
||||
expect($("#is_practice_exam").is(":checked")).toBe(false);
|
||||
expect($("#id_time_limit").val()).toBe("02:30");
|
||||
});
|
||||
|
||||
|
||||
@@ -275,11 +275,17 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
event.preventDefault();
|
||||
if (!$(event.currentTarget).is(':checked')) {
|
||||
this.$('#id_exam_proctoring').attr('checked', false);
|
||||
this.$('#id_time_limit').val('00:30');
|
||||
this.$('#id_time_limit').val('00:00');
|
||||
this.$('#id_exam_proctoring').attr('disabled','disabled');
|
||||
this.$('#id_time_limit').attr('disabled', 'disabled');
|
||||
this.$('#id_practice_exam').attr('checked', false);
|
||||
this.$('#id_practice_exam').attr('disabled','disabled');
|
||||
}
|
||||
else {
|
||||
if (!this.isValidTimeLimit(this.$('#id_time_limit').val())) {
|
||||
this.$('#id_time_limit').val('00:30');
|
||||
}
|
||||
this.$('#id_practice_exam').removeAttr('disabled');
|
||||
this.$('#id_exam_proctoring').removeAttr('disabled');
|
||||
this.$('#id_time_limit').removeAttr('disabled');
|
||||
}
|
||||
@@ -289,11 +295,17 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
AbstractEditor.prototype.afterRender.call(this);
|
||||
this.$('input.time').timepicker({
|
||||
'timeFormat' : 'H:i',
|
||||
'minTime': '00:30',
|
||||
'maxTime': '05:00',
|
||||
'forceRoundTime': false
|
||||
});
|
||||
this.setExamTime(this.model.get('default_time_limit_minutes'));
|
||||
this.setExamTmePreference(this.model.get('is_time_limited'));
|
||||
this.setExamProctoring(this.model.get('is_proctored_enabled'));
|
||||
this.setPracticeExam(this.model.get('is_practice_exam'));
|
||||
},
|
||||
setPracticeExam: function(value) {
|
||||
this.$('#id_practice_exam').prop('checked', value);
|
||||
},
|
||||
setExamProctoring: function(value) {
|
||||
this.$('#id_exam_proctoring').prop('checked', value);
|
||||
@@ -307,14 +319,18 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
if (!this.$('#id_timed_examination').is(':checked')) {
|
||||
this.$('#id_exam_proctoring').attr('disabled','disabled');
|
||||
this.$('#id_time_limit').attr('disabled', 'disabled');
|
||||
this.$('#id_practice_exam').attr('disabled', 'disabled');
|
||||
}
|
||||
},
|
||||
isExamTimeEnabled: function () {
|
||||
return this.$('#id_timed_examination').is(':checked');
|
||||
},
|
||||
isPracticeExam: function () {
|
||||
return this.$('#id_practice_exam').is(':checked');
|
||||
},
|
||||
isValidTimeLimit: function(time_limit) {
|
||||
var pattern = new RegExp('^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$');
|
||||
return pattern.test(time_limit);
|
||||
return pattern.test(time_limit) && time_limit !== "00:00";
|
||||
},
|
||||
getExamTimeLimit: function () {
|
||||
return this.$('#id_time_limit').val();
|
||||
@@ -338,6 +354,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
var time_limit = this.getExamTimeLimit();
|
||||
return {
|
||||
metadata: {
|
||||
'is_practice_exam': this.isPracticeExam(),
|
||||
'is_time_limited': this.isExamTimeEnabled(),
|
||||
'is_proctored_enabled': this.isExamProctoringEnabled(),
|
||||
'default_time_limit_minutes': this.convertTimeLimitToMinutes(time_limit)
|
||||
|
||||
@@ -24,6 +24,17 @@
|
||||
<p class='field-message'><%- gettext('Students see warnings when 20% and 5% of the allotted time remains. In certain cases, students can be granted allowances that give them extra time to complete the exam.') %></p>
|
||||
</ul>
|
||||
</div>
|
||||
<ul class="list-fields list-input">
|
||||
<li class="field field-checkbox checkbox-cosmetic">
|
||||
<input type="checkbox" id="id_practice_exam" name="practice_exam" class="input input-checkbox" />
|
||||
<label for="id_practice_exam" class="label">
|
||||
<i class="icon fa fa-check-square-o input-checkbox-checked"></i>
|
||||
<i class="icon fa fa-square-o input-checkbox-unchecked"></i>
|
||||
<%- gettext('This exam is for practice only') %>
|
||||
</label>
|
||||
</li>
|
||||
<p class='field-message'> <%- gettext('Learners can experience the proctoring software setup process and try some example problems. Make sure this practice exam is set up as an ungraded exam.') %> </p>
|
||||
</ul>
|
||||
<ul class="list-fields list-input">
|
||||
<li class="field field-checkbox checkbox-cosmetic">
|
||||
<input type="checkbox" id="id_exam_proctoring" name="exam_proctoring" class="input input-checkbox" />
|
||||
|
||||
Reference in New Issue
Block a user