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
a76cbb49
Commit
a76cbb49
authored
Jul 20, 2017
by
Tyler Hallada
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Override exam grade w/ service on review rejected
parent
580220ce
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
79 additions
and
1 deletions
+79
-1
edx_proctoring/api.py
+25
-0
edx_proctoring/backends/tests/test_software_secure.py
+3
-1
edx_proctoring/management/commands/tests/test_set_attempt_status.py
+2
-0
edx_proctoring/models.py
+9
-0
edx_proctoring/tests/test_api.py
+23
-0
edx_proctoring/tests/test_services.py
+17
-0
No files found.
edx_proctoring/api.py
View file @
a76cbb49
...
@@ -58,6 +58,8 @@ SHOW_EXPIRY_MESSAGE_DURATION = 1 * 60 # duration within which expiry message is
...
@@ -58,6 +58,8 @@ SHOW_EXPIRY_MESSAGE_DURATION = 1 * 60 # duration within which expiry message is
APPROVED_STATUS
=
'approved'
APPROVED_STATUS
=
'approved'
REJECTED_GRADE_OVERRIDE_SCORE
=
0
def
create_exam
(
course_id
,
content_id
,
exam_name
,
time_limit_mins
,
due_date
=
None
,
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
,
hide_after_due
=
False
):
is_proctored
=
True
,
is_practice_exam
=
False
,
external_id
=
None
,
is_active
=
True
,
hide_after_due
=
False
):
...
@@ -888,6 +890,29 @@ def update_attempt_status(exam_id, user_id, to_status,
...
@@ -888,6 +890,29 @@ def update_attempt_status(exam_id, user_id, to_status,
cascade_effects
=
False
cascade_effects
=
False
)
)
if
ProctoredExamStudentAttemptStatus
.
needs_grade_override
(
to_status
):
grades_service
=
get_runtime_service
(
'grades'
)
log_msg
=
(
'Overriding exam subsection grade for '
'user_id {user_id} on {course_id} for '
'content_id {content_id}. Override '
'score: {score}'
.
format
(
user_id
=
exam_attempt_obj
.
user_id
,
course_id
=
exam
[
'course_id'
],
content_id
=
exam_attempt_obj
.
proctored_exam
.
content_id
,
score
=
REJECTED_GRADE_OVERRIDE_SCORE
)
)
log
.
info
(
log_msg
)
grades_service
.
override_subsection_grade
(
user_id
=
exam_attempt_obj
.
user_id
,
course_key_or_id
=
exam
[
'course_id'
],
subsection
=
exam_attempt_obj
.
proctored_exam
.
content_id
,
score
=
REJECTED_GRADE_OVERRIDE_SCORE
)
# call service to get course name.
# call service to get course name.
credit_service
=
get_runtime_service
(
'credit'
)
credit_service
=
get_runtime_service
(
'credit'
)
credit_state
=
credit_service
.
get_credit_state
(
credit_state
=
credit_service
.
get_credit_state
(
...
...
edx_proctoring/backends/tests/test_software_secure.py
View file @
a76cbb49
...
@@ -43,7 +43,7 @@ from edx_proctoring.models import (
...
@@ -43,7 +43,7 @@ from edx_proctoring.models import (
ProctoredExamStudentAllowance
ProctoredExamStudentAllowance
)
)
from
edx_proctoring.backends.tests.test_review_payload
import
create_test_review_payload
from
edx_proctoring.backends.tests.test_review_payload
import
create_test_review_payload
from
edx_proctoring.tests.test_services
import
MockCreditService
,
MockInstructorService
from
edx_proctoring.tests.test_services
import
MockCreditService
,
MockInstructorService
,
MockGradesService
from
edx_proctoring.backends.software_secure
import
SOFTWARE_SECURE_INVALID_CHARS
from
edx_proctoring.backends.software_secure
import
SOFTWARE_SECURE_INVALID_CHARS
...
@@ -104,6 +104,7 @@ class SoftwareSecureTests(TestCase):
...
@@ -104,6 +104,7 @@ class SoftwareSecureTests(TestCase):
set_runtime_service
(
'credit'
,
MockCreditService
())
set_runtime_service
(
'credit'
,
MockCreditService
())
set_runtime_service
(
'instructor'
,
MockInstructorService
())
set_runtime_service
(
'instructor'
,
MockInstructorService
())
set_runtime_service
(
'grades'
,
MockGradesService
())
def
tearDown
(
self
):
def
tearDown
(
self
):
"""
"""
...
@@ -111,6 +112,7 @@ class SoftwareSecureTests(TestCase):
...
@@ -111,6 +112,7 @@ class SoftwareSecureTests(TestCase):
"""
"""
super
(
SoftwareSecureTests
,
self
)
.
tearDown
()
super
(
SoftwareSecureTests
,
self
)
.
tearDown
()
set_runtime_service
(
'credit'
,
None
)
set_runtime_service
(
'credit'
,
None
)
set_runtime_service
(
'grades'
,
None
)
def
test_provider_instance
(
self
):
def
test_provider_instance
(
self
):
"""
"""
...
...
edx_proctoring/management/commands/tests/test_set_attempt_status.py
View file @
a76cbb49
...
@@ -15,6 +15,7 @@ from edx_proctoring.management.commands import set_attempt_status
...
@@ -15,6 +15,7 @@ from edx_proctoring.management.commands import set_attempt_status
from
edx_proctoring.models
import
ProctoredExamStudentAttemptStatus
,
ProctoredExamStudentAttempt
from
edx_proctoring.models
import
ProctoredExamStudentAttemptStatus
,
ProctoredExamStudentAttempt
from
edx_proctoring.tests.test_services
import
(
from
edx_proctoring.tests.test_services
import
(
MockCreditService
,
MockCreditService
,
MockGradesService
)
)
from
edx_proctoring.runtime
import
set_runtime_service
from
edx_proctoring.runtime
import
set_runtime_service
...
@@ -31,6 +32,7 @@ class SetAttemptStatusTests(LoggedInTestCase):
...
@@ -31,6 +32,7 @@ class SetAttemptStatusTests(LoggedInTestCase):
"""
"""
super
(
SetAttemptStatusTests
,
self
)
.
setUp
()
super
(
SetAttemptStatusTests
,
self
)
.
setUp
()
set_runtime_service
(
'credit'
,
MockCreditService
())
set_runtime_service
(
'credit'
,
MockCreditService
())
set_runtime_service
(
'grades'
,
MockGradesService
())
self
.
exam_id
=
create_exam
(
self
.
exam_id
=
create_exam
(
course_id
=
'foo'
,
course_id
=
'foo'
,
content_id
=
'bar'
,
content_id
=
'bar'
,
...
...
edx_proctoring/models.py
View file @
a76cbb49
...
@@ -214,6 +214,15 @@ class ProctoredExamStudentAttemptStatus(object):
...
@@ -214,6 +214,15 @@ class ProctoredExamStudentAttemptStatus(object):
]
]
@classmethod
@classmethod
def
needs_grade_override
(
cls
,
to_status
):
"""
Returns a boolean if the passed in to_status calls for an override of the learner's grade.
"""
return
to_status
in
[
cls
.
rejected
]
@classmethod
def
is_a_cascadable_failure
(
cls
,
to_status
):
def
is_a_cascadable_failure
(
cls
,
to_status
):
"""
"""
Returns a boolean if the passed in to_status has a failure that needs to be cascaded
Returns a boolean if the passed in to_status has a failure that needs to be cascaded
...
...
edx_proctoring/tests/test_api.py
View file @
a76cbb49
...
@@ -71,6 +71,7 @@ from .test_services import (
...
@@ -71,6 +71,7 @@ from .test_services import (
MockCreditService
,
MockCreditService
,
MockCreditServiceNone
,
MockCreditServiceNone
,
MockCreditServiceWithCourseEndDate
,
MockCreditServiceWithCourseEndDate
,
MockGradesService
)
)
from
.utils
import
ProctoredExamTestCase
from
.utils
import
ProctoredExamTestCase
...
@@ -844,6 +845,7 @@ class ProctoredExamApiTests(ProctoredExamTestCase):
...
@@ -844,6 +845,7 @@ class ProctoredExamApiTests(ProctoredExamTestCase):
are auto marked as declined
are auto marked as declined
"""
"""
set_runtime_service
(
'grades'
,
MockGradesService
())
# create other exams in course
# create other exams in course
second_exam_id
=
create_exam
(
second_exam_id
=
create_exam
(
course_id
=
self
.
course_id
,
course_id
=
self
.
course_id
,
...
@@ -912,6 +914,26 @@ class ProctoredExamApiTests(ProctoredExamTestCase):
...
@@ -912,6 +914,26 @@ class ProctoredExamApiTests(ProctoredExamTestCase):
self
.
assertIsNone
(
get_exam_attempt
(
timed_exam_id
,
self
.
user_id
))
self
.
assertIsNone
(
get_exam_attempt
(
timed_exam_id
,
self
.
user_id
))
self
.
assertIsNone
(
get_exam_attempt
(
inactive_exam_id
,
self
.
user_id
))
self
.
assertIsNone
(
get_exam_attempt
(
inactive_exam_id
,
self
.
user_id
))
def
test_grade_override
(
self
):
"""
Verify that putting an attempt into the rejected state will also override
the learner's subsection grade for the exam
"""
set_runtime_service
(
'grades'
,
MockGradesService
())
exam_attempt
=
self
.
_create_started_exam_attempt
()
update_attempt_status
(
exam_attempt
.
proctored_exam_id
,
self
.
user
.
id
,
ProctoredExamStudentAttemptStatus
.
rejected
)
grades_service
=
get_runtime_service
(
'grades'
)
grades_status
=
grades_service
.
get_subsection_grade
(
user_id
=
self
.
user
.
id
,
course_key_or_id
=
exam_attempt
.
proctored_exam
.
course_id
,
subsection
=
exam_attempt
.
proctored_exam
.
content_id
)
self
.
assertEqual
(
grades_status
,
0
)
@ddt.data
(
@ddt.data
(
(
ProctoredExamStudentAttemptStatus
.
declined
,
ProctoredExamStudentAttemptStatus
.
eligible
),
(
ProctoredExamStudentAttemptStatus
.
declined
,
ProctoredExamStudentAttemptStatus
.
eligible
),
(
ProctoredExamStudentAttemptStatus
.
timed_out
,
ProctoredExamStudentAttemptStatus
.
created
),
(
ProctoredExamStudentAttemptStatus
.
timed_out
,
ProctoredExamStudentAttemptStatus
.
created
),
...
@@ -1141,6 +1163,7 @@ class ProctoredExamApiTests(ProctoredExamTestCase):
...
@@ -1141,6 +1163,7 @@ class ProctoredExamApiTests(ProctoredExamTestCase):
Assert that we get the expected status summaries
Assert that we get the expected status summaries
"""
"""
set_runtime_service
(
'grades'
,
MockGradesService
())
exam_attempt
=
self
.
_create_started_exam_attempt
()
exam_attempt
=
self
.
_create_started_exam_attempt
()
update_attempt_status
(
update_attempt_status
(
exam_attempt
.
proctored_exam_id
,
exam_attempt
.
proctored_exam_id
,
...
...
edx_proctoring/tests/test_services.py
View file @
a76cbb49
...
@@ -173,3 +173,20 @@ class TestProctoringService(unittest.TestCase):
...
@@ -173,3 +173,20 @@ class TestProctoringService(unittest.TestCase):
service1
=
ProctoringService
()
service1
=
ProctoringService
()
service2
=
ProctoringService
()
service2
=
ProctoringService
()
self
.
assertIs
(
service1
,
service2
)
self
.
assertIs
(
service1
,
service2
)
class
MockGradesService
(
object
):
"""
Simple mock of the Grades Service
"""
def
__init__
(
self
):
"""Initialize empty data store for grades (a dict)"""
self
.
grades
=
{}
def
get_subsection_grade
(
self
,
user_id
,
course_key_or_id
,
subsection
):
"""Returns entered grade override for key (user_id + course_key + subsection) or None"""
return
self
.
grades
.
get
(
str
(
user_id
)
+
str
(
course_key_or_id
)
+
str
(
subsection
))
def
override_subsection_grade
(
self
,
user_id
,
course_key_or_id
,
subsection
,
score
):
"""Sets grade override score for key (user_id + course_key + subsection)"""
self
.
grades
[
str
(
user_id
)
+
str
(
course_key_or_id
)
+
str
(
subsection
)]
=
score
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