Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-proctoring
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
edx-proctoring
Commits
fa19afd7
Commit
fa19afd7
authored
Nov 04, 2015
by
chrisndodge
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #221 from edx/rc/release-0.10.x
Rc/release 0.10.x
parents
b8332996
b757a025
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
34 additions
and
68 deletions
+34
-68
README.md
+2
-2
edx_proctoring/api.py
+6
-16
edx_proctoring/static/proctoring/js/views/proctored_exam_attempt_view.js
+2
-2
edx_proctoring/templates/practice_exam/entrance.html
+0
-2
edx_proctoring/templates/proctored_exam/confirm-decline.html
+1
-1
edx_proctoring/templates/proctored_exam/entrance.html
+2
-6
edx_proctoring/templates/proctored_exam/ready_to_start.html
+1
-1
edx_proctoring/templates/proctored_exam/ready_to_submit.html
+2
-2
edx_proctoring/templates/timed_exam/entrance.html
+1
-1
edx_proctoring/templates/timed_exam/ready_to_submit.html
+2
-2
edx_proctoring/tests/test_api.py
+14
-32
setup.py
+1
-1
No files found.
README.md
View file @
fa19afd7
...
...
@@ -15,13 +15,13 @@ In order to use edx-proctoring, you must obtain an account (and secret configura
CONFIGURATION:
You will need to turn on the ENABLE_
PROCTORED
_EXAMS in lms.env.json and cms.env.json FEATURES dictionary:
You will need to turn on the ENABLE_
SPECIAL
_EXAMS in lms.env.json and cms.env.json FEATURES dictionary:
```
:
"FEATURES": {
:
"ENABLE_
PROCTORED
_EXAMS": true,
"ENABLE_
SPECIAL
_EXAMS": true,
:
}
```
...
...
edx_proctoring/api.py
View file @
fa19afd7
...
...
@@ -49,14 +49,6 @@ from edx_proctoring.runtime import get_runtime_service
log
=
logging
.
getLogger
(
__name__
)
def
is_feature_enabled
():
"""
Returns if this feature has been enabled in our FEATURE flags
"""
return
hasattr
(
settings
,
'FEATURES'
)
and
settings
.
FEATURES
.
get
(
'ENABLE_PROCTORED_EXAMS'
,
False
)
def
create_exam
(
course_id
,
content_id
,
exam_name
,
time_limit_mins
,
due_date
=
None
,
is_proctored
=
True
,
is_practice_exam
=
False
,
external_id
=
None
,
is_active
=
True
):
"""
...
...
@@ -475,7 +467,7 @@ def create_exam_attempt(exam_id, user_id, taking_as_proctored=False):
update_attempt_status
(
exam_id
,
user_id
,
ProctoredExamStudentAttemptStatus
.
declin
ed
ProctoredExamStudentAttemptStatus
.
expir
ed
)
log_msg
=
(
...
...
@@ -1297,15 +1289,12 @@ def _get_timed_exam_view(exam, context, exam_id, user_id, course_id):
# time limit, including any accommodations
allowed_time_limit_mins
=
exam
[
'time_limit_mins'
]
allowance
=
ProctoredExamStudentAllowance
.
get_allowance_for_user
(
exam_id
,
user_id
,
"Additional time (minutes)"
)
allowance_extra_mins
=
ProctoredExamStudentAllowance
.
get_additional_time_granted
(
exam_id
,
user_id
)
if
allowance
:
allowed_time_limit_mins
+=
int
(
allowance
.
value
)
if
allowance
_extra_mins
:
allowed_time_limit_mins
+=
int
(
allowance
_extra_mins
)
# apply any cut off times according to due dates
allowed_time_limit_mins
,
_
=
_calculate_allowed_mins
(
exam
[
'due_date'
],
allowed_time_limit_mins
...
...
@@ -1328,6 +1317,7 @@ def _get_timed_exam_view(exam, context, exam_id, user_id, course_id):
'total_time'
:
total_time
,
'has_due_date'
:
has_due_date
,
'exam_id'
:
exam_id
,
'exam_name'
:
exam
[
'exam_name'
],
'progress_page_url'
:
progress_page_url
,
'does_time_remain'
:
_does_time_remain
(
attempt
),
'enter_exam_endpoint'
:
reverse
(
'edx_proctoring.proctored_exam.attempt.collection'
),
...
...
edx_proctoring/static/proctoring/js/views/proctored_exam_attempt_view.js
View file @
fa19afd7
...
...
@@ -154,8 +154,8 @@ var edx = edx || {};
}
_
.
each
(
data_json
.
proctored_exam_attempts
,
function
(
proctored_exam_attempt
)
{
if
(
proctored_exam_attempt
.
taking_a
s_proctored
)
{
if
(
proctored_exam_attempt
.
is_sample_attempt
)
{
if
(
proctored_exam_attempt
.
proctored_exam
.
i
s_proctored
)
{
if
(
proctored_exam_attempt
.
proctored_exam
.
is_practice_exam
)
{
proctored_exam_attempt
.
exam_attempt_type
=
gettext
(
'Practice'
);
}
else
{
proctored_exam_attempt
.
exam_attempt_type
=
gettext
(
'Proctored'
);
...
...
edx_proctoring/templates/practice_exam/entrance.html
View file @
fa19afd7
...
...
@@ -12,9 +12,7 @@
{% endblocktrans %}
</p>
<button
class=
"gated-sequence start-timed-exam"
data-ajax-url=
"{{enter_exam_endpoint}}"
data-exam-id=
"{{exam_id}}"
data-attempt-proctored=
true
data-start-immediately=
false
>
<a>
{% trans "Continue to my practice exam" %}
</a>
<p>
{% blocktrans %}
You will be guided through steps to set up online proctoring software and to perform various checks.
...
...
edx_proctoring/templates/proctored_exam/confirm-decline.html
View file @
fa19afd7
...
...
@@ -12,7 +12,7 @@
{% endblocktrans %}
</div>
<div
class=
"proctored-exam-skip-actions"
>
<button
class=
"proctored-exam-skip-confirm-button btn btn-primary btn-base"
data-ajax-url=
"{{enter_exam_endpoint}}"
data-exam-id=
"{{exam_id}}"
>
<button
class=
"proctored-exam-skip-confirm-button btn btn-p
l-p
rimary btn-base"
data-ajax-url=
"{{enter_exam_endpoint}}"
data-exam-id=
"{{exam_id}}"
>
{% trans "Continue Exam Without Proctoring" %}
</button>
<button
class=
"proctored-exam-skip-cancel-button btn btn-default btn-base"
>
...
...
edx_proctoring/templates/proctored_exam/entrance.html
View file @
fa19afd7
...
...
@@ -11,9 +11,7 @@
{% endblocktrans %}
</p>
<button
class=
"gated-sequence start-timed-exam"
data-ajax-url=
"{{enter_exam_endpoint}}"
data-exam-id=
"{{exam_id}}"
data-attempt-proctored=
true
data-start-immediately=
false
>
<a>
{% trans "Continue to my proctored exam. I want to be eligible for credit." %}
</a>
{% trans "Continue to my proctored exam. I want to be eligible for credit." %}
<p>
{% blocktrans %}
You will be guided through steps to set up online proctoring software and to perform various checks.
</br>
...
...
@@ -23,9 +21,7 @@
<i
class=
"fa fa-arrow-circle-right"
></i>
</button>
<button
class=
"gated-sequence start-timed-exam"
data-attempt-proctored=
false
>
<a>
{% trans "Take this exam without proctoring." %}
</a>
{% trans "Take this exam without proctoring." %}
<i
class=
"fa fa-arrow-circle-right"
></i>
<p>
{% blocktrans %}
...
...
edx_proctoring/templates/proctored_exam/ready_to_start.html
View file @
fa19afd7
...
...
@@ -15,7 +15,7 @@
{% endblocktrans %}
</p>
<div>
<button
type=
"button"
class=
"proctored-enter-exam btn btn-primary btn-base"
data-action=
"start"
data-exam-id=
"{{exam_id}}"
data-change-state-url=
"{{change_state_url}}"
>
<button
type=
"button"
class=
"proctored-enter-exam btn btn-p
l-p
rimary btn-base"
data-action=
"start"
data-exam-id=
"{{exam_id}}"
data-change-state-url=
"{{change_state_url}}"
>
{% blocktrans %}
Start my exam
{% endblocktrans %}
...
...
edx_proctoring/templates/proctored_exam/ready_to_submit.html
View file @
fa19afd7
...
...
@@ -12,13 +12,13 @@
as well as achieve a final grade that meets credit requirements for the course.
{% endblocktrans %}
</p>
<button
type=
"button"
name=
"submit-proctored-exam"
class=
"exam-action-button btn btn-primary btn-base"
data-action=
"submit"
data-exam-id=
"{{exam_id}}"
data-change-state-url=
"{{change_state_url}}"
>
<button
type=
"button"
name=
"submit-proctored-exam"
class=
"exam-action-button btn btn-p
l-p
rimary btn-base"
data-action=
"submit"
data-exam-id=
"{{exam_id}}"
data-change-state-url=
"{{change_state_url}}"
>
{% blocktrans %}
Yes, end my proctored exam
{% endblocktrans %}
</button>
{% if does_time_remain %}
<button
type=
"button"
name=
"goback-proctored-exam"
class=
"exam-action-button btn btn-secondary btn-base"
data-action=
"start"
data-exam-id=
"{{exam_id}}"
data-change-state-url=
"{{change_state_url}}"
>
<button
type=
"button"
name=
"goback-proctored-exam"
class=
"exam-action-button btn btn-secondary btn-base"
data-action=
"start"
data-exam-id=
"{{exam_id}}"
data-change-state-url=
"{{change_state_url}}"
style=
"box-shadow: none"
>
{% blocktrans %}
No, I'd like to continue working
{% endblocktrans %}
...
...
edx_proctoring/templates/timed_exam/entrance.html
View file @
fa19afd7
...
...
@@ -2,7 +2,7 @@
<div
class=
"sequence timed-exam entrance"
data-exam-id=
"{{exam_id}}"
>
<h3>
{% blocktrans %}
{{
display
_name }} is a Timed Exam ({{total_time}})
{{
exam
_name }} is a Timed Exam ({{total_time}})
{% endblocktrans %}
</h3>
<p>
...
...
edx_proctoring/templates/timed_exam/ready_to_submit.html
View file @
fa19afd7
...
...
@@ -10,13 +10,13 @@
Make sure your answers are ready to be submitted, and then submit your exam. Your exam will then be graded.
{% endblocktrans %}
</p>
<button
type=
"button"
name=
"submit-proctored-exam"
class=
"exam-action-button btn btn-primary btn-base"
data-action=
"submit"
data-exam-id=
"{{exam_id}}"
data-change-state-url=
"{{change_state_url}}"
>
<button
type=
"button"
name=
"submit-proctored-exam"
class=
"exam-action-button btn btn-p
l-p
rimary btn-base"
data-action=
"submit"
data-exam-id=
"{{exam_id}}"
data-change-state-url=
"{{change_state_url}}"
>
{% blocktrans %}
Yes, submit my timed exam.
{% endblocktrans %}
</button>
{% if does_time_remain %}
<button
type=
"button"
name=
"goback-proctored-exam"
class=
"exam-action-button btn btn-secondary btn-base"
data-action=
"start"
data-exam-id=
"{{exam_id}}"
data-change-state-url=
"{{change_state_url}}"
>
<button
type=
"button"
name=
"goback-proctored-exam"
class=
"exam-action-button btn btn-secondary btn-base"
data-action=
"start"
data-exam-id=
"{{exam_id}}"
data-change-state-url=
"{{change_state_url}}"
style=
"box-shadow: none"
>
{% blocktrans %}
No, I want to continue working.
{% endblocktrans %}
...
...
edx_proctoring/tests/test_api.py
View file @
fa19afd7
...
...
@@ -31,7 +31,6 @@ from edx_proctoring.api import (
remove_exam_attempt
,
get_all_exam_attempts
,
get_filtered_exam_attempts
,
is_feature_enabled
,
mark_exam_attempt_timeout
,
mark_exam_attempt_as_ready
,
update_attempt_status
,
...
...
@@ -95,7 +94,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
self
.
value
=
'10'
self
.
external_id
=
'test_external_id'
self
.
proctored_exam_id
=
self
.
_create_proctored_exam
()
self
.
timed_exam
=
self
.
_create_timed_exam
()
self
.
timed_exam
_id
=
self
.
_create_timed_exam
()
self
.
practice_exam_id
=
self
.
_create_practice_exam
()
self
.
disabled_exam_id
=
self
.
_create_disabled_exam
()
...
...
@@ -276,7 +275,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
else
:
exam_id
=
self
.
proctored_exam_id
else
:
exam_id
=
self
.
timed_exam
exam_id
=
self
.
timed_exam
_id
return
ProctoredExamStudentAttempt
.
objects
.
create
(
proctored_exam_id
=
exam_id
,
...
...
@@ -291,7 +290,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
Creates the ProctoredExamStudentAttempt object.
"""
return
ProctoredExamStudentAttempt
.
objects
.
create
(
proctored_exam_id
=
self
.
proctored_exam_id
if
is_proctored
else
self
.
timed_exam
,
proctored_exam_id
=
self
.
proctored_exam_id
if
is_proctored
else
self
.
timed_exam
_id
,
user_id
=
self
.
user_id
,
external_id
=
self
.
external_id
,
started_at
=
started_at
if
started_at
else
datetime
.
now
(
pytz
.
UTC
),
...
...
@@ -324,18 +323,6 @@ class ProctoredExamApiTests(LoggedInTestCase):
proctored_exam_id
=
self
.
proctored_exam_id
,
user_id
=
self
.
user_id
,
key
=
self
.
key
,
value
=
self
.
value
)
def
test_feature_enabled
(
self
):
"""
Checks the is_feature_enabled method
"""
self
.
assertFalse
(
is_feature_enabled
())
with
patch
.
dict
(
'django.conf.settings.FEATURES'
,
{
'ENABLE_PROCTORED_EXAMS'
:
False
}):
self
.
assertFalse
(
is_feature_enabled
())
with
patch
.
dict
(
'django.conf.settings.FEATURES'
,
{
'ENABLE_PROCTORED_EXAMS'
:
True
}):
self
.
assertTrue
(
is_feature_enabled
())
def
test_create_duplicate_exam
(
self
):
"""
Test to create a proctored exam that has already exist in the
...
...
@@ -414,7 +401,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
test to get the exam by the exam_id and
then compare their values.
"""
timed_exam
=
get_exam_by_id
(
self
.
timed_exam
)
timed_exam
=
get_exam_by_id
(
self
.
timed_exam
_id
)
self
.
assertEqual
(
timed_exam
[
'course_id'
],
self
.
course_id
)
self
.
assertEqual
(
timed_exam
[
'content_id'
],
self
.
content_id_timed
)
self
.
assertEqual
(
timed_exam
[
'exam_name'
],
self
.
exam_name
)
...
...
@@ -546,7 +533,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
with
freeze_time
(
reset_time
):
attempt_id
=
create_exam_attempt
(
exam_id
,
self
.
user_id
)
attempt
=
get_exam_attempt_by_id
(
attempt_id
)
self
.
assertEqual
(
attempt
[
'status'
],
ProctoredExamStudentAttemptStatus
.
declin
ed
)
self
.
assertEqual
(
attempt
[
'status'
],
ProctoredExamStudentAttemptStatus
.
expir
ed
)
def
test_create_an_exam_attempt
(
self
):
"""
...
...
@@ -1647,29 +1634,26 @@ class ProctoredExamApiTests(LoggedInTestCase):
But user has an allowance
"""
ProctoredExamStudentAllowance
.
objects
.
create
(
proctored_exam_id
=
self
.
timed_exam
,
user_id
=
self
.
user_id
,
key
=
'Additional time (minutes)'
,
value
=
15
allowed_extra_time
=
10
add_allowance_for_user
(
self
.
timed_exam_id
,
self
.
user
.
username
,
ProctoredExamStudentAllowance
.
ADDITIONAL_TIME_GRANTED
,
str
(
allowed_extra_time
)
)
rendered_response
=
get_student_view
(
user_id
=
self
.
user_id
,
course_id
=
self
.
course_id
,
content_id
=
self
.
content_id_timed
,
context
=
{
'is_proctored'
:
False
,
'display_name'
:
self
.
exam_name
,
'default_time_limit_mins'
:
90
}
context
=
{}
)
self
.
assertNotIn
(
'data-exam-id="{proctored_exam_id}"'
.
format
(
proctored_exam_id
=
self
.
proctored_exam_id
),
rendered_response
)
self
.
assertIn
(
self
.
timed_exam_msg
.
format
(
exam_name
=
self
.
exam_name
),
rendered_response
)
self
.
assertIn
(
'3
6
minutes'
,
rendered_response
)
self
.
assertIn
(
'3
1
minutes'
,
rendered_response
)
self
.
assertNotIn
(
self
.
start_an_exam_msg
.
format
(
exam_name
=
self
.
exam_name
),
rendered_response
)
@ddt.data
(
...
...
@@ -1696,9 +1680,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
course_id
=
self
.
course_id
,
content_id
=
self
.
content_id_timed
,
context
=
{
'is_proctored'
:
False
,
'display_name'
:
self
.
exam_name
,
'default_time_limit_mins'
:
90
}
)
self
.
assertIn
(
expected_content
,
rendered_response
)
...
...
@@ -2102,7 +2084,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
Assert that we get the expected status summaries
for the timed exams.
"""
timed_exam
=
get_exam_by_id
(
self
.
timed_exam
)
timed_exam
=
get_exam_by_id
(
self
.
timed_exam
_id
)
summary
=
get_attempt_status_summary
(
self
.
user
.
id
,
timed_exam
[
'course_id'
],
...
...
setup.py
View file @
fa19afd7
...
...
@@ -34,7 +34,7 @@ def load_requirements(*requirements_paths):
setup
(
name
=
'edx-proctoring'
,
version
=
'0.10.1
0
'
,
version
=
'0.10.1
5
'
,
description
=
'Proctoring subsystem for Open edX'
,
long_description
=
open
(
'README.md'
)
.
read
(),
author
=
'edX'
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment