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
edx
edx-proctoring
Commits
47ce2865
Commit
47ce2865
authored
Aug 23, 2017
by
Ari Rizzitano
Committed by
GitHub
Aug 23, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #372 from edx/ari/ss-review-report
add report generation endpoint + tests
parents
614b8853
3189e027
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
201 additions
and
1 deletions
+201
-1
edx_proctoring/__init__.py
+1
-1
edx_proctoring/api.py
+46
-0
edx_proctoring/tests/test_api.py
+154
-0
No files found.
edx_proctoring/__init__.py
View file @
47ce2865
...
...
@@ -4,6 +4,6 @@ The exam proctoring subsystem for the Open edX platform.
from
__future__
import
absolute_import
__version__
=
'1.
1
.0'
__version__
=
'1.
2
.0'
default_app_config
=
'edx_proctoring.apps.EdxProctoringConfig'
# pylint: disable=invalid-name
edx_proctoring/api.py
View file @
47ce2865
...
...
@@ -37,6 +37,7 @@ from edx_proctoring.models import (
ProctoredExamStudentAttempt
,
ProctoredExamStudentAttemptStatus
,
ProctoredExamReviewPolicy
,
ProctoredExamSoftwareSecureReview
,
)
from
edx_proctoring.serializers
import
(
ProctoredExamSerializer
,
...
...
@@ -1947,3 +1948,48 @@ def get_student_view(user_id, course_id, content_id,
if
sub_view_func
:
return
sub_view_func
(
exam
,
context
,
exam_id
,
user_id
,
course_id
)
return
None
def
get_exam_violation_report
(
course_id
,
include_practice_exams
=
False
):
"""
Returns proctored exam attempts for the course id, including review details.
Violation status messages are aggregated as a list per attempt for each
violation type.
"""
attempts_by_code
=
{
attempt
[
'attempt_code'
]:
{
'course_id'
:
attempt
[
'proctored_exam'
][
'course_id'
],
'exam_name'
:
attempt
[
'proctored_exam'
][
'exam_name'
],
'username'
:
attempt
[
'user'
][
'username'
],
'email'
:
attempt
[
'user'
][
'email'
],
'attempt_code'
:
attempt
[
'attempt_code'
],
'allowed_time_limit_mins'
:
attempt
[
'allowed_time_limit_mins'
],
'is_sample_attempt'
:
attempt
[
'is_sample_attempt'
],
'started_at'
:
attempt
[
'started_at'
],
'completed_at'
:
attempt
[
'completed_at'
],
'status'
:
attempt
[
'status'
],
'review_status'
:
None
}
for
attempt
in
get_all_exam_attempts
(
course_id
)
}
reviews
=
ProctoredExamSoftwareSecureReview
.
objects
.
prefetch_related
(
'proctoredexamsoftwaresecurecomment_set'
)
.
filter
(
exam__course_id
=
course_id
,
exam__is_practice_exam
=
include_practice_exams
)
for
review
in
reviews
:
attempt_code
=
review
.
attempt_code
attempts_by_code
[
attempt_code
][
'review_status'
]
=
review
.
review_status
for
comment
in
review
.
proctoredexamsoftwaresecurecomment_set
.
all
():
comments_key
=
'{status} Comments'
.
format
(
status
=
comment
.
status
)
if
comments_key
not
in
attempts_by_code
[
attempt_code
]:
attempts_by_code
[
attempt_code
][
comments_key
]
=
[]
attempts_by_code
[
attempt_code
][
comments_key
]
.
append
(
comment
.
comment
)
return
sorted
(
attempts_by_code
.
values
(),
key
=
lambda
a
:
a
[
'exam_name'
])
edx_proctoring/tests/test_api.py
View file @
47ce2865
...
...
@@ -31,6 +31,7 @@ from edx_proctoring.api import (
get_exam_attempt_by_id
,
remove_exam_attempt
,
get_all_exam_attempts
,
get_exam_violation_report
,
get_filtered_exam_attempts
,
get_last_exam_completion_date
,
mark_exam_attempt_timeout
,
...
...
@@ -62,7 +63,10 @@ from edx_proctoring.exceptions import (
)
from
edx_proctoring.models
import
(
ProctoredExam
,
ProctoredExamSoftwareSecureReview
,
ProctoredExamSoftwareSecureComment
,
ProctoredExamStudentAllowance
,
ProctoredExamStudentAttempt
,
ProctoredExamStudentAttemptStatus
,
ProctoredExamReviewPolicy
,
)
...
...
@@ -1663,3 +1667,153 @@ class ProctoredExamApiTests(ProctoredExamTestCase):
timed_exam
[
'content_id'
]
)
self
.
assertIsNone
(
summary
)
def
test_get_exam_violation_report
(
self
):
"""
Test to get all the exam attempts.
"""
# attempt with comments in multiple categories
exam1_id
=
create_exam
(
course_id
=
self
.
course_id
,
content_id
=
'test_content_1'
,
exam_name
=
'DDDDDD'
,
time_limit_mins
=
self
.
default_time_limit
)
exam1_attempt_id
=
create_exam_attempt
(
exam_id
=
exam1_id
,
user_id
=
self
.
user_id
)
exam1_attempt
=
ProctoredExamStudentAttempt
.
objects
.
get_exam_attempt_by_id
(
exam1_attempt_id
)
exam1_review
=
ProctoredExamSoftwareSecureReview
.
objects
.
create
(
exam
=
ProctoredExam
.
get_exam_by_id
(
exam1_id
),
attempt_code
=
exam1_attempt
.
attempt_code
,
review_status
=
"Suspicious"
)
ProctoredExamSoftwareSecureComment
.
objects
.
create
(
review
=
exam1_review
,
status
=
"Rules Violation"
,
comment
=
"foo"
,
start_time
=
0
,
stop_time
=
1
,
duration
=
1
)
ProctoredExamSoftwareSecureComment
.
objects
.
create
(
review
=
exam1_review
,
status
=
"Suspicious"
,
comment
=
"bar"
,
start_time
=
0
,
stop_time
=
1
,
duration
=
1
)
ProctoredExamSoftwareSecureComment
.
objects
.
create
(
review
=
exam1_review
,
status
=
"Suspicious"
,
comment
=
"baz"
,
start_time
=
0
,
stop_time
=
1
,
duration
=
1
)
# attempt with comments in only one category
exam2_id
=
create_exam
(
course_id
=
self
.
course_id
,
content_id
=
'test_content_2'
,
exam_name
=
'CCCCCC'
,
time_limit_mins
=
self
.
default_time_limit
)
exam2_attempt_id
=
create_exam_attempt
(
exam_id
=
exam2_id
,
user_id
=
self
.
user_id
)
exam2_attempt
=
ProctoredExamStudentAttempt
.
objects
.
get_exam_attempt_by_id
(
exam2_attempt_id
)
exam2_review
=
ProctoredExamSoftwareSecureReview
.
objects
.
create
(
exam
=
ProctoredExam
.
get_exam_by_id
(
exam2_id
),
attempt_code
=
exam2_attempt
.
attempt_code
,
review_status
=
"Rules Violation"
)
ProctoredExamSoftwareSecureComment
.
objects
.
create
(
review
=
exam2_review
,
status
=
"Rules Violation"
,
comment
=
"bar"
,
start_time
=
0
,
stop_time
=
1
,
duration
=
1
)
# attempt with no comments, on a different exam
exam3_id
=
create_exam
(
course_id
=
self
.
course_id
,
content_id
=
'test_content_3'
,
exam_name
=
'BBBBBB'
,
time_limit_mins
=
self
.
default_time_limit
)
exam3_attempt_id
=
create_exam_attempt
(
exam_id
=
exam3_id
,
user_id
=
self
.
user_id
)
exam3_attempt
=
ProctoredExamStudentAttempt
.
objects
.
get_exam_attempt_by_id
(
exam3_attempt_id
)
ProctoredExamSoftwareSecureReview
.
objects
.
create
(
exam
=
ProctoredExam
.
get_exam_by_id
(
exam3_id
),
attempt_code
=
exam3_attempt
.
attempt_code
,
review_status
=
"Clean"
)
# attempt with no comments or review
exam4_id
=
create_exam
(
course_id
=
self
.
course_id
,
content_id
=
'test_content_4'
,
exam_name
=
'AAAAAA'
,
time_limit_mins
=
self
.
default_time_limit
)
exam4_attempt_id
=
create_exam_attempt
(
exam_id
=
exam4_id
,
user_id
=
self
.
user_id
)
ProctoredExamStudentAttempt
.
objects
.
get_exam_attempt_by_id
(
exam4_attempt_id
)
report
=
get_exam_violation_report
(
self
.
course_id
)
self
.
assertEqual
(
len
(
report
),
4
)
self
.
assertEqual
([
attempt
[
'exam_name'
]
for
attempt
in
report
],
[
'AAAAAA'
,
'BBBBBB'
,
'CCCCCC'
,
'DDDDDD'
])
self
.
assertTrue
(
'Rules Violation Comments'
in
report
[
3
])
self
.
assertEqual
(
len
(
report
[
3
][
'Rules Violation Comments'
]),
1
)
self
.
assertTrue
(
'Suspicious Comments'
in
report
[
3
])
self
.
assertEqual
(
len
(
report
[
3
][
'Suspicious Comments'
]),
2
)
self
.
assertEqual
(
report
[
3
][
'review_status'
],
'Suspicious'
)
self
.
assertTrue
(
'Suspicious Comments'
not
in
report
[
2
])
self
.
assertTrue
(
'Rules Violation Comments'
in
report
[
2
])
self
.
assertEqual
(
len
(
report
[
2
][
'Rules Violation Comments'
]),
1
)
self
.
assertEqual
(
report
[
2
][
'review_status'
],
'Rules Violation'
)
self
.
assertEqual
(
report
[
1
][
'review_status'
],
'Clean'
)
self
.
assertIsNone
(
report
[
0
][
'review_status'
])
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