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
d1c9b6a2
Commit
d1c9b6a2
authored
Jun 29, 2015
by
Afzal Wali
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added a test for create Exam REST API
parent
bf15198d
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
115 additions
and
12 deletions
+115
-12
edx_proctoring/models.py
+5
-5
edx_proctoring/serializers.py
+24
-0
edx_proctoring/tests/test_views.py
+73
-3
edx_proctoring/urls.py
+2
-2
edx_proctoring/views.py
+9
-1
settings.py
+2
-1
No files found.
edx_proctoring/models.py
View file @
d1c9b6a2
...
@@ -49,7 +49,7 @@ class ProctoredExam(TimeStampedModel):
...
@@ -49,7 +49,7 @@ class ProctoredExam(TimeStampedModel):
"""
"""
try
:
try
:
proctored_exam
=
cls
.
objects
.
get
(
course_id
=
course_id
,
content_id
=
content_id
)
proctored_exam
=
cls
.
objects
.
get
(
course_id
=
course_id
,
content_id
=
content_id
)
except
cls
.
DoesNotExist
:
except
cls
.
DoesNotExist
:
# pylint: disable=no-member
proctored_exam
=
None
proctored_exam
=
None
return
proctored_exam
return
proctored_exam
...
@@ -61,7 +61,7 @@ class ProctoredExam(TimeStampedModel):
...
@@ -61,7 +61,7 @@ class ProctoredExam(TimeStampedModel):
"""
"""
try
:
try
:
proctored_exam
=
cls
.
objects
.
get
(
id
=
exam_id
)
proctored_exam
=
cls
.
objects
.
get
(
id
=
exam_id
)
except
cls
.
DoesNotExist
:
except
cls
.
DoesNotExist
:
# pylint: disable=no-member
proctored_exam
=
None
proctored_exam
=
None
return
proctored_exam
return
proctored_exam
...
@@ -119,7 +119,7 @@ class ProctoredExamStudentAttempt(TimeStampedModel):
...
@@ -119,7 +119,7 @@ class ProctoredExamStudentAttempt(TimeStampedModel):
"""
"""
try
:
try
:
exam_attempt_obj
=
cls
.
objects
.
get
(
proctored_exam_id
=
exam_id
,
user_id
=
user_id
)
exam_attempt_obj
=
cls
.
objects
.
get
(
proctored_exam_id
=
exam_id
,
user_id
=
user_id
)
except
cls
.
DoesNotExist
:
except
cls
.
DoesNotExist
:
# pylint: disable=no-member
exam_attempt_obj
=
None
exam_attempt_obj
=
None
return
exam_attempt_obj
return
exam_attempt_obj
...
@@ -182,7 +182,7 @@ class ProctoredExamStudentAllowance(TimeStampedModel):
...
@@ -182,7 +182,7 @@ class ProctoredExamStudentAllowance(TimeStampedModel):
"""
"""
try
:
try
:
student_allowance
=
cls
.
objects
.
get
(
proctored_exam_id
=
exam_id
,
user_id
=
user_id
,
key
=
key
)
student_allowance
=
cls
.
objects
.
get
(
proctored_exam_id
=
exam_id
,
user_id
=
user_id
,
key
=
key
)
except
cls
.
DoesNotExist
:
except
cls
.
DoesNotExist
:
# pylint: disable=no-member
student_allowance
=
None
student_allowance
=
None
return
student_allowance
return
student_allowance
...
@@ -202,7 +202,7 @@ class ProctoredExamStudentAllowance(TimeStampedModel):
...
@@ -202,7 +202,7 @@ class ProctoredExamStudentAllowance(TimeStampedModel):
student_allowance
=
cls
.
objects
.
get
(
proctored_exam_id
=
exam_id
,
user_id
=
user_id
,
key
=
key
)
student_allowance
=
cls
.
objects
.
get
(
proctored_exam_id
=
exam_id
,
user_id
=
user_id
,
key
=
key
)
student_allowance
.
value
=
value
student_allowance
.
value
=
value
student_allowance
.
save
()
student_allowance
.
save
()
except
cls
.
DoesNotExist
:
except
cls
.
DoesNotExist
:
# pylint: disable=no-member
cls
.
objects
.
create
(
proctored_exam_id
=
exam_id
,
user_id
=
user_id
,
key
=
key
,
value
=
value
)
cls
.
objects
.
create
(
proctored_exam_id
=
exam_id
,
user_id
=
user_id
,
key
=
key
,
value
=
value
)
...
...
edx_proctoring/serializers.py
View file @
d1c9b6a2
"""Defines serializers used by the Proctoring API."""
"""Defines serializers used by the Proctoring API."""
from
django.conf
import
settings
from
rest_framework
import
serializers
from
rest_framework
import
serializers
from
edx_proctoring.models
import
ProctoredExam
,
ProctoredExamStudentAttempt
,
ProctoredExamStudentAllowance
from
edx_proctoring.models
import
ProctoredExam
,
ProctoredExamStudentAttempt
,
ProctoredExamStudentAllowance
class
StrictBooleanField
(
serializers
.
BooleanField
):
"""
Boolean field serializer to cater for a bug in DRF BooleanField serializer
where required=True is ignored.
"""
def
from_native
(
self
,
value
):
if
value
in
(
'true'
,
't'
,
'True'
,
'1'
):
return
True
if
value
in
(
'false'
,
'f'
,
'False'
,
'0'
):
return
False
return
None
class
ProctoredExamSerializer
(
serializers
.
ModelSerializer
):
class
ProctoredExamSerializer
(
serializers
.
ModelSerializer
):
"""
"""
Serializer for the ProctoredExam Model.
Serializer for the ProctoredExam Model.
"""
"""
course_id
=
serializers
.
RegexField
(
settings
.
COURSE_ID_REGEX
,
required
=
True
)
content_id
=
serializers
.
CharField
(
required
=
True
)
external_id
=
serializers
.
CharField
(
required
=
True
)
exam_name
=
serializers
.
CharField
(
required
=
True
)
time_limit_mins
=
serializers
.
IntegerField
(
required
=
True
)
is_active
=
StrictBooleanField
(
required
=
True
)
is_proctored
=
StrictBooleanField
(
required
=
True
)
class
Meta
:
class
Meta
:
"""
"""
Meta Class
Meta Class
"""
"""
model
=
ProctoredExam
model
=
ProctoredExam
fields
=
(
fields
=
(
"course_id"
,
"content_id"
,
"external_id"
,
"exam_name"
,
"course_id"
,
"content_id"
,
"external_id"
,
"exam_name"
,
"time_limit_mins"
,
"is_proctored"
,
"is_active"
"time_limit_mins"
,
"is_proctored"
,
"is_active"
...
...
edx_proctoring/tests/test_views.py
View file @
d1c9b6a2
...
@@ -3,6 +3,7 @@ All tests for the proctored_exams.py
...
@@ -3,6 +3,7 @@ All tests for the proctored_exams.py
"""
"""
from
django.test.client
import
Client
from
django.test.client
import
Client
from
django.core.urlresolvers
import
reverse
,
NoReverseMatch
from
django.core.urlresolvers
import
reverse
,
NoReverseMatch
from
edx_proctoring.models
import
ProctoredExam
from
.utils
import
(
from
.utils
import
(
LoggedInTestCase
LoggedInTestCase
...
@@ -35,16 +36,85 @@ class ProctoredExamsApiTests(LoggedInTestCase):
...
@@ -35,16 +36,85 @@ class ProctoredExamsApiTests(LoggedInTestCase):
response
=
self
.
client
.
get
(
reverse
(
urlpattern
.
name
))
response
=
self
.
client
.
get
(
reverse
(
urlpattern
.
name
))
except
NoReverseMatch
:
except
NoReverseMatch
:
# some of our URL mappings may require a argument substitution
# some of our URL mappings may require a argument substitution
response
=
self
.
client
.
get
(
reverse
(
urlpattern
.
name
,
args
=
[
0
]))
try
:
response
=
self
.
client
.
get
(
reverse
(
urlpattern
.
name
,
args
=
[
0
]))
except
NoReverseMatch
:
# some require 2 args.
response
=
self
.
client
.
get
(
reverse
(
urlpattern
.
name
,
args
=
[
"0/0/0"
,
0
]))
self
.
assertEqual
(
response
.
status_code
,
403
)
self
.
assertEqual
(
response
.
status_code
,
403
)
def
test_get_proctored_exam_status
(
self
):
class
StudentProctoredExamAttempt
(
LoggedInTestCase
):
"""
Tests for StudentProctoredExamAttempt
"""
def
test_get_exam_attempt
(
self
):
"""
"""
Test Case for retrieving student proctored exam status.
Test Case for retrieving student proctored exam
attempt
status.
"""
"""
response
=
self
.
client
.
get
(
response
=
self
.
client
.
get
(
reverse
(
'edx_proctoring.proctored_exam.attempt'
)
reverse
(
'edx_proctoring.proctored_exam.attempt'
)
)
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
class
ProctoredExamViewTests
(
LoggedInTestCase
):
"""
Tests for the ProctoredExamView
"""
def
test_create_exam
(
self
):
"""
Test the POST method of the exam endpoint to create an exam.
"""
exam_data
=
{
'course_id'
:
"a/b/c"
,
'exam_name'
:
"midterm1"
,
'content_id'
:
'123aXqe0'
,
'time_limit_mins'
:
90
,
'external_id'
:
'123'
,
'is_proctored'
:
True
,
'is_active'
:
True
}
response
=
self
.
client
.
post
(
reverse
(
'edx_proctoring.proctored_exam.exam'
),
exam_data
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertGreater
(
response
.
data
[
'exam_id'
],
0
)
# Now lookup the exam by giving the exam_id returned and match the data.
response
=
self
.
client
.
get
(
reverse
(
'edx_proctoring.proctored_exam.exam_by_id'
,
kwargs
=
{
'exam_id'
:
response
.
data
[
'exam_id'
]})
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
[
'course_id'
],
exam_data
[
'course_id'
])
self
.
assertEqual
(
response
.
data
[
'exam_name'
],
exam_data
[
'exam_name'
])
self
.
assertEqual
(
response
.
data
[
'content_id'
],
exam_data
[
'content_id'
])
self
.
assertEqual
(
response
.
data
[
'external_id'
],
exam_data
[
'external_id'
])
self
.
assertEqual
(
response
.
data
[
'time_limit_mins'
],
exam_data
[
'time_limit_mins'
])
def
test_get_exam_by_id
(
self
):
"""
Tests the Get Exam by id endpoint
"""
# Create an exam.
proctored_exam
=
ProctoredExam
.
objects
.
create
(
course_id
=
'test_course'
,
content_id
=
'test_content'
,
exam_name
=
'Test Exam'
,
external_id
=
'123aXqe3'
,
time_limit_mins
=
90
)
response
=
self
.
client
.
get
(
reverse
(
'edx_proctoring.proctored_exam.exam_by_id'
,
kwargs
=
{
'exam_id'
:
proctored_exam
.
id
})
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
[
'course_id'
],
proctored_exam
.
course_id
)
self
.
assertEqual
(
response
.
data
[
'exam_name'
],
proctored_exam
.
exam_name
)
self
.
assertEqual
(
response
.
data
[
'content_id'
],
proctored_exam
.
content_id
)
self
.
assertEqual
(
response
.
data
[
'external_id'
],
proctored_exam
.
external_id
)
self
.
assertEqual
(
response
.
data
[
'time_limit_mins'
],
proctored_exam
.
time_limit_mins
)
edx_proctoring/urls.py
View file @
d1c9b6a2
...
@@ -16,13 +16,13 @@ urlpatterns = patterns( # pylint: disable=invalid-name
...
@@ -16,13 +16,13 @@ urlpatterns = patterns( # pylint: disable=invalid-name
url
(
url
(
r'edx_proctoring/v1/proctored_exam/exam/exam_id/(?P<exam_id>\d+)$'
,
r'edx_proctoring/v1/proctored_exam/exam/exam_id/(?P<exam_id>\d+)$'
,
views
.
ProctoredExamView
.
as_view
(),
views
.
ProctoredExamView
.
as_view
(),
name
=
'edx_proctoring.proctored_exam.exam'
name
=
'edx_proctoring.proctored_exam.exam
_by_id
'
),
),
url
(
url
(
r'edx_proctoring/v1/proctored_exam/exam/course_id/{}/content_id/(?P<content_id>\d+)$'
.
format
(
r'edx_proctoring/v1/proctored_exam/exam/course_id/{}/content_id/(?P<content_id>\d+)$'
.
format
(
settings
.
COURSE_ID_PATTERN
),
settings
.
COURSE_ID_PATTERN
),
views
.
ProctoredExamView
.
as_view
(),
views
.
ProctoredExamView
.
as_view
(),
name
=
'edx_proctoring.proctored_exam.exam'
name
=
'edx_proctoring.proctored_exam.exam
_by_content_id
'
),
),
url
(
url
(
r'edx_proctoring/v1/proctored_exam/attempt$'
,
r'edx_proctoring/v1/proctored_exam/attempt$'
,
...
...
edx_proctoring/views.py
View file @
d1c9b6a2
...
@@ -101,7 +101,15 @@ class ProctoredExamView(AuthenticatedAPIView):
...
@@ -101,7 +101,15 @@ class ProctoredExamView(AuthenticatedAPIView):
serializer
=
ProctoredExamSerializer
(
data
=
request
.
DATA
)
serializer
=
ProctoredExamSerializer
(
data
=
request
.
DATA
)
if
serializer
.
is_valid
():
if
serializer
.
is_valid
():
try
:
try
:
exam_id
=
create_exam
(
**
request
.
DATA
)
exam_id
=
create_exam
(
course_id
=
request
.
DATA
.
get
(
'course_id'
,
None
),
content_id
=
request
.
DATA
.
get
(
'content_id'
,
None
),
exam_name
=
request
.
DATA
.
get
(
'exam_name'
,
None
),
time_limit_mins
=
request
.
DATA
.
get
(
'time_limit_mins'
,
None
),
is_proctored
=
request
.
DATA
.
get
(
'is_proctored'
,
None
),
external_id
=
request
.
DATA
.
get
(
'external_id'
,
None
),
is_active
=
request
.
DATA
.
get
(
'is_active'
,
None
)
)
return
Response
({
'exam_id'
:
exam_id
})
return
Response
({
'exam_id'
:
exam_id
})
except
ProctoredExamAlreadyExists
:
except
ProctoredExamAlreadyExists
:
return
Response
(
return
Response
(
...
...
settings.py
View file @
d1c9b6a2
...
@@ -68,4 +68,5 @@ MIDDLEWARE_CLASSES = (
...
@@ -68,4 +68,5 @@ MIDDLEWARE_CLASSES = (
ROOT_URLCONF
=
'edx_proctoring.urls'
ROOT_URLCONF
=
'edx_proctoring.urls'
COURSE_ID_PATTERN
=
r'(?P<course_id>[^/+]+(/|\+)[^/+]+(/|\+)[^/]+)'
COURSE_ID_REGEX
=
r'[^/+]+(/|\+)[^/+]+(/|\+)[^/]+'
COURSE_ID_PATTERN
=
r'(?P<course_id>
%
s)'
%
COURSE_ID_REGEX
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