Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
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-platform
Commits
dda92d13
Commit
dda92d13
authored
Oct 05, 2016
by
Andy Armstrong
Committed by
GitHub
Oct 05, 2016
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #13518 from edx/andya/proctoring-verification-step
Introduce id verification step for proctored exams
parents
ce1eb237
ecf4515b
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
130 additions
and
7 deletions
+130
-7
cms/djangoapps/contentstore/signals.py
+9
-1
common/lib/xmodule/xmodule/seq_module.py
+12
-2
common/test/acceptance/pages/lms/pay_and_verify.py
+34
-0
common/test/acceptance/tests/lms/test_lms_courseware.py
+26
-1
lms/djangoapps/courseware/module_render.py
+3
-2
lms/djangoapps/courseware/tests/test_module_render.py
+4
-0
lms/djangoapps/verify_student/services.py
+32
-0
lms/envs/bok_choy.py
+9
-0
requirements/edx/github.txt
+1
-1
No files found.
cms/djangoapps/contentstore/signals.py
View file @
dda92d13
""" receivers of course_published and library_updated events in order to trigger indexing task """
import
logging
from
datetime
import
datetime
from
pytz
import
UTC
...
...
@@ -13,6 +14,9 @@ from openedx.core.lib.gating import api as gating_api
from
util.module_utils
import
yield_dynamic_descriptor_descendants
log
=
logging
.
getLogger
(
__name__
)
@receiver
(
SignalHandler
.
course_published
)
def
listen_for_course_publish
(
sender
,
course_key
,
**
kwargs
):
# pylint: disable=unused-argument
"""
...
...
@@ -23,7 +27,11 @@ def listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable=
# first is to registered exams, the credit subsystem will assume that
# all proctored exams have already been registered, so we have to do that first
register_special_exams
(
course_key
)
try
:
register_special_exams
(
course_key
)
# pylint: disable=broad-except
except
Exception
as
exception
:
log
.
exception
(
exception
)
# then call into the credit subsystem (in /openedx/djangoapps/credit)
# to perform any 'on_publish' workflow
...
...
common/lib/xmodule/xmodule/seq_module.py
View file @
dda92d13
...
...
@@ -137,10 +137,11 @@ class ProctoringFields(object):
@XBlock.wants
(
'proctoring'
)
@XBlock.wants
(
'verification'
)
@XBlock.wants
(
'milestones'
)
@XBlock.wants
(
'credit'
)
@XBlock.needs
(
"user"
)
@XBlock.needs
(
"bookmarks"
)
@XBlock.needs
(
'user'
)
@XBlock.needs
(
'bookmarks'
)
class
SequenceModule
(
SequenceFields
,
ProctoringFields
,
XModule
):
"""
Layout module which lays out content in a temporal sequence
...
...
@@ -433,6 +434,7 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
proctoring_service
=
self
.
runtime
.
service
(
self
,
'proctoring'
)
credit_service
=
self
.
runtime
.
service
(
self
,
'credit'
)
verification_service
=
self
.
runtime
.
service
(
self
,
'verification'
)
# Is this sequence designated as a Timed Examination, which includes
# Proctored Exams
...
...
@@ -465,6 +467,14 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
'credit_state'
:
credit_state
})
# inject verification status
if
verification_service
:
verification_status
,
__
=
verification_service
.
get_status
(
user_id
)
context
.
update
({
'verification_status'
:
verification_status
,
'reverify_url'
:
verification_service
.
reverify_url
(),
})
# See if the edx-proctoring subsystem wants to present
# a special view to the student rather
# than the actual sequence content
...
...
common/test/acceptance/pages/lms/pay_and_verify.py
View file @
dda92d13
...
...
@@ -161,3 +161,37 @@ class FakePaymentPage(PageObject):
self
.
q
(
css
=
"input[value='Submit']"
)
.
click
()
return
PaymentAndVerificationFlow
(
self
.
browser
,
self
.
_course_id
,
entry_point
=
'payment-confirmation'
)
.
wait_for_page
()
class
FakeSoftwareSecureVerificationPage
(
PageObject
):
"""
This page is a page used for testing that allows the user to change the status of their most recent
verification.
"""
url
=
BASE_URL
+
'/verify_student/software-secure-fake-response'
def
__init__
(
self
,
browser
):
super
(
FakeSoftwareSecureVerificationPage
,
self
)
.
__init__
(
browser
)
def
is_browser_on_page
(
self
):
""" Determine if browser is on the page. """
message
=
self
.
q
(
css
=
'BODY'
)
.
text
[
0
]
match
=
re
.
search
(
'Fake Software Secure page'
,
message
)
return
True
if
match
else
False
def
mark_approved
(
self
):
""" Mark the latest verification attempt as passing. """
self
.
q
(
css
=
'#btn_pass'
)
.
click
()
def
mark_denied
(
self
):
""" Mark the latest verification attempt as denied. """
self
.
q
(
css
=
'#btn_denied'
)
.
click
()
def
mark_error
(
self
):
""" Mark the latest verification attempt as an error. """
self
.
q
(
css
=
'#btn_error'
)
.
click
()
def
mark_unkown_error
(
self
):
""" Mark the latest verification attempt as an unknown error. """
self
.
q
(
css
=
'#btn_unkonwn_error'
)
.
click
()
common/test/acceptance/tests/lms/test_lms_courseware.py
View file @
dda92d13
...
...
@@ -17,7 +17,7 @@ from ...pages.lms.course_nav import CourseNavPage
from
...pages.lms.courseware
import
CoursewarePage
,
CoursewareSequentialTabPage
from
...pages.lms.create_mode
import
ModeCreationPage
from
...pages.lms.dashboard
import
DashboardPage
from
...pages.lms.pay_and_verify
import
PaymentAndVerificationFlow
,
FakePaymentPage
from
...pages.lms.pay_and_verify
import
PaymentAndVerificationFlow
,
FakePaymentPage
,
FakeSoftwareSecureVerificationPage
from
...pages.lms.problem
import
ProblemPage
from
...pages.lms.progress
import
ProgressPage
from
...pages.lms.staff_view
import
StaffPage
...
...
@@ -201,6 +201,28 @@ class ProctoredExamTest(UniqueCourseTest):
# Submit payment
self
.
fake_payment_page
.
submit_payment
()
def
_verify_user
(
self
):
"""
Takes user through the verification flow and then marks the verification as 'approved'.
"""
# Immediately verify the user
self
.
immediate_verification_page
.
immediate_verification
()
# Take face photo and proceed to the ID photo step
self
.
payment_and_verification_flow
.
webcam_capture
()
self
.
payment_and_verification_flow
.
next_verification_step
(
self
.
immediate_verification_page
)
# Take ID photo and proceed to the review photos step
self
.
payment_and_verification_flow
.
webcam_capture
()
self
.
payment_and_verification_flow
.
next_verification_step
(
self
.
immediate_verification_page
)
# Submit photos and proceed to the enrollment confirmation step
self
.
payment_and_verification_flow
.
next_verification_step
(
self
.
immediate_verification_page
)
# Mark the verification as passing.
verification
=
FakeSoftwareSecureVerificationPage
(
self
.
browser
)
.
visit
()
verification
.
mark_approved
()
def
test_can_create_proctored_exam_in_studio
(
self
):
"""
Given that I am a staff member
...
...
@@ -221,6 +243,7 @@ class ProctoredExamTest(UniqueCourseTest):
select advanced settings tab
When I Make the exam proctored.
And I login as a verified student.
And I verify the user's ID.
And visit the courseware as a verified student.
Then I can see an option to take the exam as a proctored exam.
"""
...
...
@@ -235,6 +258,8 @@ class ProctoredExamTest(UniqueCourseTest):
LogoutPage
(
self
.
browser
)
.
visit
()
self
.
_login_as_a_verified_user
()
self
.
_verify_user
()
self
.
courseware_page
.
visit
()
self
.
assertTrue
(
self
.
courseware_page
.
can_start_proctored_exam
)
...
...
lms/djangoapps/courseware/module_render.py
View file @
dda92d13
...
...
@@ -51,7 +51,7 @@ from lms.djangoapps.lms_xblock.field_data import LmsFieldData
from
lms.djangoapps.lms_xblock.models
import
XBlockAsidesConfig
from
openedx.core.djangoapps.bookmarks.services
import
BookmarksService
from
lms.djangoapps.lms_xblock.runtime
import
LmsModuleSystem
,
unquote_slashes
,
quote_slashes
from
lms.djangoapps.verify_student.services
import
ReverificationService
from
lms.djangoapps.verify_student.services
import
VerificationService
,
ReverificationService
from
openedx.core.djangoapps.credit.services
import
CreditService
from
openedx.core.djangoapps.util.user_utils
import
SystemUser
from
openedx.core.lib.xblock_utils
import
(
...
...
@@ -747,7 +747,8 @@ def get_module_system_for_user(user, student_data, # TODO # pylint: disable=to
'fs'
:
FSService
(),
'field-data'
:
field_data
,
'user'
:
DjangoXBlockUserService
(
user
,
user_is_staff
=
user_is_staff
),
"reverification"
:
ReverificationService
(),
'verification'
:
VerificationService
(),
'reverification'
:
ReverificationService
(),
'proctoring'
:
ProctoringService
(),
'milestones'
:
milestones_helpers
.
get_service
(),
'credit'
:
CreditService
(),
...
...
lms/djangoapps/courseware/tests/test_module_render.py
View file @
dda92d13
...
...
@@ -67,9 +67,11 @@ from edx_proctoring.api import (
)
from
edx_proctoring.runtime
import
set_runtime_service
from
edx_proctoring.tests.test_services
import
MockCreditService
from
verify_student.tests.factories
import
SoftwareSecurePhotoVerificationFactory
from
milestones.tests.utils
import
MilestonesTestCaseMixin
TEST_DATA_DIR
=
settings
.
COMMON_TEST_DATA_ROOT
...
...
@@ -740,6 +742,7 @@ class TestProctoringRendering(SharedModuleStoreTestCase):
self
.
request
=
factory
.
get
(
chapter_url
)
self
.
request
.
user
=
UserFactory
.
create
()
self
.
user
=
UserFactory
.
create
()
SoftwareSecurePhotoVerificationFactory
.
create
(
user
=
self
.
request
.
user
)
self
.
modulestore
=
self
.
store
.
_get_modulestore_for_courselike
(
self
.
course_key
)
# pylint: disable=protected-access
with
self
.
modulestore
.
bulk_operations
(
self
.
course_key
):
self
.
toy_course
=
self
.
store
.
get_course
(
self
.
course_key
,
depth
=
2
)
...
...
@@ -1020,6 +1023,7 @@ class TestProctoringRendering(SharedModuleStoreTestCase):
if
attempt_status
:
create_exam_attempt
(
exam_id
,
self
.
request
.
user
.
id
,
taking_as_proctored
=
True
)
update_attempt_status
(
exam_id
,
self
.
request
.
user
.
id
,
attempt_status
)
return
usage_key
def
_find_url_name
(
self
,
toc
,
url_name
):
...
...
lms/djangoapps/verify_student/services.py
View file @
dda92d13
...
...
@@ -13,10 +13,42 @@ from opaque_keys.edx.keys import CourseKey
from
student.models
import
User
,
CourseEnrollment
from
lms.djangoapps.verify_student.models
import
VerificationCheckpoint
,
VerificationStatus
,
SkippedReverification
from
.models
import
SoftwareSecurePhotoVerification
log
=
logging
.
getLogger
(
__name__
)
class
VerificationService
(
object
):
"""
Learner verification XBlock service
"""
def
get_status
(
self
,
user_id
):
"""
Returns the user's current photo verification status.
Args:
user_id: the user's id
Returns: one of the following strings
'none' - no such verification exists
'expired' - verification has expired
'approved' - verification has been approved
'pending' - verification process is still ongoing
'must_reverify' - verification has been denied and user must resubmit photos
"""
user
=
User
.
objects
.
get
(
id
=
user_id
)
# TODO: provide a photo verification abstraction so that this
# isn't hard-coded to use Software Secure.
return
SoftwareSecurePhotoVerification
.
user_status
(
user
)
def
reverify_url
(
self
):
"""
Returns the URL for a user to verify themselves.
"""
return
reverse
(
'verify_student_reverify'
)
class
ReverificationService
(
object
):
"""
Reverification XBlock service
...
...
lms/envs/bok_choy.py
View file @
dda92d13
...
...
@@ -132,6 +132,9 @@ FEATURES['LICENSING'] = True
# Use the auto_auth workflow for creating users and logging them in
FEATURES
[
'AUTOMATIC_AUTH_FOR_TESTING'
]
=
True
# Open up endpoint for faking Software Secure responses
FEATURES
[
'ENABLE_SOFTWARE_SECURE_FAKE'
]
=
True
########################### Entrance Exams #################################
FEATURES
[
'MILESTONES_APP'
]
=
True
FEATURES
[
'ENTRANCE_EXAMS'
]
=
True
...
...
@@ -176,6 +179,12 @@ MOCK_SEARCH_BACKING_FILE = (
TEST_ROOT
/
"index_file.dat"
)
.
abspath
()
# Verify student settings
VERIFY_STUDENT
[
"SOFTWARE_SECURE"
]
=
{
"API_ACCESS_KEY"
:
"BBBBBBBBBBBBBBBBBBBB"
,
"API_SECRET_KEY"
:
"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
,
}
# this secret key should be the same as cms/envs/bok_choy.py's
SECRET_KEY
=
"very_secret_bok_choy_key"
...
...
requirements/edx/github.txt
View file @
dda92d13
...
...
@@ -91,7 +91,7 @@ git+https://github.com/edx/xblock-utils.git@v1.0.3#egg=xblock-utils==1.0.3
-e git+https://github.com/edx/edx-reverification-block.git@0.0.5#egg=edx-reverification-block==0.0.5
git+https://github.com/edx/edx-user-state-client.git@1.0.1#egg=edx-user-state-client==1.0.1
git+https://github.com/edx/xblock-lti-consumer.git@v1.0.9#egg=xblock-lti-consumer==1.0.9
git+https://github.com/edx/edx-proctoring.git@0.1
3.0#egg=edx-proctoring==0.13
.0
git+https://github.com/edx/edx-proctoring.git@0.1
4.0#egg=edx-proctoring==0.14
.0
# Third Party XBlocks
-e git+https://github.com/mitodl/edx-sga@172a90fd2738f8142c10478356b2d9ed3e55334a#egg=edx-sga
...
...
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