Merge pull request #12165 from edx/efischer/hide_timed_exams
TNL-4366 Hide Timed Exams
This commit is contained in:
@@ -83,7 +83,8 @@ def register_special_exams(course_key):
|
||||
due_date=timed_exam.due,
|
||||
is_proctored=timed_exam.is_proctored_exam,
|
||||
is_practice_exam=timed_exam.is_practice_exam,
|
||||
is_active=True
|
||||
is_active=True,
|
||||
hide_after_due=timed_exam.hide_after_due,
|
||||
)
|
||||
msg = 'Updated timed exam {exam_id}'.format(exam_id=exam['id'])
|
||||
log.info(msg)
|
||||
@@ -97,7 +98,8 @@ def register_special_exams(course_key):
|
||||
due_date=timed_exam.due,
|
||||
is_proctored=timed_exam.is_proctored_exam,
|
||||
is_practice_exam=timed_exam.is_practice_exam,
|
||||
is_active=True
|
||||
is_active=True,
|
||||
hide_after_due=timed_exam.hide_after_due,
|
||||
)
|
||||
msg = 'Created new timed exam {exam_id}'.format(exam_id=exam_id)
|
||||
log.info(msg)
|
||||
|
||||
@@ -53,6 +53,10 @@ class TestProctoredExams(ModuleStoreTestCase):
|
||||
exam_review_policy = get_review_policy_by_exam_id(exam['id'])
|
||||
self.assertEqual(exam_review_policy['review_policy'], sequence.exam_review_rules)
|
||||
|
||||
if not exam['is_proctored'] and not exam['is_practice_exam']:
|
||||
# the hide after due value only applies to timed exams
|
||||
self.assertEqual(exam['hide_after_due'], sequence.hide_after_due)
|
||||
|
||||
self.assertEqual(exam['course_id'], unicode(self.course.id))
|
||||
self.assertEqual(exam['content_id'], unicode(sequence.location))
|
||||
self.assertEqual(exam['exam_name'], sequence.display_name)
|
||||
@@ -62,13 +66,14 @@ class TestProctoredExams(ModuleStoreTestCase):
|
||||
self.assertEqual(exam['is_active'], expected_active)
|
||||
|
||||
@ddt.data(
|
||||
(True, 10, True, False, True, False),
|
||||
(True, 10, False, False, True, False),
|
||||
(True, 10, True, True, True, True),
|
||||
(True, 10, True, False, True, False, False),
|
||||
(True, 10, False, False, True, False, False),
|
||||
(True, 10, False, False, True, False, True),
|
||||
(True, 10, True, True, True, True, False),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_publishing_exam(self, is_time_limited, default_time_limit_minutes,
|
||||
is_proctored_exam, is_practice_exam, expected_active, republish):
|
||||
def test_publishing_exam(self, is_time_limited, default_time_limit_minutes, is_proctored_exam,
|
||||
is_practice_exam, expected_active, republish, hide_after_due):
|
||||
"""
|
||||
Happy path testing to see that when a course is published which contains
|
||||
a proctored exam, it will also put an entry into the exam tables
|
||||
@@ -85,7 +90,8 @@ class TestProctoredExams(ModuleStoreTestCase):
|
||||
is_proctored_exam=is_proctored_exam,
|
||||
is_practice_exam=is_practice_exam,
|
||||
due=datetime.now(UTC) + timedelta(minutes=default_time_limit_minutes + 1),
|
||||
exam_review_rules="allow_use_of_paper"
|
||||
exam_review_rules="allow_use_of_paper",
|
||||
hide_after_due=hide_after_due,
|
||||
)
|
||||
|
||||
listen_for_course_publish(self, self.course.id)
|
||||
@@ -117,7 +123,8 @@ class TestProctoredExams(ModuleStoreTestCase):
|
||||
graded=True,
|
||||
is_time_limited=True,
|
||||
default_time_limit_minutes=10,
|
||||
is_proctored_exam=True
|
||||
is_proctored_exam=True,
|
||||
hide_after_due=False,
|
||||
)
|
||||
|
||||
listen_for_course_publish(self, self.course.id)
|
||||
@@ -147,7 +154,8 @@ class TestProctoredExams(ModuleStoreTestCase):
|
||||
graded=True,
|
||||
is_time_limited=True,
|
||||
default_time_limit_minutes=10,
|
||||
is_proctored_exam=True
|
||||
is_proctored_exam=True,
|
||||
hide_after_due=False,
|
||||
)
|
||||
|
||||
listen_for_course_publish(self, self.course.id)
|
||||
@@ -182,7 +190,8 @@ class TestProctoredExams(ModuleStoreTestCase):
|
||||
graded=True,
|
||||
is_time_limited=True,
|
||||
default_time_limit_minutes=10,
|
||||
is_proctored_exam=True
|
||||
is_proctored_exam=True,
|
||||
hide_after_due=False,
|
||||
)
|
||||
|
||||
listen_for_course_publish(self, self.course.id)
|
||||
@@ -218,7 +227,8 @@ class TestProctoredExams(ModuleStoreTestCase):
|
||||
is_time_limited=True,
|
||||
default_time_limit_minutes=10,
|
||||
is_proctored_exam=True,
|
||||
exam_review_rules="allow_use_of_paper"
|
||||
exam_review_rules="allow_use_of_paper",
|
||||
hide_after_due=False,
|
||||
)
|
||||
|
||||
listen_for_course_publish(self, self.course.id)
|
||||
|
||||
@@ -935,7 +935,8 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
|
||||
"is_practice_exam": xblock.is_practice_exam,
|
||||
"is_time_limited": xblock.is_time_limited,
|
||||
"exam_review_rules": xblock.exam_review_rules,
|
||||
"default_time_limit_minutes": xblock.default_time_limit_minutes
|
||||
"default_time_limit_minutes": xblock.default_time_limit_minutes,
|
||||
"hide_after_due": xblock.hide_after_due,
|
||||
})
|
||||
|
||||
# Update with gating info
|
||||
|
||||
@@ -50,6 +50,7 @@ class CourseMetadata(object):
|
||||
'is_time_limited',
|
||||
'is_practice_exam',
|
||||
'exam_review_rules',
|
||||
'hide_after_due',
|
||||
'self_paced',
|
||||
'chrome',
|
||||
'default_tab',
|
||||
|
||||
@@ -598,7 +598,7 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
|
||||
var getDisplayNameWrapper, setEditModalValues, mockServerValuesJson,
|
||||
selectDisableSpecialExams, selectBasicSettings, selectAdvancedSettings,
|
||||
selectAccessSettings, selectTimedExam, selectProctoredExam, selectPracticeExam,
|
||||
selectPrerequisite, selectLastPrerequisiteSubsection;
|
||||
selectPrerequisite, selectLastPrerequisiteSubsection, checkOptionFieldVisibility;
|
||||
|
||||
getDisplayNameWrapper = function() {
|
||||
return getItemHeaders('subsection').find('.wrapper-xblock-field');
|
||||
@@ -612,7 +612,7 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
|
||||
};
|
||||
|
||||
selectDisableSpecialExams = function() {
|
||||
this.$("#id_not_timed").prop('checked', true).trigger('change');
|
||||
this.$("input.no_special_exam").prop('checked', true).trigger('change');
|
||||
};
|
||||
|
||||
selectBasicSettings = function() {
|
||||
@@ -627,22 +627,23 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
|
||||
this.$(".modal-section .settings-tab-button[data-tab='access']").click();
|
||||
};
|
||||
|
||||
selectTimedExam = function(time_limit) {
|
||||
this.$("#id_timed_exam").prop('checked', true).trigger('change');
|
||||
this.$("#id_time_limit").val(time_limit);
|
||||
this.$("#id_time_limit").trigger('focusout');
|
||||
selectTimedExam = function(time_limit, hide_after_due) {
|
||||
this.$("input.timed_exam").prop('checked', true).trigger('change');
|
||||
this.$(".field-time-limit input").val(time_limit);
|
||||
this.$(".field-time-limit input").trigger('focusout');
|
||||
this.$('.field-hide-after-due input').prop('checked', hide_after_due).trigger('change');
|
||||
};
|
||||
|
||||
selectProctoredExam = function(time_limit) {
|
||||
this.$("#id_proctored_exam").prop('checked', true).trigger('change');
|
||||
this.$("#id_time_limit").val(time_limit);
|
||||
this.$("#id_time_limit").trigger('focusout');
|
||||
this.$("input.proctored_exam").prop('checked', true).trigger('change');
|
||||
this.$(".field-time-limit input").val(time_limit);
|
||||
this.$(".field-time-limit input").trigger('focusout');
|
||||
};
|
||||
|
||||
selectPracticeExam = function(time_limit) {
|
||||
this.$("#id_practice_exam").prop('checked', true).trigger('change');
|
||||
this.$("#id_time_limit").val(time_limit);
|
||||
this.$("#id_time_limit").trigger('focusout');
|
||||
this.$("input.practice_exam").prop('checked', true).trigger('change');
|
||||
this.$(".field-time-limit input").val(time_limit);
|
||||
this.$(".field-time-limit input").trigger('focusout');
|
||||
};
|
||||
|
||||
selectPrerequisite = function() {
|
||||
@@ -654,6 +655,13 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
|
||||
this.$("#prereq_min_score").val(minScore).trigger('keyup');
|
||||
};
|
||||
|
||||
// Helper to validate oft-checked additional option fields' visibility
|
||||
checkOptionFieldVisibility = function(time_limit, review_rules, hide_after_due) {
|
||||
expect($('.field-time-limit').is(':visible')).toBe(time_limit);
|
||||
expect($('.field-exam-review-rules').is(':visible')).toBe(review_rules);
|
||||
expect($('.field-hide-after-due').is(':visible')).toBe(hide_after_due);
|
||||
};
|
||||
|
||||
// Contains hard-coded dates because dates are presented in different formats.
|
||||
mockServerValuesJson = createMockSectionJSON({
|
||||
release_date: 'Jan 01, 2970 at 05:00 UTC'
|
||||
@@ -670,8 +678,9 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
|
||||
is_prereq: false,
|
||||
"is_time_limited": true,
|
||||
"is_practice_exam": false,
|
||||
"is_proctored_exam": true,
|
||||
"default_time_limit_minutes": 150
|
||||
"is_proctored_exam": false,
|
||||
"default_time_limit_minutes": 150,
|
||||
"hide_after_due": true,
|
||||
}, [
|
||||
createMockVerticalJSON({
|
||||
has_changes: true,
|
||||
@@ -815,13 +824,13 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
|
||||
for (i = 0; i < valid_times.length; i++){
|
||||
time_limit = valid_times[i];
|
||||
selectTimedExam(time_limit);
|
||||
expect($("#id_time_limit").val()).toEqual(time_limit);
|
||||
expect($(".field-time-limit input").val()).toEqual(time_limit);
|
||||
}
|
||||
for (i = 0; i < invalid_times.length; i++){
|
||||
time_limit = invalid_times[i];
|
||||
selectTimedExam(time_limit);
|
||||
expect($("#id_time_limit").val()).not.toEqual(time_limit);
|
||||
expect($("#id_time_limit").val()).toEqual(default_time);
|
||||
expect($(".field-time-limit input").val()).not.toEqual(time_limit);
|
||||
expect($(".field-time-limit input").val()).toEqual(default_time);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -829,7 +838,8 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
|
||||
createCourseOutlinePage(this, mockCourseJSON, false);
|
||||
outlinePage.$('.outline-subsection .configure-button').click();
|
||||
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
|
||||
selectProctoredExam("02:30");
|
||||
selectAdvancedSettings();
|
||||
selectTimedExam("02:30", true);
|
||||
$(".wrapper-modal-window .action-save").click();
|
||||
AjaxHelpers.expectJsonRequest(requests, 'POST', '/xblock/mock-subsection', {
|
||||
"graderType":"Lab",
|
||||
@@ -842,8 +852,9 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
|
||||
"exam_review_rules": "",
|
||||
"is_time_limited": true,
|
||||
"is_practice_exam": false,
|
||||
"is_proctored_enabled": true,
|
||||
"default_time_limit_minutes": 150
|
||||
"is_proctored_enabled": false,
|
||||
"default_time_limit_minutes": 150,
|
||||
"hide_after_due": true,
|
||||
}
|
||||
});
|
||||
expect(requests[0].requestHeaders['X-HTTP-Method-Override']).toBe('PATCH');
|
||||
@@ -872,30 +883,35 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
|
||||
expect($("#due_date").val()).toBe('7/10/2014');
|
||||
expect($("#grading_type").val()).toBe('Lab');
|
||||
expect($("#staff_lock").is(":checked")).toBe(true);
|
||||
expect($("#id_timed_exam").is(":checked")).toBe(false);
|
||||
expect($("#id_proctored_exam").is(":checked")).toBe(true);
|
||||
expect($("#id_not_timed").is(":checked")).toBe(false);
|
||||
expect($("#id_practice_exam").is(":checked")).toBe(false);
|
||||
expect($("#id_time_limit").val()).toBe("02:30");
|
||||
expect($("input.timed_exam").is(":checked")).toBe(true);
|
||||
expect($("input.proctored_exam").is(":checked")).toBe(false);
|
||||
expect($("input.no_special_exam").is(":checked")).toBe(false);
|
||||
expect($("input.practice_exam").is(":checked")).toBe(false);
|
||||
expect($(".field-time-limit input").val()).toBe("02:30");
|
||||
expect($(".field-hide-after-due input").is(":checked")).toBe(true);
|
||||
});
|
||||
|
||||
it('can hide the time limit field when the None radio box is selected', function() {
|
||||
it('can hide time limit and hide after due fields when the None radio box is selected', function() {
|
||||
createCourseOutlinePage(this, mockCourseJSON, false);
|
||||
outlinePage.$('.outline-subsection .configure-button').click();
|
||||
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
|
||||
selectAdvancedSettings();
|
||||
selectDisableSpecialExams();
|
||||
|
||||
// id_time_limit_div should be hidden when None is specified
|
||||
expect($('#id_time_limit_div')).toHaveClass('is-hidden');
|
||||
// all additional options should be hidden
|
||||
expect($('.exam-options').is(':hidden')).toBe(true);
|
||||
});
|
||||
|
||||
it('can select the practice exam', function() {
|
||||
createCourseOutlinePage(this, mockCourseJSON, false);
|
||||
outlinePage.$('.outline-subsection .configure-button').click();
|
||||
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
|
||||
selectAdvancedSettings();
|
||||
selectPracticeExam("00:30");
|
||||
// id_time_limit_div should not be hidden when practice exam is specified
|
||||
expect($('#id_time_limit_div')).not.toHaveClass('is-hidden"');
|
||||
|
||||
// time limit should be visible, review rules and hide after due should be hidden
|
||||
checkOptionFieldVisibility(true, false, false);
|
||||
|
||||
$(".wrapper-modal-window .action-save").click();
|
||||
});
|
||||
|
||||
@@ -903,9 +919,12 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
|
||||
createCourseOutlinePage(this, mockCourseJSON, false);
|
||||
outlinePage.$('.outline-subsection .configure-button').click();
|
||||
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
|
||||
selectAdvancedSettings();
|
||||
selectTimedExam("00:30");
|
||||
// id_time_limit_div should not be hidden when timed exam is specified
|
||||
expect($('#id_time_limit_div')).not.toHaveClass('is-hidden"');
|
||||
|
||||
// time limit and hide after due should be visible, review rules should be hidden
|
||||
checkOptionFieldVisibility(true, false, true);
|
||||
|
||||
$(".wrapper-modal-window .action-save").click();
|
||||
});
|
||||
|
||||
@@ -913,9 +932,12 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
|
||||
createCourseOutlinePage(this, mockCourseJSON, false);
|
||||
outlinePage.$('.outline-subsection .configure-button').click();
|
||||
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
|
||||
selectAdvancedSettings();
|
||||
selectProctoredExam("00:30");
|
||||
// id_time_limit_div should not be hidden when timed exam is specified
|
||||
expect($('#id_time_limit_div')).not.toHaveClass('is-hidden"');
|
||||
|
||||
// time limit and review rules should be visible, hide after due should be hidden
|
||||
checkOptionFieldVisibility(true, true, false);
|
||||
|
||||
$(".wrapper-modal-window .action-save").click();
|
||||
|
||||
});
|
||||
@@ -924,10 +946,12 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
|
||||
createCourseOutlinePage(this, mockCourseJSON, false);
|
||||
outlinePage.$('.outline-subsection .configure-button').click();
|
||||
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
|
||||
selectAdvancedSettings();
|
||||
selectProctoredExam("abcd");
|
||||
// id_time_limit_div should not be hidden when timed exam is specified
|
||||
expect($('#id_time_limit_div')).not.toHaveClass('is-hidden"');
|
||||
expect($('#id_time_limit')).toHaveValue('00:30');
|
||||
|
||||
// time limit field should be visible and have the correct value
|
||||
expect($('.field-time-limit').is(':visible')).toBe(true);
|
||||
expect($('.field-time-limit input').val()).toEqual("00:30");
|
||||
|
||||
});
|
||||
|
||||
@@ -944,21 +968,24 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
|
||||
"is_time_limited": false,
|
||||
"is_practice_exam": false,
|
||||
"is_proctored_exam": false,
|
||||
"default_time_limit_minutes": 150
|
||||
"default_time_limit_minutes": 150,
|
||||
"hide_after_due": false,
|
||||
}, [
|
||||
]),
|
||||
])
|
||||
]);
|
||||
createCourseOutlinePage(this, mockCourseWithSpecialExamJSON, false);
|
||||
outlinePage.$('.outline-subsection .configure-button').click();
|
||||
expect($("#id_timed_exam").is(":checked")).toBe(false);
|
||||
expect($("#id_proctored_exam").is(":checked")).toBe(false);
|
||||
expect($("#id_not_timed").is(":checked")).toBe(true);
|
||||
expect($("#id_practice_exam").is(":checked")).toBe(false);
|
||||
expect($("#id_time_limit").val()).toBe("02:30");
|
||||
selectAdvancedSettings();
|
||||
expect($("input.timed_exam").is(":checked")).toBe(false);
|
||||
expect($("input.proctored_exam").is(":checked")).toBe(false);
|
||||
expect($("input.no_special_exam").is(":checked")).toBe(true);
|
||||
expect($("input.practice_exam").is(":checked")).toBe(false);
|
||||
expect($(".field-time-limit input").val()).toBe("02:30");
|
||||
expect($('.field-hide-after-due').is(':hidden')).toBe(true);
|
||||
});
|
||||
|
||||
it('can show a saved timed exam correctly', function() {
|
||||
it('can show a saved timed exam correctly when hide_after_due is true', function() {
|
||||
var mockCourseWithSpecialExamJSON = createMockCourseJSON({}, [
|
||||
createMockSectionJSON({
|
||||
has_changes: true,
|
||||
@@ -971,18 +998,51 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
|
||||
"is_time_limited": true,
|
||||
"is_practice_exam": false,
|
||||
"is_proctored_exam": false,
|
||||
"default_time_limit_minutes": 10
|
||||
"default_time_limit_minutes": 10,
|
||||
"hide_after_due": true,
|
||||
}, [
|
||||
]),
|
||||
])
|
||||
]);
|
||||
createCourseOutlinePage(this, mockCourseWithSpecialExamJSON, false);
|
||||
outlinePage.$('.outline-subsection .configure-button').click();
|
||||
expect($("#id_timed_exam").is(":checked")).toBe(true);
|
||||
expect($("#id_proctored_exam").is(":checked")).toBe(false);
|
||||
expect($("#id_not_timed").is(":checked")).toBe(false);
|
||||
expect($("#id_practice_exam").is(":checked")).toBe(false);
|
||||
expect($("#id_time_limit").val()).toBe("00:10");
|
||||
selectAdvancedSettings();
|
||||
expect($("input.timed_exam").is(":checked")).toBe(true);
|
||||
expect($("input.proctored_exam").is(":checked")).toBe(false);
|
||||
expect($("input.no_special_exam").is(":checked")).toBe(false);
|
||||
expect($("input.practice_exam").is(":checked")).toBe(false);
|
||||
expect($(".field-time-limit input").val()).toBe("00:10");
|
||||
expect($('.field-hide-after-due input').is(":checked")).toBe(true);
|
||||
});
|
||||
|
||||
it('can show a saved timed exam correctly when hide_after_due is true', function() {
|
||||
var mockCourseWithSpecialExamJSON = createMockCourseJSON({}, [
|
||||
createMockSectionJSON({
|
||||
has_changes: true,
|
||||
enable_proctored_exams: true,
|
||||
enable_timed_exams: true
|
||||
|
||||
}, [
|
||||
createMockSubsectionJSON({
|
||||
has_changes: true,
|
||||
"is_time_limited": true,
|
||||
"is_practice_exam": false,
|
||||
"is_proctored_exam": false,
|
||||
"default_time_limit_minutes": 10,
|
||||
"hide_after_due": false,
|
||||
}, [
|
||||
]),
|
||||
])
|
||||
]);
|
||||
createCourseOutlinePage(this, mockCourseWithSpecialExamJSON, false);
|
||||
outlinePage.$('.outline-subsection .configure-button').click();
|
||||
selectAdvancedSettings();
|
||||
expect($("input.timed_exam").is(":checked")).toBe(true);
|
||||
expect($("input.proctored_exam").is(":checked")).toBe(false);
|
||||
expect($("input.no_special_exam").is(":checked")).toBe(false);
|
||||
expect($("input.practice_exam").is(":checked")).toBe(false);
|
||||
expect($(".field-time-limit input").val()).toBe("00:10");
|
||||
expect($('.field-hide-after-due input').is(":checked")).toBe(false);
|
||||
});
|
||||
|
||||
it('can show a saved practice exam correctly', function() {
|
||||
@@ -1005,11 +1065,13 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
|
||||
]);
|
||||
createCourseOutlinePage(this, mockCourseWithSpecialExamJSON, false);
|
||||
outlinePage.$('.outline-subsection .configure-button').click();
|
||||
expect($("#id_timed_exam").is(":checked")).toBe(false);
|
||||
expect($("#id_proctored_exam").is(":checked")).toBe(false);
|
||||
expect($("#id_not_timed").is(":checked")).toBe(false);
|
||||
expect($("#id_practice_exam").is(":checked")).toBe(true);
|
||||
expect($("#id_time_limit").val()).toBe("02:30");
|
||||
selectAdvancedSettings();
|
||||
expect($("input.timed_exam").is(":checked")).toBe(false);
|
||||
expect($("input.proctored_exam").is(":checked")).toBe(false);
|
||||
expect($("input.no_special_exam").is(":checked")).toBe(false);
|
||||
expect($("input.practice_exam").is(":checked")).toBe(true);
|
||||
expect($(".field-time-limit input").val()).toBe("02:30");
|
||||
expect($('.field-hide-after-due').is(':hidden')).toBe(true);
|
||||
});
|
||||
|
||||
it('can show a saved proctored exam correctly', function() {
|
||||
@@ -1032,11 +1094,13 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
|
||||
]);
|
||||
createCourseOutlinePage(this, mockCourseWithSpecialExamJSON, false);
|
||||
outlinePage.$('.outline-subsection .configure-button').click();
|
||||
expect($("#id_timed_exam").is(":checked")).toBe(false);
|
||||
expect($("#id_proctored_exam").is(":checked")).toBe(true);
|
||||
expect($("#id_not_timed").is(":checked")).toBe(false);
|
||||
expect($("#id_practice_exam").is(":checked")).toBe(false);
|
||||
expect($("#id_time_limit").val()).toBe("02:30");
|
||||
selectAdvancedSettings();
|
||||
expect($("input.timed_exam").is(":checked")).toBe(false);
|
||||
expect($("input.proctored_exam").is(":checked")).toBe(true);
|
||||
expect($("input.no_special_exam").is(":checked")).toBe(false);
|
||||
expect($("input.practice_exam").is(":checked")).toBe(false);
|
||||
expect($(".field-time-limit input").val()).toBe("02:30");
|
||||
expect($('.field-hide-after-due').is(':hidden')).toBe(true);
|
||||
});
|
||||
|
||||
it('does not show proctored settings if proctored exams not enabled', function() {
|
||||
@@ -1052,16 +1116,19 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u
|
||||
"is_time_limited": true,
|
||||
"is_practice_exam": false,
|
||||
"is_proctored_exam": false,
|
||||
"default_time_limit_minutes": 150
|
||||
"default_time_limit_minutes": 150,
|
||||
"hide_after_due": true,
|
||||
}, [
|
||||
]),
|
||||
])
|
||||
]);
|
||||
createCourseOutlinePage(this, mockCourseWithSpecialExamJSON, false);
|
||||
outlinePage.$('.outline-subsection .configure-button').click();
|
||||
expect($("#id_timed_exam").is(":checked")).toBe(true);
|
||||
expect($("#id_not_timed").is(":checked")).toBe(false);
|
||||
expect($("#id_time_limit").val()).toBe("02:30");
|
||||
selectAdvancedSettings();
|
||||
expect($("input.timed_exam").is(":checked")).toBe(true);
|
||||
expect($("input.no_special_exam").is(":checked")).toBe(false);
|
||||
expect($(".field-time-limit input").val()).toBe("02:30");
|
||||
expect($('.field-hide-after-due input').is(":checked")).toBe(true);
|
||||
});
|
||||
|
||||
it('can select prerequisite', function() {
|
||||
|
||||
@@ -332,41 +332,47 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
templateName: 'timed-examination-preference-editor',
|
||||
className: 'edit-settings-timed-examination',
|
||||
events : {
|
||||
'change #id_not_timed': 'notTimedExam',
|
||||
'change #id_timed_exam': 'setTimedExam',
|
||||
'change #id_practice_exam': 'setPracticeExam',
|
||||
'change #id_proctored_exam': 'setProctoredExam',
|
||||
'focusout #id_time_limit': 'timeLimitFocusout'
|
||||
'change input.no_special_exam': 'notTimedExam',
|
||||
'change input.timed_exam': 'setTimedExam',
|
||||
'change input.practice_exam': 'setPracticeExam',
|
||||
'change input.proctored_exam': 'setProctoredExam',
|
||||
'focusout .field-time-limit input': 'timeLimitFocusout'
|
||||
},
|
||||
notTimedExam: function (event) {
|
||||
event.preventDefault();
|
||||
this.$('#id_time_limit_div').hide();
|
||||
this.$('.exam-review-rules-list-fields').hide();
|
||||
this.$('#id_time_limit').val('00:00');
|
||||
this.$('.exam-options').hide();
|
||||
this.$('.field-time-limit input').val('00:00');
|
||||
},
|
||||
selectSpecialExam: function (showRulesField) {
|
||||
this.$('#id_time_limit_div').show();
|
||||
if (!this.isValidTimeLimit(this.$('#id_time_limit').val())) {
|
||||
this.$('#id_time_limit').val('00:30');
|
||||
selectSpecialExam: function (showRulesField, showHideAfterDueField) {
|
||||
this.$('.exam-options').show();
|
||||
this.$('.field-time-limit').show();
|
||||
if (!this.isValidTimeLimit(this.$('.field-time-limit input').val())) {
|
||||
this.$('.field-time-limit input').val('00:30');
|
||||
}
|
||||
if (showRulesField) {
|
||||
this.$('.exam-review-rules-list-fields').show();
|
||||
this.$('.field-exam-review-rules').show();
|
||||
}
|
||||
else {
|
||||
this.$('.exam-review-rules-list-fields').hide();
|
||||
this.$('.field-exam-review-rules').hide();
|
||||
}
|
||||
if (showHideAfterDueField) {
|
||||
this.$('.field-hide-after-due').show();
|
||||
}
|
||||
else {
|
||||
this.$('.field-hide-after-due').hide();
|
||||
}
|
||||
},
|
||||
setTimedExam: function (event) {
|
||||
event.preventDefault();
|
||||
this.selectSpecialExam(false);
|
||||
this.selectSpecialExam(false, true);
|
||||
},
|
||||
setPracticeExam: function (event) {
|
||||
event.preventDefault();
|
||||
this.selectSpecialExam(false);
|
||||
this.selectSpecialExam(false, false);
|
||||
},
|
||||
setProctoredExam: function (event) {
|
||||
event.preventDefault();
|
||||
this.selectSpecialExam(true);
|
||||
this.selectSpecialExam(true, false);
|
||||
},
|
||||
timeLimitFocusout: function(event) {
|
||||
event.preventDefault();
|
||||
@@ -389,43 +395,51 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
this.setExamTime(this.model.get('default_time_limit_minutes'));
|
||||
|
||||
this.setReviewRules(this.model.get('exam_review_rules'));
|
||||
this.setHideAfterDue(this.model.get('hide_after_due'));
|
||||
},
|
||||
setExamType: function(is_time_limited, is_proctored_exam, is_practice_exam) {
|
||||
this.$('.field-time-limit').hide();
|
||||
this.$('.field-exam-review-rules').hide();
|
||||
this.$('.field-hide-after-due').hide();
|
||||
|
||||
if (!is_time_limited) {
|
||||
this.$("#id_not_timed").prop('checked', true);
|
||||
this.$('input.no_special_exam').prop('checked', true);
|
||||
return;
|
||||
}
|
||||
|
||||
this.$('#id_time_limit_div').show();
|
||||
this.$('.exam-review-rules-list-fields').hide();
|
||||
this.$('.field-time-limit').show();
|
||||
|
||||
if (this.options.enable_proctored_exams && is_proctored_exam) {
|
||||
if (is_practice_exam) {
|
||||
this.$('#id_practice_exam').prop('checked', true);
|
||||
this.$('input.practice_exam').prop('checked', true);
|
||||
} else {
|
||||
this.$('#id_proctored_exam').prop('checked', true);
|
||||
this.$('.exam-review-rules-list-fields').show();
|
||||
this.$('input.proctored_exam').prop('checked', true);
|
||||
this.$('.field-exam-review-rules').show();
|
||||
}
|
||||
} else {
|
||||
// Since we have an early exit at the top of the method
|
||||
// if the subsection is not time limited, then
|
||||
// here we rightfully assume that it just a timed exam
|
||||
this.$("#id_timed_exam").prop('checked', true);
|
||||
this.$('input.timed_exam').prop('checked', true);
|
||||
this.$('.field-hide-after-due').show();
|
||||
}
|
||||
},
|
||||
setExamTime: function(value) {
|
||||
var time = this.convertTimeLimitMinutesToString(value);
|
||||
this.$('#id_time_limit').val(time);
|
||||
this.$('.field-time-limit input').val(time);
|
||||
},
|
||||
setReviewRules: function (value) {
|
||||
this.$('#id_exam_review_rules').val(value);
|
||||
this.$('.field-exam-review-rules textarea').val(value);
|
||||
},
|
||||
setHideAfterDue: function(value) {
|
||||
this.$('.field-hide-after-due input').prop('checked', value);
|
||||
},
|
||||
isValidTimeLimit: function(time_limit) {
|
||||
var pattern = new RegExp('^\\d{1,2}:[0-5][0-9]$');
|
||||
return pattern.test(time_limit) && time_limit !== "00:00";
|
||||
},
|
||||
getExamTimeLimit: function () {
|
||||
return this.$('#id_time_limit').val();
|
||||
return this.$('.field-time-limit input').val();
|
||||
},
|
||||
convertTimeLimitMinutesToString: function (timeLimitMinutes) {
|
||||
var hoursStr = "" + Math.floor(timeLimitMinutes / 60);
|
||||
@@ -444,21 +458,22 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
var is_practice_exam;
|
||||
var is_proctored_exam;
|
||||
var time_limit = this.getExamTimeLimit();
|
||||
var exam_review_rules = this.$('#id_exam_review_rules').val();
|
||||
var exam_review_rules = this.$('.field-exam-review-rules textarea').val();
|
||||
var hide_after_due = this.$('.field-hide-after-due input').is(':checked');
|
||||
|
||||
if (this.$("#id_not_timed").is(':checked')){
|
||||
if (this.$('input.no_special_exam').is(':checked')){
|
||||
is_time_limited = false;
|
||||
is_practice_exam = false;
|
||||
is_proctored_exam = false;
|
||||
} else if (this.$("#id_timed_exam").is(':checked')){
|
||||
} else if (this.$('input.timed_exam').is(':checked')){
|
||||
is_time_limited = true;
|
||||
is_practice_exam = false;
|
||||
is_proctored_exam = false;
|
||||
} else if (this.$("#id_proctored_exam").is(':checked')){
|
||||
} else if (this.$('input.proctored_exam').is(':checked')){
|
||||
is_time_limited = true;
|
||||
is_practice_exam = false;
|
||||
is_proctored_exam = true;
|
||||
} else if (this.$("#id_practice_exam").is(':checked')){
|
||||
} else if (this.$('input.practice_exam').is(':checked')){
|
||||
is_time_limited = true;
|
||||
is_practice_exam = true;
|
||||
is_proctored_exam = true;
|
||||
@@ -469,6 +484,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
'is_practice_exam': is_practice_exam,
|
||||
'is_time_limited': is_time_limited,
|
||||
'exam_review_rules': exam_review_rules,
|
||||
'hide_after_due': hide_after_due,
|
||||
// We have to use the legacy field name
|
||||
// as the Ajax handler directly populates
|
||||
// the xBlocks fields. We will have to
|
||||
|
||||
@@ -564,7 +564,7 @@
|
||||
}
|
||||
.list-fields {
|
||||
.field-message {
|
||||
color: $gray;
|
||||
color: $gray-d1;
|
||||
font-size: ($baseline/2);
|
||||
}
|
||||
.field {
|
||||
|
||||
@@ -303,7 +303,7 @@ $outline-indent-width: $baseline;
|
||||
%outline-item-status {
|
||||
@extend %t-copy-sub2;
|
||||
@extend %t-strong;
|
||||
color: $color-copy-base;
|
||||
color: $gray-d1;
|
||||
|
||||
.icon {
|
||||
@extend %t-icon5;
|
||||
@@ -576,12 +576,16 @@ $outline-indent-width: $baseline;
|
||||
> .subsection-status .status-timed-proctored-exam {
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
> .subsection-status .status-hide-after-due {
|
||||
opacity: 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
// status - grading
|
||||
.status-grading, .status-timed-proctored-exam {
|
||||
.status-grading, .status-timed-proctored-exam, .status-hide-after-due {
|
||||
@include transition(opacity $tmg-f2 ease-in-out 0s);
|
||||
opacity: 0.65;
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.status-grading-value, .status-proctored-exam-value {
|
||||
|
||||
@@ -67,96 +67,96 @@ if (is_proctored_exam) {
|
||||
}
|
||||
%>
|
||||
<% if (parentInfo) { %>
|
||||
<li class="outline-item outline-<%= xblockType %> <%= visibilityClass %> is-draggable <%= includesChildren ? 'is-collapsible' : '' %> <%= isCollapsed ? 'is-collapsed' : '' %>"
|
||||
data-parent="<%= parentInfo.get('id') %>" data-locator="<%= xblockInfo.get('id') %>">
|
||||
<li class="outline-item outline-<%- xblockType %> <%- visibilityClass %> is-draggable <%- includesChildren ? 'is-collapsible' : '' %> <%- isCollapsed ? 'is-collapsed' : '' %>"
|
||||
data-parent="<%- parentInfo.get('id') %>" data-locator="<%- xblockInfo.get('id') %>">
|
||||
|
||||
<span class="draggable-drop-indicator draggable-drop-indicator-before"><i class="icon fa fa-caret-right"></i></span>
|
||||
<% if (xblockInfo.isHeaderVisible()) { %>
|
||||
<div class="<%= xblockType %>-header">
|
||||
<div class="<%- xblockType %>-header">
|
||||
<% if (includesChildren) { %>
|
||||
<h3 class="<%= xblockType %>-header-details expand-collapse <%= isCollapsed ? 'expand' : 'collapse' %> ui-toggle-expansion"
|
||||
title="<%= interpolate(
|
||||
<h3 class="<%- xblockType %>-header-details expand-collapse <%- isCollapsed ? 'expand' : 'collapse' %> ui-toggle-expansion"
|
||||
title="<%- interpolate(
|
||||
gettext('Collapse/Expand this %(xblock_type)s'), { xblock_type: xblockTypeDisplayName }, true
|
||||
) %>"
|
||||
>
|
||||
<i class="icon fa fa-caret-down"></i>
|
||||
<% } else { %>
|
||||
<h3 class="<%= xblockType %>-header-details">
|
||||
<h3 class="<%- xblockType %>-header-details">
|
||||
<% } %>
|
||||
<% if (xblockInfo.isVertical()) { %>
|
||||
<span class="unit-title item-title">
|
||||
<a href="<%= xblockInfo.get('studio_url') %>"><%- xblockInfo.get('display_name') %></a>
|
||||
<a href="<%- xblockInfo.get('studio_url') %>"><%- xblockInfo.get('display_name') %></a>
|
||||
</span>
|
||||
<% } else { %>
|
||||
<span class="wrapper-<%= xblockType %>-title wrapper-xblock-field incontext-editor is-editable" data-field="display_name" data-field-display-name="<%= gettext("Display Name") %>">
|
||||
<span class="<%= xblockType %>-title item-title xblock-field-value incontext-editor-value"><%- xblockInfo.get('display_name') %></span>
|
||||
<span class="wrapper-<%- xblockType %>-title wrapper-xblock-field incontext-editor is-editable" data-field="display_name" data-field-display-name="<%- gettext("Display Name") %>">
|
||||
<span class="<%- xblockType %>-title item-title xblock-field-value incontext-editor-value"><%- xblockInfo.get('display_name') %></span>
|
||||
</span>
|
||||
<% } %>
|
||||
</h3>
|
||||
|
||||
<div class="<%= xblockType %>-header-actions">
|
||||
<div class="<%- xblockType %>-header-actions">
|
||||
<ul class="actions-list">
|
||||
<% if (xblockInfo.isPublishable()) { %>
|
||||
<li class="action-item action-publish">
|
||||
<a href="#" data-tooltip="<%= gettext('Publish') %>" class="publish-button action-button">
|
||||
<a href="#" data-tooltip="<%- gettext('Publish') %>" class="publish-button action-button">
|
||||
<i class="icon fa fa-upload"></i>
|
||||
<span class="sr action-button-text"><%= gettext('Publish') %></span>
|
||||
<span class="sr action-button-text"><%- gettext('Publish') %></span>
|
||||
</a>
|
||||
</li>
|
||||
<% } %>
|
||||
<% if (xblockInfo.isEditableOnCourseOutline()) { %>
|
||||
<li class="action-item action-configure">
|
||||
<a href="#" data-tooltip="<%= gettext('Configure') %>" class="configure-button action-button">
|
||||
<a href="#" data-tooltip="<%- gettext('Configure') %>" class="configure-button action-button">
|
||||
<i class="icon fa fa-gear"></i>
|
||||
<span class="sr action-button-text"><%= gettext('Configure') %></span>
|
||||
<span class="sr action-button-text"><%- gettext('Configure') %></span>
|
||||
</a>
|
||||
</li>
|
||||
<% } %>
|
||||
<% if (xblockInfo.isDeletable()) { %>
|
||||
<li class="action-item action-delete">
|
||||
<a href="#" data-tooltip="<%= gettext('Delete') %>" class="delete-button action-button">
|
||||
<a href="#" data-tooltip="<%- gettext('Delete') %>" class="delete-button action-button">
|
||||
<i class="icon fa fa-trash-o" aria-hidden="true"></i>
|
||||
<span class="sr action-button-text"><%= gettext('Delete') %></span>
|
||||
<span class="sr action-button-text"><%- gettext('Delete') %></span>
|
||||
</a>
|
||||
</li>
|
||||
<% } %>
|
||||
<% if (xblockInfo.isDraggable()) { %>
|
||||
<li class="action-item action-drag">
|
||||
<span data-tooltip="<%= gettext('Drag to reorder') %>"
|
||||
class="drag-handle <%= xblockType %>-drag-handle action">
|
||||
<span class="sr"><%= gettext('Drag to reorder') %></span>
|
||||
<span data-tooltip="<%- gettext('Drag to reorder') %>"
|
||||
class="drag-handle <%- xblockType %>-drag-handle action">
|
||||
<span class="sr"><%- gettext('Drag to reorder') %></span>
|
||||
</span>
|
||||
</li>
|
||||
<% } %>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="<%= xblockType %>-status">
|
||||
<div class="<%- xblockType %>-status">
|
||||
<% if (!xblockInfo.isVertical()) { %>
|
||||
<% if (xblockInfo.get('explanatory_message') !=null) { %>
|
||||
<div class="explanatory-message">
|
||||
<span>
|
||||
<%= xblockInfo.get('explanatory_message') %>
|
||||
<%- xblockInfo.get('explanatory_message') %>
|
||||
</span>
|
||||
</div>
|
||||
<% } else { %>
|
||||
<div class="status-release">
|
||||
<p>
|
||||
<span class="sr status-release-label"><%= gettext('Release Status:') %></span>
|
||||
<span class="sr status-release-label"><%- gettext('Release Status:') %></span>
|
||||
<span class="status-release-value">
|
||||
<% if (!course.get('self_paced')) { %>
|
||||
<% if (xblockInfo.get('released_to_students')) { %>
|
||||
<i class="icon fa fa-check-square-o"></i>
|
||||
<%= gettext('Released:') %>
|
||||
<i class="icon fa fa-check"></i>
|
||||
<%- gettext('Released:') %>
|
||||
<% } else if (xblockInfo.get('release_date')) { %>
|
||||
<i class="icon fa fa-clock-o"></i>
|
||||
<%= gettext('Scheduled:') %>
|
||||
<%- gettext('Scheduled:') %>
|
||||
<% } else { %>
|
||||
<i class="icon fa fa-clock-o"></i>
|
||||
<%= gettext('Unscheduled') %>
|
||||
<%- gettext('Unscheduled') %>
|
||||
<% } %>
|
||||
<% if (xblockInfo.get('release_date')) { %>
|
||||
<%= xblockInfo.get('release_date') %>
|
||||
<%- xblockInfo.get('release_date') %>
|
||||
<% } %>
|
||||
<% } %>
|
||||
</span>
|
||||
@@ -166,25 +166,36 @@ if (is_proctored_exam) {
|
||||
<% if (xblockInfo.get('is_time_limited')) { %>
|
||||
<div class="status-timed-proctored-exam">
|
||||
<p>
|
||||
<span class="sr status-grading-label"> <%= gettext('Graded as:') %> </span>
|
||||
<span class="sr status-grading-label"> <%- gettext('Graded as:') %> </span>
|
||||
<i class="icon fa fa-check"></i>
|
||||
<span class="status-grading-value"> <%= gradingType %> </span>
|
||||
<span class="status-grading-value"> <%- gradingType %> </span>
|
||||
-
|
||||
<span class="sr status-proctored-exam-label"> <%- exam_value %> </span>
|
||||
<span class="status-proctored-exam-value"> <%- exam_value %> </span>
|
||||
<% if (xblockInfo.get('due_date')) { %>
|
||||
<span class="status-grading-date"> <%= gettext('Due:') %> <%= xblockInfo.get('due_date') %> </span>
|
||||
<span class="status-grading-date"> <%- gettext('Due:') %> <%- xblockInfo.get('due_date') %> </span>
|
||||
<% } %>
|
||||
</p>
|
||||
</div>
|
||||
<div class="status-hide-after-due">
|
||||
<p>
|
||||
<% if (!is_proctored_exam && xblockInfo.get('hide_after_due')) { %>
|
||||
<i class="icon fa fa-eye-slash"></i>
|
||||
<span class="status-hide-after-due-value"> <%- gettext("Exam will remain hidden after due date") %> </span>
|
||||
<% } else { %>
|
||||
<i class="icon fa fa-eye"></i>
|
||||
<span class="status-hide-after-due-value"> <%- gettext("Exam will be visible after due date") %> </span>
|
||||
<% } %>
|
||||
</p>
|
||||
</div>
|
||||
<% } else if (xblockInfo.get('due_date') || xblockInfo.get('graded')) { %>
|
||||
<div class="status-grading">
|
||||
<p>
|
||||
<span class="sr status-grading-label"> <%= gettext('Graded as:') %> </span>
|
||||
<span class="sr status-grading-label"> <%- gettext('Graded as:') %> </span>
|
||||
<i class="icon fa fa-check"></i>
|
||||
<span class="status-grading-value"> <%= gradingType %> </span>
|
||||
<span class="status-grading-value"> <%- gradingType %> </span>
|
||||
<% if (xblockInfo.get('due_date') && !course.get('self_paced')) { %>
|
||||
<span class="status-grading-date"> <%= gettext('Due:') %> <%= xblockInfo.get('due_date') %> </span>
|
||||
<span class="status-grading-date"> <%- gettext('Due:') %> <%- xblockInfo.get('due_date') %> </span>
|
||||
<% } %>
|
||||
</p>
|
||||
</div>
|
||||
@@ -192,7 +203,7 @@ if (is_proctored_exam) {
|
||||
<% } %>
|
||||
<% if (statusMessage) { %>
|
||||
<div class="status-message">
|
||||
<i class="icon fa <%= statusIconClass %>"></i>
|
||||
<i class="icon fa <%- statusIconClass %>"></i>
|
||||
<p class="status-message-copy"><%- statusMessage %></p>
|
||||
</div>
|
||||
<% } %>
|
||||
@@ -202,20 +213,20 @@ if (is_proctored_exam) {
|
||||
|
||||
<% if (!parentInfo && xblockInfo.get('child_info') && xblockInfo.get('child_info').children.length === 0) { %>
|
||||
<div class="no-content add-section">
|
||||
<p><%= gettext("You haven't added any content to this course yet.") %>
|
||||
<a href="#" class="button button-new" data-category="<%= childCategory %>"
|
||||
data-parent="<%= xblockInfo.get('id') %>" data-default-name="<%= defaultNewChildName %>"
|
||||
title="<%= interpolate(
|
||||
<p><%- gettext("You haven't added any content to this course yet.") %>
|
||||
<a href="#" class="button button-new" data-category="<%- childCategory %>"
|
||||
data-parent="<%- xblockInfo.get('id') %>" data-default-name="<%- defaultNewChildName %>"
|
||||
title="<%- interpolate(
|
||||
gettext('Click to add a new %(xblock_type)s'), { xblock_type: defaultNewChildName }, true
|
||||
) %>"
|
||||
>
|
||||
<i class="icon fa fa-plus"></i><%= addChildLabel %>
|
||||
<i class="icon fa fa-plus"></i><%- addChildLabel %>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<% } else if (!xblockInfo.isVertical()) { %>
|
||||
<div class="outline-content <%= xblockType %>-content">
|
||||
<ol class="<%= typeListClass %> is-sortable">
|
||||
<div class="outline-content <%- xblockType %>-content">
|
||||
<ol class="<%- typeListClass %> is-sortable">
|
||||
<li class="ui-splint ui-splint-indicator">
|
||||
<span class="draggable-drop-indicator draggable-drop-indicator-initial"><i class="icon fa fa-caret-right"></i></span>
|
||||
</li>
|
||||
@@ -223,14 +234,14 @@ if (is_proctored_exam) {
|
||||
|
||||
<% if (childType) { %>
|
||||
<% if (xblockInfo.isChildAddable()) { %>
|
||||
<div class="add-<%= childType %> add-item">
|
||||
<a href="#" class="button button-new" data-category="<%= childCategory %>"
|
||||
data-parent="<%= xblockInfo.get('id') %>" data-default-name="<%= defaultNewChildName %>"
|
||||
title="<%= interpolate(
|
||||
<div class="add-<%- childType %> add-item">
|
||||
<a href="#" class="button button-new" data-category="<%- childCategory %>"
|
||||
data-parent="<%- xblockInfo.get('id') %>" data-default-name="<%- defaultNewChildName %>"
|
||||
title="<%- interpolate(
|
||||
gettext('Click to add a new %(xblock_type)s'), { xblock_type: defaultNewChildName }, true
|
||||
) %>"
|
||||
>
|
||||
<i class="icon fa fa-plus"></i><%= addChildLabel %>
|
||||
<i class="icon fa fa-plus"></i><%- addChildLabel %>
|
||||
</a>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
@@ -1,73 +1,65 @@
|
||||
<form>
|
||||
<h3 class="modal-section-title"><%= gettext('Set as a Special Exam') %></h3>
|
||||
<div class="modal-section-content has-actions">
|
||||
<div class='exam-time-list-fields'>
|
||||
<ul class="list-fields list-input">
|
||||
<li class="field-radio">
|
||||
<input type="radio" id="id_not_timed" name="proctored" class="input input-radio" checked="checked"/>
|
||||
<label for="id_not_timed" class="label">
|
||||
<%- gettext('None') %>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class='exam-time-list-fields'>
|
||||
<ul class="list-fields list-input">
|
||||
<li class="field-radio">
|
||||
<input type="radio" id="id_timed_exam" name="proctored" class="input input-radio" />
|
||||
<label for="id_timed_exam" class="label" aria-describedby="timed-exam-description">
|
||||
<%- gettext('Timed') %>
|
||||
</label>
|
||||
</li>
|
||||
<ul class="list-fields list-input exam-types" role="group" aria-label="<%- gettext('Exam Types') %>">
|
||||
<li class="field-radio">
|
||||
<label class="label">
|
||||
<input type="radio" name="exam_type" class="input input-radio no_special_exam" checked="checked"/>
|
||||
<%- gettext('None') %>
|
||||
</label>
|
||||
</li>
|
||||
<li class="field-radio">
|
||||
<label class="label">
|
||||
<input type="radio" name="exam_type" class="input input-radio timed_exam"
|
||||
aria-describedby="timed-exam-description" />
|
||||
<%- gettext('Timed') %>
|
||||
</label>
|
||||
<p class='field-message' id='timed-exam-description'> <%- gettext('Use a timed exam to limit the time learners can spend on problems in this subsection. Learners must submit answers before the time expires. You can allow additional time for individual learners through the Instructor Dashboard.') %> </p>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<% if (enable_proctored_exam) { %>
|
||||
<div class='exam-time-list-fields'>
|
||||
<ul class="list-fields list-input">
|
||||
<li class="field-radio">
|
||||
<input type="radio" id="id_proctored_exam" name="proctored" class="input input-radio" />
|
||||
<label for="id_proctored_exam" class="label" aria-describedby="proctored-exam-description">
|
||||
<%- gettext('Proctored') %>
|
||||
</label>
|
||||
</li>
|
||||
</li>
|
||||
<% if (enable_proctored_exam) { %>
|
||||
<li class="field-radio">
|
||||
<label class="label">
|
||||
<input type="radio" name="exam_type" class="input input-radio proctored_exam"
|
||||
aria-describedby="proctored-exam-description" />
|
||||
<%- gettext('Proctored') %>
|
||||
</label>
|
||||
<p class='field-message' id='proctored-exam-description'> <%- gettext('Proctored exams are timed and they record video of each learner taking the exam. The videos are then reviewed to ensure that learners follow all examination rules.') %> </p>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='exam-time-list-fields'>
|
||||
<ul class="list-fields list-input">
|
||||
<li class="field-radio">
|
||||
<input type="radio" id="id_practice_exam" name="proctored" class="input input-radio" />
|
||||
<label for="id_practice_exam" class="label" aria-describedby="practice-exam-description">
|
||||
<%- gettext('Practice Proctored') %>
|
||||
</label>
|
||||
</li>
|
||||
</li>
|
||||
<li class="field-radio">
|
||||
<label class="label">
|
||||
<input type="radio" name="exam_type" class="input input-radio practice_exam"
|
||||
aria-describedby="practice-exam-description"/>
|
||||
<%- gettext('Practice Proctored') %>
|
||||
</label>
|
||||
<p class='field-message' id='practice-exam-description'> <%- gettext("Use a practice proctored exam to introduce learners to the proctoring tools and processes. Results of a practice exam do not affect a learner's grade.") %> </p>
|
||||
</ul>
|
||||
</div>
|
||||
<% } %>
|
||||
<div class='exam-time-list-fields is-hidden' id='id_time_limit_div'>
|
||||
<ul class="list-fields list-input time-limit">
|
||||
<li class="field field-text field-time-limit">
|
||||
<label for="id_time_limit" class="label"><%- gettext('Time Allotted (HH:MM):') %> </label>
|
||||
<input type="text" id="id_time_limit" name="time_limit"
|
||||
value="" aria-describedby="time-limit-description"
|
||||
placeholder="HH:MM" class="time_limit release-time time input input-text" autocomplete="off" />
|
||||
</li>
|
||||
<% } %>
|
||||
</ul>
|
||||
<ul class="list-fields list-input exam-options">
|
||||
<li class="field field-text field-time-limit">
|
||||
<label class="label">
|
||||
<%- gettext('Time Allotted (HH:MM):') %>
|
||||
<input type="text" value="" aria-describedby="time-limit-description" placeholder="HH:MM"
|
||||
class="time_limit release-time time input input-text" autocomplete="off" />
|
||||
</label>
|
||||
<p class='field-message' id='time-limit-description'><%- gettext('Select a time allotment for the exam. If it is over 24 hours, type in the amount of time. You can grant individual learners extra time to complete the exam through the Instructor Dashboard.') %></p>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='exam-review-rules-list-fields is-hidden'>
|
||||
<ul class="list-fields list-input exam-review-rules">
|
||||
<li class="field field-text field-exam-review-rules">
|
||||
<label for="id_exam_review_rules" class="label"><%- gettext('Review Rules') %> </label>
|
||||
<textarea id="id_exam_review_rules" cols="50" maxlength="255" name="review_rules" aria-describedby="review-rules-description"
|
||||
</li>
|
||||
<li class="field field-text field-exam-review-rules">
|
||||
<label class="label">
|
||||
<%- gettext('Review Rules') %>
|
||||
<textarea cols="50" maxlength="255" aria-describedby="review-rules-description"
|
||||
class="review-rules input input-text" autocomplete="off" />
|
||||
</li>
|
||||
</label>
|
||||
<p class='field-message' id='review-rules-description'><%- gettext('Specify any additional rules or rule exceptions that the proctoring review team should enforce when reviewing the videos. For example, you could specify that calculators are allowed.') %></p>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li class="field-checkbox field-hide-after-due">
|
||||
<label class="label">
|
||||
<input type="checkbox" class="input input-checkbox"
|
||||
aria-describedby="hide-after-due-description"/>
|
||||
<%- gettext('Hide Exam After Due Date') %>
|
||||
</label>
|
||||
<p class='field-message' id='hide-after-due-description'><%- gettext('By default, submitted exams are available for review after the due date has passed. Select this option to keep exams hidden after that date.') %></p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user