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
d5de1d82
Commit
d5de1d82
authored
Oct 13, 2015
by
Chris Dodge
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
store declines in the CreditRequirementStatus table
parent
4f897783
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
123 additions
and
19 deletions
+123
-19
edx_proctoring/api.py
+32
-8
edx_proctoring/tests/test_api.py
+89
-9
edx_proctoring/tests/test_views.py
+2
-2
No files found.
edx_proctoring/api.py
View file @
d5de1d82
...
@@ -600,11 +600,13 @@ def update_attempt_status(exam_id, user_id, to_status, raise_if_not_found=True,
...
@@ -600,11 +600,13 @@ def update_attempt_status(exam_id, user_id, to_status, raise_if_not_found=True,
exam
=
get_exam_by_id
(
exam_id
)
exam
=
get_exam_by_id
(
exam_id
)
if
to_status
==
ProctoredExamStudentAttemptStatus
.
verified
:
if
to_status
==
ProctoredExamStudentAttemptStatus
.
verified
:
verification
=
'satisfied'
credit_requirement_status
=
'satisfied'
elif
to_status
==
ProctoredExamStudentAttemptStatus
.
submitted
:
elif
to_status
==
ProctoredExamStudentAttemptStatus
.
submitted
:
verification
=
'submitted'
credit_requirement_status
=
'submitted'
elif
to_status
==
ProctoredExamStudentAttemptStatus
.
declined
:
credit_requirement_status
=
'declined'
else
:
else
:
verification
=
'failed'
credit_requirement_status
=
'failed'
log_msg
=
(
log_msg
=
(
'Calling set_credit_requirement_status for '
'Calling set_credit_requirement_status for '
...
@@ -613,7 +615,7 @@ def update_attempt_status(exam_id, user_id, to_status, raise_if_not_found=True,
...
@@ -613,7 +615,7 @@ def update_attempt_status(exam_id, user_id, to_status, raise_if_not_found=True,
user_id
=
exam_attempt_obj
.
user_id
,
user_id
=
exam_attempt_obj
.
user_id
,
course_id
=
exam
[
'course_id'
],
course_id
=
exam
[
'course_id'
],
content_id
=
exam_attempt_obj
.
proctored_exam
.
content_id
,
content_id
=
exam_attempt_obj
.
proctored_exam
.
content_id
,
status
=
verification
status
=
credit_requirement_status
)
)
)
)
log
.
info
(
log_msg
)
log
.
info
(
log_msg
)
...
@@ -623,7 +625,7 @@ def update_attempt_status(exam_id, user_id, to_status, raise_if_not_found=True,
...
@@ -623,7 +625,7 @@ def update_attempt_status(exam_id, user_id, to_status, raise_if_not_found=True,
course_key_or_id
=
exam
[
'course_id'
],
course_key_or_id
=
exam
[
'course_id'
],
req_namespace
=
'proctored_exam'
,
req_namespace
=
'proctored_exam'
,
req_name
=
exam_attempt_obj
.
proctored_exam
.
content_id
,
req_name
=
exam_attempt_obj
.
proctored_exam
.
content_id
,
status
=
verification
status
=
credit_requirement_status
)
)
if
cascade_effects
and
ProctoredExamStudentAttemptStatus
.
is_a_cascadable_failure
(
to_status
):
if
cascade_effects
and
ProctoredExamStudentAttemptStatus
.
is_a_cascadable_failure
(
to_status
):
...
@@ -963,6 +965,7 @@ def _are_prerequirements_satisfied(prerequisites_statuses, evaluate_for_requirem
...
@@ -963,6 +965,7 @@ def _are_prerequirements_satisfied(prerequisites_statuses, evaluate_for_requirem
satisfied_prerequisites
=
[]
satisfied_prerequisites
=
[]
failed_prerequisites
=
[]
failed_prerequisites
=
[]
pending_prerequisites
=
[]
pending_prerequisites
=
[]
declined_prerequisites
=
[]
# insure an ordered and filtered list
# insure an ordered and filtered list
# we remove 'grade' requirements since those cannot be
# we remove 'grade' requirements since those cannot be
...
@@ -996,6 +999,8 @@ def _are_prerequirements_satisfied(prerequisites_statuses, evaluate_for_requirem
...
@@ -996,6 +999,8 @@ def _are_prerequirements_satisfied(prerequisites_statuses, evaluate_for_requirem
satisfied_prerequisites
.
append
(
requirement
)
satisfied_prerequisites
.
append
(
requirement
)
elif
status
==
'failed'
:
elif
status
==
'failed'
:
failed_prerequisites
.
append
(
requirement
)
failed_prerequisites
.
append
(
requirement
)
elif
status
==
'declined'
:
declined_prerequisites
.
append
(
requirement
)
else
:
else
:
pending_prerequisites
.
append
(
requirement
)
pending_prerequisites
.
append
(
requirement
)
...
@@ -1005,12 +1010,13 @@ def _are_prerequirements_satisfied(prerequisites_statuses, evaluate_for_requirem
...
@@ -1005,12 +1010,13 @@ def _are_prerequirements_satisfied(prerequisites_statuses, evaluate_for_requirem
# all prequisites are satisfied if there are no failed or pending requirement
# all prequisites are satisfied if there are no failed or pending requirement
# statuses
# statuses
'are_prerequisites_satisifed'
:
(
'are_prerequisites_satisifed'
:
(
not
failed_prerequisites
and
not
pending_prerequisites
not
failed_prerequisites
and
not
pending_prerequisites
and
not
declined_prerequisites
),
),
# note that we reverse the list here, because we assempled it by walking backwards
# note that we reverse the list here, because we assempled it by walking backwards
'satisfied_prerequisites'
:
list
(
reversed
(
satisfied_prerequisites
)),
'satisfied_prerequisites'
:
list
(
reversed
(
satisfied_prerequisites
)),
'failed_prerequisites'
:
list
(
reversed
(
failed_prerequisites
)),
'failed_prerequisites'
:
list
(
reversed
(
failed_prerequisites
)),
'pending_prerequisites'
:
list
(
reversed
(
pending_prerequisites
)),
'pending_prerequisites'
:
list
(
reversed
(
pending_prerequisites
)),
'declined_prerequisites'
:
list
(
reversed
(
declined_prerequisites
))
}
}
...
@@ -1364,7 +1370,9 @@ def _get_proctored_exam_view(exam, context, exam_id, user_id, course_id):
...
@@ -1364,7 +1370,9 @@ def _get_proctored_exam_view(exam, context, exam_id, user_id, course_id):
# so, show them:
# so, show them:
# 1) If there are failed prerequisites then block user and say why
# 1) If there are failed prerequisites then block user and say why
# 2) If there are pending prerequisites then block user and allow them to remediate them
# 2) If there are pending prerequisites then block user and allow them to remediate them
# 3) Otherwise - all prerequisites are satisfied - then give user
# 3) If there are declined prerequisites, then we auto-decline proctoring since user
# explicitly declined their interest in credit
# 4) Otherwise - all prerequisites are satisfied - then give user
# option to take exam as proctored
# option to take exam as proctored
# get information about prerequisites
# get information about prerequisites
...
@@ -1386,7 +1394,23 @@ def _get_proctored_exam_view(exam, context, exam_id, user_id, course_id):
...
@@ -1386,7 +1394,23 @@ def _get_proctored_exam_view(exam, context, exam_id, user_id, course_id):
})
})
if
not
prerequisite_status
[
'are_prerequisites_satisifed'
]:
if
not
prerequisite_status
[
'are_prerequisites_satisifed'
]:
# do we have failed prerequisites? That takes priority
# do we have any declined prerequisites, if so, then we
# will auto-decline this proctored exam
if
prerequisite_status
[
'declined_prerequisites'
]:
# user hasn't a record of attempt, create one now
# so we can mark it as declined
create_exam_attempt
(
exam_id
,
user_id
)
update_attempt_status
(
exam_id
,
user_id
,
ProctoredExamStudentAttemptStatus
.
declined
,
raise_if_not_found
=
False
)
return
None
# do we have failed prerequisites? That takes priority in terms of
# messaging
if
prerequisite_status
[
'failed_prerequisites'
]:
if
prerequisite_status
[
'failed_prerequisites'
]:
# Let's resolve the URLs to jump to this prequisite
# Let's resolve the URLs to jump to this prequisite
prerequisite_status
[
'failed_prerequisites'
]
=
_resolve_prerequisite_links
(
prerequisite_status
[
'failed_prerequisites'
]
=
_resolve_prerequisite_links
(
...
...
edx_proctoring/tests/test_api.py
View file @
d5de1d82
...
@@ -155,6 +155,39 @@ class ProctoredExamApiTests(LoggedInTestCase):
...
@@ -155,6 +155,39 @@ class ProctoredExamApiTests(LoggedInTestCase):
},
},
]
]
self
.
declined_prerequisites
=
[
{
'namespace'
:
'proctoring'
,
'name'
:
'proc1'
,
'order'
:
2
,
'status'
:
'satisfied'
,
},
{
'namespace'
:
'reverification'
,
'name'
:
'rever1'
,
'order'
:
1
,
'status'
:
'satisfied'
,
},
{
'namespace'
:
'grade'
,
'name'
:
'grade1'
,
'order'
:
0
,
'status'
:
'pending'
,
},
{
'namespace'
:
'reverification'
,
'name'
:
'rever2'
,
'order'
:
3
,
'status'
:
'declined'
,
},
{
'namespace'
:
'proctoring'
,
'name'
:
'proc2'
,
'order'
:
4
,
'status'
:
'pending'
,
},
]
def
_create_proctored_exam
(
self
):
def
_create_proctored_exam
(
self
):
"""
"""
Calls the api's create_exam to create an exam object.
Calls the api's create_exam to create an exam object.
...
@@ -575,6 +608,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
...
@@ -575,6 +608,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
@ddt.data
(
@ddt.data
(
(
ProctoredExamStudentAttemptStatus
.
verified
,
'satisfied'
),
(
ProctoredExamStudentAttemptStatus
.
verified
,
'satisfied'
),
(
ProctoredExamStudentAttemptStatus
.
submitted
,
'submitted'
),
(
ProctoredExamStudentAttemptStatus
.
submitted
,
'submitted'
),
(
ProctoredExamStudentAttemptStatus
.
declined
,
'declined'
),
(
ProctoredExamStudentAttemptStatus
.
error
,
'failed'
)
(
ProctoredExamStudentAttemptStatus
.
error
,
'failed'
)
)
)
@ddt.unpack
@ddt.unpack
...
@@ -797,11 +831,15 @@ class ProctoredExamApiTests(LoggedInTestCase):
...
@@ -797,11 +831,15 @@ class ProctoredExamApiTests(LoggedInTestCase):
(
'reverification'
,
'pending'
,
'The following prerequisites are in a <strong>pending</strong> state'
,
True
),
(
'reverification'
,
'pending'
,
'The following prerequisites are in a <strong>pending</strong> state'
,
True
),
(
'reverification'
,
'failed'
,
'You did not satisfy the following prerequisites'
,
True
),
(
'reverification'
,
'failed'
,
'You did not satisfy the following prerequisites'
,
True
),
(
'reverification'
,
'satisfied'
,
'To be eligible to earn credit for this course'
,
False
),
(
'reverification'
,
'satisfied'
,
'To be eligible to earn credit for this course'
,
False
),
(
'reverification'
,
'declined'
,
None
,
False
),
(
'proctored_exam'
,
None
,
'The following prerequisites are in a <strong>pending</strong> state'
,
True
),
(
'proctored_exam'
,
None
,
'The following prerequisites are in a <strong>pending</strong> state'
,
True
),
(
'proctored_exam'
,
'pending'
,
'The following prerequisites are in a <strong>pending</strong> state'
,
True
),
(
'proctored_exam'
,
'pending'
,
'The following prerequisites are in a <strong>pending</strong> state'
,
True
),
(
'proctored_exam'
,
'failed'
,
'You did not satisfy the following prerequisites'
,
True
),
(
'proctored_exam'
,
'failed'
,
'You did not satisfy the following prerequisites'
,
True
),
(
'proctored_exam'
,
'satisfied'
,
'To be eligible to earn credit for this course'
,
False
),
(
'proctored_exam'
,
'satisfied'
,
'To be eligible to earn credit for this course'
,
False
),
(
'grade'
,
'failed'
,
'To be eligible to earn credit for this course'
,
False
)
(
'proctored_exam'
,
'declined'
,
None
,
False
),
(
'grade'
,
'failed'
,
'To be eligible to earn credit for this course'
,
False
),
# this is nonesense, but let's double check it
(
'grade'
,
'declined'
,
'To be eligible to earn credit for this course'
,
False
),
)
)
@ddt.unpack
@ddt.unpack
def
test_prereq_scenarios
(
self
,
namespace
,
req_status
,
expected_content
,
should_see_prereq
):
def
test_prereq_scenarios
(
self
,
namespace
,
req_status
,
expected_content
,
should_see_prereq
):
...
@@ -839,7 +877,16 @@ class ProctoredExamApiTests(LoggedInTestCase):
...
@@ -839,7 +877,16 @@ class ProctoredExamApiTests(LoggedInTestCase):
}
}
)
)
self
.
assertIn
(
expected_content
,
rendered_response
)
if
expected_content
:
self
.
assertIn
(
expected_content
,
rendered_response
)
else
:
self
.
assertIsNone
(
rendered_response
)
if
req_status
==
'declined'
and
not
expected_content
:
# also we should have auto-declined if a pre-requisite was declined
attempt
=
get_exam_attempt
(
exam
[
'id'
],
self
.
user_id
)
self
.
assertIsNotNone
(
attempt
)
self
.
assertEqual
(
attempt
[
'status'
],
ProctoredExamStudentAttemptStatus
.
declined
)
if
should_see_prereq
:
if
should_see_prereq
:
self
.
assertIn
(
'Foo Requirement'
,
rendered_response
)
self
.
assertIn
(
'Foo Requirement'
,
rendered_response
)
...
@@ -2134,19 +2181,20 @@ class ProctoredExamApiTests(LoggedInTestCase):
...
@@ -2134,19 +2181,20 @@ class ProctoredExamApiTests(LoggedInTestCase):
self
.
assertEqual
(
ordered_list
[
3
][
'name'
],
'proc2'
)
self
.
assertEqual
(
ordered_list
[
3
][
'name'
],
'proc2'
)
@ddt.data
(
@ddt.data
(
(
'rever1'
,
True
,
0
,
0
,
0
),
(
'rever1'
,
True
,
0
,
0
,
0
,
0
),
(
'proc1'
,
True
,
1
,
0
,
0
),
(
'proc1'
,
True
,
1
,
0
,
0
,
0
),
(
'rever2'
,
True
,
2
,
0
,
0
),
(
'rever2'
,
True
,
2
,
0
,
0
,
0
),
(
'proc2'
,
False
,
2
,
1
,
0
),
(
'proc2'
,
False
,
2
,
1
,
0
,
0
),
(
'unknown'
,
False
,
2
,
1
,
1
),
(
'unknown'
,
False
,
2
,
1
,
1
,
0
),
(
None
,
False
,
2
,
1
,
1
),
(
None
,
False
,
2
,
1
,
1
,
0
),
)
)
@ddt.unpack
@ddt.unpack
def
test_are_prerequisite_satisifed
(
self
,
content_id
,
def
test_are_prerequisite_satisifed
(
self
,
content_id
,
expected_are_prerequisites_satisifed
,
expected_are_prerequisites_satisifed
,
expected_len_satisfied_prerequisites
,
expected_len_satisfied_prerequisites
,
expected_len_failed_prerequisites
,
expected_len_failed_prerequisites
,
expected_len_pending_prerequisites
):
expected_len_pending_prerequisites
,
expected_len_declined_prerequisites
):
"""
"""
verify proper operation of the logic when computing is prerequisites are satisfied
verify proper operation of the logic when computing is prerequisites are satisfied
"""
"""
...
@@ -2161,3 +2209,35 @@ class ProctoredExamApiTests(LoggedInTestCase):
...
@@ -2161,3 +2209,35 @@ class ProctoredExamApiTests(LoggedInTestCase):
self
.
assertEqual
(
len
(
results
[
'satisfied_prerequisites'
]),
expected_len_satisfied_prerequisites
)
self
.
assertEqual
(
len
(
results
[
'satisfied_prerequisites'
]),
expected_len_satisfied_prerequisites
)
self
.
assertEqual
(
len
(
results
[
'failed_prerequisites'
]),
expected_len_failed_prerequisites
)
self
.
assertEqual
(
len
(
results
[
'failed_prerequisites'
]),
expected_len_failed_prerequisites
)
self
.
assertEqual
(
len
(
results
[
'pending_prerequisites'
]),
expected_len_pending_prerequisites
)
self
.
assertEqual
(
len
(
results
[
'pending_prerequisites'
]),
expected_len_pending_prerequisites
)
self
.
assertEqual
(
len
(
results
[
'declined_prerequisites'
]),
expected_len_declined_prerequisites
)
@ddt.data
(
(
'rever1'
,
True
,
0
,
0
,
0
,
0
),
(
'proc1'
,
True
,
1
,
0
,
0
,
0
),
(
'rever2'
,
True
,
2
,
0
,
0
,
0
),
(
'proc2'
,
False
,
2
,
0
,
0
,
1
),
(
'unknown'
,
False
,
2
,
0
,
1
,
1
),
(
None
,
False
,
2
,
0
,
1
,
1
),
)
@ddt.unpack
def
test_declined_prerequisites
(
self
,
content_id
,
expected_are_prerequisites_satisifed
,
expected_len_satisfied_prerequisites
,
expected_len_failed_prerequisites
,
expected_len_pending_prerequisites
,
expected_len_declined_prerequisites
):
"""
verify proper operation of the logic when computing is prerequisites are satisfied
"""
results
=
_are_prerequirements_satisfied
(
self
.
declined_prerequisites
,
content_id
,
filter_out_namespaces
=
[
'grade'
]
)
self
.
assertEqual
(
results
[
'are_prerequisites_satisifed'
],
expected_are_prerequisites_satisifed
)
self
.
assertEqual
(
len
(
results
[
'satisfied_prerequisites'
]),
expected_len_satisfied_prerequisites
)
self
.
assertEqual
(
len
(
results
[
'failed_prerequisites'
]),
expected_len_failed_prerequisites
)
self
.
assertEqual
(
len
(
results
[
'pending_prerequisites'
]),
expected_len_pending_prerequisites
)
self
.
assertEqual
(
len
(
results
[
'declined_prerequisites'
]),
expected_len_declined_prerequisites
)
edx_proctoring/tests/test_views.py
View file @
d5de1d82
...
@@ -1573,7 +1573,7 @@ class TestStudentProctoredExamAttempt(LoggedInTestCase):
...
@@ -1573,7 +1573,7 @@ class TestStudentProctoredExamAttempt(LoggedInTestCase):
)
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
# make sure we
fail
ed the requirement status
# make sure we
declin
ed the requirement status
credit_service
=
get_runtime_service
(
'credit'
)
credit_service
=
get_runtime_service
(
'credit'
)
credit_status
=
credit_service
.
get_credit_state
(
self
.
user
.
id
,
proctored_exam
.
course_id
)
credit_status
=
credit_service
.
get_credit_state
(
self
.
user
.
id
,
proctored_exam
.
course_id
)
...
@@ -1581,7 +1581,7 @@ class TestStudentProctoredExamAttempt(LoggedInTestCase):
...
@@ -1581,7 +1581,7 @@ class TestStudentProctoredExamAttempt(LoggedInTestCase):
self
.
assertEqual
(
len
(
credit_status
[
'credit_requirement_status'
]),
1
)
self
.
assertEqual
(
len
(
credit_status
[
'credit_requirement_status'
]),
1
)
self
.
assertEqual
(
self
.
assertEqual
(
credit_status
[
'credit_requirement_status'
][
0
][
'status'
],
credit_status
[
'credit_requirement_status'
][
0
][
'status'
],
'
fail
ed'
'
declin
ed'
)
)
def
test_exam_callback
(
self
):
def
test_exam_callback
(
self
):
...
...
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