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
aae446e6
Commit
aae446e6
authored
Jul 23, 2015
by
chrisndodge
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #34 from edx/cdodge/pass-user-name
Cdodge/pass user name
parents
85b670d9
e01228a7
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
116 additions
and
31 deletions
+116
-31
edx_proctoring/api.py
+16
-4
edx_proctoring/backends/backend.py
+1
-2
edx_proctoring/backends/null.py
+1
-2
edx_proctoring/backends/software_secure.py
+22
-8
edx_proctoring/backends/tests/test_backend.py
+5
-10
edx_proctoring/backends/tests/test_software_secure.py
+41
-2
edx_proctoring/runtime.py
+21
-0
edx_proctoring/templates/proctoring/seq_proctored_exam_instructions.html
+6
-2
edx_proctoring/tests/test_services.py
+3
-1
No files found.
edx_proctoring/api.py
View file @
aae446e6
...
@@ -34,6 +34,7 @@ from edx_proctoring.serializers import (
...
@@ -34,6 +34,7 @@ from edx_proctoring.serializers import (
from
edx_proctoring.utils
import
humanized_time
from
edx_proctoring.utils
import
humanized_time
from
edx_proctoring.backends
import
get_backend_provider
from
edx_proctoring.backends
import
get_backend_provider
from
edx_proctoring.runtime
import
get_runtime_service
def
is_feature_enabled
():
def
is_feature_enabled
():
...
@@ -235,13 +236,24 @@ def create_exam_attempt(exam_id, user_id, taking_as_proctored=False):
...
@@ -235,13 +236,24 @@ def create_exam_attempt(exam_id, user_id, taking_as_proctored=False):
)
)
)
)
# get the name of the user, if the service is available
full_name
=
None
profile_service
=
get_runtime_service
(
'profile'
)
if
profile_service
:
profile
=
profile_service
(
user_id
)
full_name
=
profile
[
'name'
]
# now call into the backend provider to register exam attempt
# now call into the backend provider to register exam attempt
external_id
=
get_backend_provider
()
.
register_exam_attempt
(
external_id
=
get_backend_provider
()
.
register_exam_attempt
(
exam
,
exam
,
allowed_time_limit_mins
,
context
=
{
attempt_code
,
'time_limit_mins'
:
allowed_time_limit_mins
,
False
,
'attempt_code'
:
attempt_code
,
callback_url
'is_sample_attempt'
:
False
,
'callback_url'
:
callback_url
,
'full_name'
:
full_name
,
}
)
)
attempt
=
ProctoredExamStudentAttempt
.
create_exam_attempt
(
attempt
=
ProctoredExamStudentAttempt
.
create_exam_attempt
(
...
...
edx_proctoring/backends/backend.py
View file @
aae446e6
...
@@ -14,8 +14,7 @@ class ProctoringBackendProvider(object):
...
@@ -14,8 +14,7 @@ class ProctoringBackendProvider(object):
__metaclass__
=
abc
.
ABCMeta
__metaclass__
=
abc
.
ABCMeta
@abc.abstractmethod
@abc.abstractmethod
def
register_exam_attempt
(
self
,
exam
,
time_limit_mins
,
attempt_code
,
def
register_exam_attempt
(
self
,
exam
,
context
):
is_sample_attempt
,
callback_url
):
"""
"""
Called when the exam attempt has been created but not started
Called when the exam attempt has been created but not started
"""
"""
...
...
edx_proctoring/backends/null.py
View file @
aae446e6
...
@@ -10,8 +10,7 @@ class NullBackendProvider(ProctoringBackendProvider):
...
@@ -10,8 +10,7 @@ class NullBackendProvider(ProctoringBackendProvider):
Implementation of the ProctoringBackendProvider that does nothing
Implementation of the ProctoringBackendProvider that does nothing
"""
"""
def
register_exam_attempt
(
self
,
exam
,
time_limit_mins
,
attempt_code
,
def
register_exam_attempt
(
self
,
exam
,
context
):
is_sample_attempt
,
callback_url
):
"""
"""
Called when the exam attempt has been created but not started
Called when the exam attempt has been created but not started
"""
"""
...
...
edx_proctoring/backends/software_secure.py
View file @
aae446e6
...
@@ -40,19 +40,17 @@ class SoftwareSecureBackendProvider(ProctoringBackendProvider):
...
@@ -40,19 +40,17 @@ class SoftwareSecureBackendProvider(ProctoringBackendProvider):
self
.
timeout
=
10
self
.
timeout
=
10
self
.
software_download_url
=
software_download_url
self
.
software_download_url
=
software_download_url
def
register_exam_attempt
(
self
,
exam
,
time_limit_mins
,
attempt_code
,
def
register_exam_attempt
(
self
,
exam
,
context
):
is_sample_attempt
,
callback_url
):
"""
"""
Method that is responsible for communicating with the backend provider
Method that is responsible for communicating with the backend provider
to establish a new proctored exam
to establish a new proctored exam
"""
"""
attempt_code
=
context
[
'attempt_code'
]
data
=
self
.
_get_payload
(
data
=
self
.
_get_payload
(
exam
,
exam
,
time_limit_mins
,
context
attempt_code
,
is_sample_attempt
,
callback_url
)
)
headers
=
{
headers
=
{
"Content-Type"
:
'application/json'
"Content-Type"
:
'application/json'
...
@@ -114,11 +112,25 @@ class SoftwareSecureBackendProvider(ProctoringBackendProvider):
...
@@ -114,11 +112,25 @@ class SoftwareSecureBackendProvider(ProctoringBackendProvider):
encrypted_text
=
cipher
.
encrypt
(
pad
(
pwd
))
encrypted_text
=
cipher
.
encrypt
(
pad
(
pwd
))
return
base64
.
b64encode
(
encrypted_text
)
return
base64
.
b64encode
(
encrypted_text
)
def
_get_payload
(
self
,
exam
,
time_limit_mins
,
attempt_code
,
def
_get_payload
(
self
,
exam
,
context
):
is_sample_attempt
,
callback_url
):
"""
"""
Constructs the data payload that Software Secure expects
Constructs the data payload that Software Secure expects
"""
"""
attempt_code
=
context
[
'attempt_code'
]
time_limit_mins
=
context
[
'time_limit_mins'
]
is_sample_attempt
=
context
[
'is_sample_attempt'
]
callback_url
=
context
[
'callback_url'
]
full_name
=
context
[
'full_name'
]
first_name
=
''
last_name
=
''
if
full_name
:
name_elements
=
full_name
.
split
(
' '
)
first_name
=
name_elements
[
0
]
if
len
(
name_elements
)
>
1
:
last_name
=
' '
.
join
(
name_elements
[
1
:])
now
=
datetime
.
datetime
.
utcnow
()
now
=
datetime
.
datetime
.
utcnow
()
start_time_str
=
now
.
strftime
(
"
%
a,
%
d
%
b
%
Y
%
H:
%
M:
%
S GMT"
)
start_time_str
=
now
.
strftime
(
"
%
a,
%
d
%
b
%
Y
%
H:
%
M:
%
S GMT"
)
end_time_str
=
(
now
+
datetime
.
timedelta
(
minutes
=
time_limit_mins
))
.
strftime
(
"
%
a,
%
d
%
b
%
Y
%
H:
%
M:
%
S GMT"
)
end_time_str
=
(
now
+
datetime
.
timedelta
(
minutes
=
time_limit_mins
))
.
strftime
(
"
%
a,
%
d
%
b
%
Y
%
H:
%
M:
%
S GMT"
)
...
@@ -140,6 +152,8 @@ class SoftwareSecureBackendProvider(ProctoringBackendProvider):
...
@@ -140,6 +152,8 @@ class SoftwareSecureBackendProvider(ProctoringBackendProvider):
"noOfStudents"
:
1
,
"noOfStudents"
:
1
,
"examID"
:
exam
[
'id'
],
"examID"
:
exam
[
'id'
],
"courseID"
:
exam
[
'course_id'
],
"courseID"
:
exam
[
'course_id'
],
"firstName"
:
first_name
,
"lastName"
:
last_name
,
}
}
}
}
...
...
edx_proctoring/backends/tests/test_backend.py
View file @
aae446e6
...
@@ -12,8 +12,7 @@ class TestBackendProvider(ProctoringBackendProvider):
...
@@ -12,8 +12,7 @@ class TestBackendProvider(ProctoringBackendProvider):
Implementation of the ProctoringBackendProvider that does nothing
Implementation of the ProctoringBackendProvider that does nothing
"""
"""
def
register_exam_attempt
(
self
,
exam
,
time_limit_mins
,
attempt_code
,
def
register_exam_attempt
(
self
,
exam
,
context
):
is_sample_attempt
,
callback_url
):
"""
"""
Called when the exam attempt has been created but not started
Called when the exam attempt has been created but not started
"""
"""
...
@@ -46,17 +45,13 @@ class PassthroughBackendProvider(ProctoringBackendProvider):
...
@@ -46,17 +45,13 @@ class PassthroughBackendProvider(ProctoringBackendProvider):
Implementation of the ProctoringBackendProvider that just calls the base class
Implementation of the ProctoringBackendProvider that just calls the base class
"""
"""
def
register_exam_attempt
(
self
,
exam
,
time_limit_mins
,
attempt_code
,
def
register_exam_attempt
(
self
,
exam
,
context
):
is_sample_attempt
,
callback_url
):
"""
"""
Called when the exam attempt has been created but not started
Called when the exam attempt has been created but not started
"""
"""
return
super
(
PassthroughBackendProvider
,
self
)
.
register_exam_attempt
(
return
super
(
PassthroughBackendProvider
,
self
)
.
register_exam_attempt
(
exam
,
exam
,
time_limit_mins
,
context
attempt_code
,
is_sample_attempt
,
callback_url
)
)
def
start_exam_attempt
(
self
,
exam
,
attempt
):
def
start_exam_attempt
(
self
,
exam
,
attempt
):
...
@@ -100,7 +95,7 @@ class TestBackends(TestCase):
...
@@ -100,7 +95,7 @@ class TestBackends(TestCase):
provider
=
PassthroughBackendProvider
()
provider
=
PassthroughBackendProvider
()
with
self
.
assertRaises
(
NotImplementedError
):
with
self
.
assertRaises
(
NotImplementedError
):
provider
.
register_exam_attempt
(
None
,
None
,
None
,
None
,
None
)
provider
.
register_exam_attempt
(
None
,
None
)
with
self
.
assertRaises
(
NotImplementedError
):
with
self
.
assertRaises
(
NotImplementedError
):
provider
.
start_exam_attempt
(
None
,
None
)
provider
.
start_exam_attempt
(
None
,
None
)
...
@@ -118,7 +113,7 @@ class TestBackends(TestCase):
...
@@ -118,7 +113,7 @@ class TestBackends(TestCase):
provider
=
NullBackendProvider
()
provider
=
NullBackendProvider
()
self
.
assertIsNone
(
provider
.
register_exam_attempt
(
None
,
None
,
None
,
None
,
None
))
self
.
assertIsNone
(
provider
.
register_exam_attempt
(
None
,
None
))
self
.
assertIsNone
(
provider
.
start_exam_attempt
(
None
,
None
))
self
.
assertIsNone
(
provider
.
start_exam_attempt
(
None
,
None
))
self
.
assertIsNone
(
provider
.
stop_exam_attempt
(
None
,
None
))
self
.
assertIsNone
(
provider
.
stop_exam_attempt
(
None
,
None
))
self
.
assertIsNone
(
provider
.
get_software_download_url
())
self
.
assertIsNone
(
provider
.
get_software_download_url
())
edx_proctoring/backends/tests/test_software_secure.py
View file @
aae446e6
...
@@ -8,13 +8,13 @@ import json
...
@@ -8,13 +8,13 @@ import json
from
django.test
import
TestCase
from
django.test
import
TestCase
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
edx_proctoring.runtime
import
set_runtime_service
from
edx_proctoring.backends
import
get_backend_provider
from
edx_proctoring.backends
import
get_backend_provider
from
edx_proctoring.exceptions
import
BackendProvideCannotRegisterAttempt
from
edx_proctoring.exceptions
import
BackendProvideCannotRegisterAttempt
from
edx_proctoring.api
import
get_exam_attempt_by_id
from
edx_proctoring.api
import
(
from
edx_proctoring.api
import
(
get_exam_attempt_by_id
,
create_exam
,
create_exam
,
create_exam_attempt
,
create_exam_attempt
,
)
)
...
@@ -71,6 +71,20 @@ class SoftwareSecureTests(TestCase):
...
@@ -71,6 +71,20 @@ class SoftwareSecureTests(TestCase):
self
.
user
=
User
(
username
=
'foo'
,
email
=
'foo@bar.com'
)
self
.
user
=
User
(
username
=
'foo'
,
email
=
'foo@bar.com'
)
self
.
user
.
save
()
self
.
user
.
save
()
def
mock_profile_service
(
user_id
):
# pylint: disable=unused-argument
"""
Mocked out Profile callback endpoint
"""
return
{
'name'
:
'Wolfgang von Strucker'
}
set_runtime_service
(
'profile'
,
mock_profile_service
)
def
tearDown
(
self
):
"""
When tests are done
"""
set_runtime_service
(
'profile'
,
None
)
def
test_provider_instance
(
self
):
def
test_provider_instance
(
self
):
"""
"""
Makes sure the instance of the proctoring module can be created
Makes sure the instance of the proctoring module can be created
...
@@ -108,6 +122,31 @@ class SoftwareSecureTests(TestCase):
...
@@ -108,6 +122,31 @@ class SoftwareSecureTests(TestCase):
self
.
assertEqual
(
attempt
[
'external_id'
],
'foobar'
)
self
.
assertEqual
(
attempt
[
'external_id'
],
'foobar'
)
self
.
assertIsNone
(
attempt
[
'started_at'
])
self
.
assertIsNone
(
attempt
[
'started_at'
])
def
test_single_name_attempt
(
self
):
"""
Tests to make sure we can parse a fullname which does not have any spaces in it
"""
def
mock_profile_service
(
user_id
):
# pylint: disable=unused-argument
"""
Mocked out Profile callback endpoint
"""
return
{
'name'
:
'Bono'
}
set_runtime_service
(
'profile'
,
mock_profile_service
)
exam_id
=
create_exam
(
course_id
=
'foo/bar/baz'
,
content_id
=
'content'
,
exam_name
=
'Sample Exam'
,
time_limit_mins
=
10
,
is_proctored
=
True
)
with
HTTMock
(
response_content
):
attempt_id
=
create_exam_attempt
(
exam_id
,
self
.
user
.
id
,
taking_as_proctored
=
True
)
self
.
assertIsNotNone
(
attempt_id
)
def
test_failing_register_attempt
(
self
):
def
test_failing_register_attempt
(
self
):
"""
"""
Makes sure we can register an attempt
Makes sure we can register an attempt
...
...
edx_proctoring/runtime.py
0 → 100644
View file @
aae446e6
"""
Runtime services that the LMS can register than we can callback on
"""
_RUNTIME_SERVICES
=
{}
def
set_runtime_service
(
name
,
callback
):
"""
Adds a service provided by the runtime (aka LMS) to our directory
"""
_RUNTIME_SERVICES
[
name
]
=
callback
def
get_runtime_service
(
name
):
"""
Returns a registered runtime service, None if no match is found
"""
return
_RUNTIME_SERVICES
.
get
(
name
)
edx_proctoring/templates/proctoring/seq_proctored_exam_instructions.html
View file @
aae446e6
...
@@ -26,8 +26,12 @@
...
@@ -26,8 +26,12 @@
</div>
</div>
</div>
</div>
<div
class=
"footer-sequence border-b-0 padding-b-0"
>
<div
class=
"footer-sequence border-b-0 padding-b-0"
>
<span>
{% trans "Note: As soon as you finish installing and setting up the proctoring software,
<span>
you will be prompted to start your timed exam." %}
</span>
{% blocktrans %}
Note: As soon as you finish installing and setting up the proctoring software,
you will be prompted to start your timed exam.
{% endblocktrans %}
</span>
<p>
<p>
{% blocktrans %}
{% blocktrans %}
Be prepared to start your exam and to complete it while adhering to the {{platform_name}}
Be prepared to start your exam and to complete it while adhering to the {{platform_name}}
...
...
edx_proctoring/tests/test_services.py
View file @
aae446e6
...
@@ -3,7 +3,9 @@ Test for the xBlock service
...
@@ -3,7 +3,9 @@ Test for the xBlock service
"""
"""
import
unittest
import
unittest
from
edx_proctoring.services
import
ProctoringService
from
edx_proctoring.services
import
(
ProctoringService
)
from
edx_proctoring
import
api
as
edx_proctoring_api
from
edx_proctoring
import
api
as
edx_proctoring_api
import
types
import
types
...
...
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