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
803bc553
Commit
803bc553
authored
May 28, 2015
by
zubair-arbi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
use reverification xblock location as an identifier of reverification checkpoint
ECOM-1612
parent
de3cccdb
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
659 additions
and
436 deletions
+659
-436
lms/djangoapps/verify_student/admin.py
+4
-4
lms/djangoapps/verify_student/migrations/0008_auto__del_field_verificationcheckpoint_checkpoint_name__add_field_veri.py
+154
-0
lms/djangoapps/verify_student/models.py
+80
-69
lms/djangoapps/verify_student/services.py
+32
-27
lms/djangoapps/verify_student/tests/test_models.py
+132
-95
lms/djangoapps/verify_student/tests/test_services.py
+48
-36
lms/djangoapps/verify_student/tests/test_views.py
+108
-103
lms/djangoapps/verify_student/urls.py
+1
-2
lms/djangoapps/verify_student/views.py
+98
-91
lms/static/js/verify_student/incourse_reverify.js
+0
-1
lms/static/js/verify_student/models/reverification_model.js
+1
-3
lms/static/js/verify_student/views/incourse_reverify_view.js
+0
-3
lms/templates/verify_student/incourse_reverify.html
+0
-1
requirements/edx/github.txt
+1
-1
No files found.
lms/djangoapps/verify_student/admin.py
View file @
803bc553
...
@@ -23,9 +23,9 @@ class VerificationStatusAdmin(admin.ModelAdmin):
...
@@ -23,9 +23,9 @@ class VerificationStatusAdmin(admin.ModelAdmin):
"""
"""
Admin for the VerificationStatus table.
Admin for the VerificationStatus table.
"""
"""
list_display
=
(
'timestamp'
,
'user'
,
'status'
,
'checkpoint'
,
'location_id'
)
list_display
=
(
'timestamp'
,
'user'
,
'status'
,
'checkpoint'
)
readonly_fields
=
()
readonly_fields
=
()
search_fields
=
(
'checkpoint
'
,
'user
'
)
search_fields
=
(
'checkpoint
__checkpoint_location'
,
'user__username
'
)
def
get_readonly_fields
(
self
,
request
,
obj
=
None
):
def
get_readonly_fields
(
self
,
request
,
obj
=
None
):
"""When editing an existing record, all fields should be read-only.
"""When editing an existing record, all fields should be read-only.
...
@@ -36,7 +36,7 @@ class VerificationStatusAdmin(admin.ModelAdmin):
...
@@ -36,7 +36,7 @@ class VerificationStatusAdmin(admin.ModelAdmin):
"""
"""
if
obj
:
if
obj
:
return
self
.
readonly_fields
+
(
'status'
,
'checkpoint'
,
'user'
,
'
location_id'
,
'
response'
,
'error'
)
return
self
.
readonly_fields
+
(
'status'
,
'checkpoint'
,
'user'
,
'response'
,
'error'
)
return
self
.
readonly_fields
return
self
.
readonly_fields
def
has_delete_permission
(
self
,
request
,
obj
=
None
):
def
has_delete_permission
(
self
,
request
,
obj
=
None
):
...
@@ -48,7 +48,7 @@ class SkippedReverificationAdmin(admin.ModelAdmin):
...
@@ -48,7 +48,7 @@ class SkippedReverificationAdmin(admin.ModelAdmin):
"""Admin for the SkippedReverification table. """
"""Admin for the SkippedReverification table. """
list_display
=
(
'created_at'
,
'user'
,
'course_id'
,
'checkpoint'
)
list_display
=
(
'created_at'
,
'user'
,
'course_id'
,
'checkpoint'
)
readonly_fields
=
(
'user'
,
'course_id'
)
readonly_fields
=
(
'user'
,
'course_id'
)
search_fields
=
(
'user
'
,
'course_id'
,
'checkpoint
'
)
search_fields
=
(
'user
__username'
,
'course_id'
,
'checkpoint__checkpoint_location
'
)
def
has_add_permission
(
self
,
request
):
def
has_add_permission
(
self
,
request
):
"""Skipped verifications can't be created in Django admin. """
"""Skipped verifications can't be created in Django admin. """
...
...
lms/djangoapps/verify_student/migrations/0008_auto__del_field_verificationcheckpoint_checkpoint_name__add_field_veri.py
0 → 100644
View file @
803bc553
# -*- coding: utf-8 -*-
from
south.utils
import
datetime_utils
as
datetime
from
south.db
import
db
from
south.v2
import
SchemaMigration
from
django.db
import
models
class
Migration
(
SchemaMigration
):
def
forwards
(
self
,
orm
):
# Removing unique constraint on 'VerificationCheckpoint', fields ['course_id', 'checkpoint_name']
db
.
delete_unique
(
'verify_student_verificationcheckpoint'
,
[
'course_id'
,
'checkpoint_name'
])
# Deleting field 'VerificationCheckpoint.checkpoint_name'
db
.
delete_column
(
'verify_student_verificationcheckpoint'
,
'checkpoint_name'
)
# Adding field 'VerificationCheckpoint.checkpoint_location'
db
.
add_column
(
'verify_student_verificationcheckpoint'
,
'checkpoint_location'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
default
=
''
,
max_length
=
255
),
keep_default
=
False
)
# Adding unique constraint on 'VerificationCheckpoint', fields ['course_id', 'checkpoint_location']
db
.
create_unique
(
'verify_student_verificationcheckpoint'
,
[
'course_id'
,
'checkpoint_location'
])
# Deleting field 'VerificationStatus.location_id'
db
.
delete_column
(
'verify_student_verificationstatus'
,
'location_id'
)
def
backwards
(
self
,
orm
):
# Removing unique constraint on 'VerificationCheckpoint', fields ['course_id', 'checkpoint_location']
db
.
delete_unique
(
'verify_student_verificationcheckpoint'
,
[
'course_id'
,
'checkpoint_location'
])
# User chose to not deal with backwards NULL issues for 'VerificationCheckpoint.checkpoint_name'
raise
RuntimeError
(
"Cannot reverse this migration. 'VerificationCheckpoint.checkpoint_name' and its values cannot be restored."
)
# The following code is provided here to aid in writing a correct migration # Adding field 'VerificationCheckpoint.checkpoint_name'
db
.
add_column
(
'verify_student_verificationcheckpoint'
,
'checkpoint_name'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
32
),
keep_default
=
False
)
# Deleting field 'VerificationCheckpoint.checkpoint_location'
db
.
delete_column
(
'verify_student_verificationcheckpoint'
,
'checkpoint_location'
)
# Adding unique constraint on 'VerificationCheckpoint', fields ['course_id', 'checkpoint_name']
db
.
create_unique
(
'verify_student_verificationcheckpoint'
,
[
'course_id'
,
'checkpoint_name'
])
# Adding field 'VerificationStatus.location_id'
db
.
add_column
(
'verify_student_verificationstatus'
,
'location_id'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
255
,
null
=
True
,
blank
=
True
),
keep_default
=
False
)
models
=
{
'auth.group'
:
{
'Meta'
:
{
'object_name'
:
'Group'
},
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'80'
}),
'permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
})
},
'auth.permission'
:
{
'Meta'
:
{
'ordering'
:
"('content_type__app_label', 'content_type__model', 'codename')"
,
'unique_together'
:
"(('content_type', 'codename'),)"
,
'object_name'
:
'Permission'
},
'codename'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'content_type'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['contenttypes.ContentType']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
})
},
'auth.user'
:
{
'Meta'
:
{
'object_name'
:
'User'
},
'date_joined'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'email'
:
(
'django.db.models.fields.EmailField'
,
[],
{
'max_length'
:
'75'
,
'blank'
:
'True'
}),
'first_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'groups'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Group']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'is_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'is_staff'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'is_superuser'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'last_login'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'last_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'password'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
}),
'user_permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'username'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'30'
})
},
'contenttypes.contenttype'
:
{
'Meta'
:
{
'ordering'
:
"('name',)"
,
'unique_together'
:
"(('app_label', 'model'),)"
,
'object_name'
:
'ContentType'
,
'db_table'
:
"'django_content_type'"
},
'app_label'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'model'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
})
},
'reverification.midcoursereverificationwindow'
:
{
'Meta'
:
{
'object_name'
:
'MidcourseReverificationWindow'
},
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'end_date'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'None'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'start_date'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'None'
,
'null'
:
'True'
,
'blank'
:
'True'
})
},
'verify_student.incoursereverificationconfiguration'
:
{
'Meta'
:
{
'object_name'
:
'InCourseReverificationConfiguration'
},
'change_date'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'blank'
:
'True'
}),
'changed_by'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
,
'null'
:
'True'
,
'on_delete'
:
'models.PROTECT'
}),
'enabled'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
})
},
'verify_student.skippedreverification'
:
{
'Meta'
:
{
'unique_together'
:
"(('user', 'course_id'),)"
,
'object_name'
:
'SkippedReverification'
},
'checkpoint'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"'skipped_checkpoint'"
,
'to'
:
"orm['verify_student.VerificationCheckpoint']"
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'created_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
})
},
'verify_student.softwaresecurephotoverification'
:
{
'Meta'
:
{
'ordering'
:
"['-created_at']"
,
'object_name'
:
'SoftwareSecurePhotoVerification'
},
'created_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'db_index'
:
'True'
,
'blank'
:
'True'
}),
'display'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
,
'db_index'
:
'True'
}),
'error_code'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
,
'blank'
:
'True'
}),
'error_msg'
:
(
'django.db.models.fields.TextField'
,
[],
{
'blank'
:
'True'
}),
'face_image_url'
:
(
'django.db.models.fields.URLField'
,
[],
{
'max_length'
:
'255'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'blank'
:
'True'
}),
'photo_id_image_url'
:
(
'django.db.models.fields.URLField'
,
[],
{
'max_length'
:
'255'
,
'blank'
:
'True'
}),
'photo_id_key'
:
(
'django.db.models.fields.TextField'
,
[],
{
'max_length'
:
'1024'
}),
'receipt_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'4ae40fdd-c39a-4a23-a593-41beec90013b'"
,
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'reviewing_service'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'blank'
:
'True'
}),
'reviewing_user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'default'
:
'None'
,
'related_name'
:
"'photo_verifications_reviewed'"
,
'null'
:
'True'
,
'to'
:
"orm['auth.User']"
}),
'status'
:
(
'model_utils.fields.StatusField'
,
[],
{
'default'
:
"'created'"
,
'max_length'
:
'100'
,
u'no_check_for_status'
:
'True'
}),
'status_changed'
:
(
'model_utils.fields.MonitorField'
,
[],
{
'default'
:
'datetime.datetime.now'
,
u'monitor'
:
"u'status'"
}),
'submitted_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'db_index'
:
'True'
}),
'updated_at'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now'
:
'True'
,
'db_index'
:
'True'
,
'blank'
:
'True'
}),
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
}),
'window'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['reverification.MidcourseReverificationWindow']"
,
'null'
:
'True'
})
},
'verify_student.verificationcheckpoint'
:
{
'Meta'
:
{
'unique_together'
:
"(('course_id', 'checkpoint_location'),)"
,
'object_name'
:
'VerificationCheckpoint'
},
'checkpoint_location'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'course_id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'photo_verification'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['verify_student.SoftwareSecurePhotoVerification']"
,
'symmetrical'
:
'False'
})
},
'verify_student.verificationstatus'
:
{
'Meta'
:
{
'object_name'
:
'VerificationStatus'
},
'checkpoint'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"'checkpoint_status'"
,
'to'
:
"orm['verify_student.VerificationCheckpoint']"
}),
'error'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'response'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
,
'blank'
:
'True'
}),
'status'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'32'
,
'db_index'
:
'True'
}),
'timestamp'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'blank'
:
'True'
}),
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
})
}
}
complete_apps
=
[
'verify_student'
]
\ No newline at end of file
lms/djangoapps/verify_student/models.py
View file @
803bc553
...
@@ -8,16 +8,17 @@ of a student over a period of time. Right now, the only models are the abstract
...
@@ -8,16 +8,17 @@ of a student over a period of time. Right now, the only models are the abstract
`SoftwareSecurePhotoVerification`. The hope is to keep as much of the
`SoftwareSecurePhotoVerification`. The hope is to keep as much of the
photo verification process as generic as possible.
photo verification process as generic as possible.
"""
"""
from
datetime
import
datetime
,
timedelta
from
email.utils
import
formatdate
import
functools
import
functools
import
json
import
json
import
logging
import
logging
from
datetime
import
datetime
,
timedelta
from
email.utils
import
formatdate
from
course_modes.models
import
CourseMode
import
pytz
import
pytz
import
requests
import
requests
import
uuid
import
uuid
from
lazy
import
lazy
from
opaque_keys.edx.keys
import
UsageKey
from
django.conf
import
settings
from
django.conf
import
settings
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
...
@@ -29,6 +30,7 @@ from django.utils.translation import ugettext as _, ugettext_lazy
...
@@ -29,6 +30,7 @@ from django.utils.translation import ugettext as _, ugettext_lazy
from
boto.s3.connection
import
S3Connection
from
boto.s3.connection
import
S3Connection
from
boto.s3.key
import
Key
from
boto.s3.key
import
Key
from
config_models.models
import
ConfigurationModel
from
config_models.models
import
ConfigurationModel
from
course_modes.models
import
CourseMode
from
model_utils.models
import
StatusModel
from
model_utils.models
import
StatusModel
from
model_utils
import
Choices
from
model_utils
import
Choices
from
reverification.models
import
MidcourseReverificationWindow
from
reverification.models
import
MidcourseReverificationWindow
...
@@ -36,6 +38,8 @@ from verify_student.ssencrypt import (
...
@@ -36,6 +38,8 @@ from verify_student.ssencrypt import (
random_aes_key
,
encrypt_and_encode
,
random_aes_key
,
encrypt_and_encode
,
generate_signed_message
,
rsa_encrypt
generate_signed_message
,
rsa_encrypt
)
)
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule_django.models
import
CourseKeyField
from
xmodule_django.models
import
CourseKeyField
...
@@ -852,12 +856,13 @@ class SoftwareSecurePhotoVerification(PhotoVerification):
...
@@ -852,12 +856,13 @@ class SoftwareSecurePhotoVerification(PhotoVerification):
@classmethod
@classmethod
def
submit_faceimage
(
cls
,
user
,
face_image
,
photo_id_key
):
def
submit_faceimage
(
cls
,
user
,
face_image
,
photo_id_key
):
"""Submit the face
image to SoftwareSecurePhotoVerification
"""Submit the face
image to SoftwareSecurePhotoVerification.
Arguments:
Arguments:
user(User): user object
user(User): user object
face_image (bytestream): raw bytestream image data
face_image (bytestream): raw bytestream image data
photo_id_key (str) : SoftwareSecurePhotoVerification attribute
photo_id_key (str) : SoftwareSecurePhotoVerification attribute
Returns:
Returns:
SoftwareSecurePhotoVerification Object
SoftwareSecurePhotoVerification Object
"""
"""
...
@@ -887,34 +892,52 @@ class SoftwareSecurePhotoVerification(PhotoVerification):
...
@@ -887,34 +892,52 @@ class SoftwareSecurePhotoVerification(PhotoVerification):
class
VerificationCheckpoint
(
models
.
Model
):
class
VerificationCheckpoint
(
models
.
Model
):
"""Represents a point at which a user is challenged to reverify his or her identity.
"""Represents a point at which a user is asked to re-verify his/her
Each checkpoint is uniquely identified by a (course_id, checkpoint_name) tuple.
identity.
"""
CHECKPOINT_CHOICES
=
(
(
"midterm"
,
"midterm"
),
(
"final"
,
"final"
),
)
Each checkpoint is uniquely identified by a
(course_id, checkpoint_location) tuple.
"""
course_id
=
CourseKeyField
(
max_length
=
255
,
db_index
=
True
)
course_id
=
CourseKeyField
(
max_length
=
255
,
db_index
=
True
)
checkpoint_
name
=
models
.
CharField
(
max_length
=
32
,
choices
=
CHECKPOINT_CHOICES
)
checkpoint_
location
=
models
.
CharField
(
max_length
=
255
)
photo_verification
=
models
.
ManyToManyField
(
SoftwareSecurePhotoVerification
)
photo_verification
=
models
.
ManyToManyField
(
SoftwareSecurePhotoVerification
)
class
Meta
:
# pylint: disable=missing-docstring, old-style-class
class
Meta
:
# pylint: disable=missing-docstring, old-style-class
unique_together
=
(
(
'course_id'
,
'checkpoint_name'
),
)
unique_together
=
(
'course_id'
,
'checkpoint_location'
)
def
__unicode__
(
self
):
def
__unicode__
(
self
):
"""Unicode representation of the checkpoint. """
"""
Unicode representation of the checkpoint.
"""
return
u"{checkpoint} in {course}"
.
format
(
return
u"{checkpoint} in {course}"
.
format
(
checkpoint
=
self
.
checkpoint_name
,
checkpoint
=
self
.
checkpoint_name
,
course
=
self
.
course_id
course
=
self
.
course_id
)
)
@lazy
def
checkpoint_name
(
self
):
"""Lazy method for getting checkpoint name of reverification block.
Return location of the checkpoint if no related assessment found in
database.
"""
checkpoint_key
=
UsageKey
.
from_string
(
self
.
checkpoint_location
)
try
:
checkpoint_name
=
modulestore
()
.
get_item
(
checkpoint_key
)
.
related_assessment
except
ItemNotFoundError
:
log
.
warning
(
u"Verification checkpoint block with location '
%
s' and course id '
%
s' "
u"not found in database."
,
self
.
checkpoint_location
,
unicode
(
self
.
course_id
)
)
checkpoint_name
=
self
.
checkpoint_location
return
checkpoint_name
def
add_verification_attempt
(
self
,
verification_attempt
):
def
add_verification_attempt
(
self
,
verification_attempt
):
"""
Add the verification attempt in M2M relation of photo_verification
"""
Add the verification attempt in M2M relation of photo_verification.
Arguments:
Arguments:
verification_attempt(
SoftwareSecurePhotoVerification
): SoftwareSecurePhotoVerification object
verification_attempt(
object
): SoftwareSecurePhotoVerification object
Returns:
Returns:
None
None
...
@@ -922,7 +945,7 @@ class VerificationCheckpoint(models.Model):
...
@@ -922,7 +945,7 @@ class VerificationCheckpoint(models.Model):
self
.
photo_verification
.
add
(
verification_attempt
)
# pylint: disable=no-member
self
.
photo_verification
.
add
(
verification_attempt
)
# pylint: disable=no-member
def
get_user_latest_status
(
self
,
user_id
):
def
get_user_latest_status
(
self
,
user_id
):
"""
Return the latest status of the given checkpoint attempt by user
"""
Get the status of the latest checkpoint attempt of the given user.
Args:
Args:
user_id(str): Id of user
user_id(str): Id of user
...
@@ -931,34 +954,35 @@ class VerificationCheckpoint(models.Model):
...
@@ -931,34 +954,35 @@ class VerificationCheckpoint(models.Model):
VerificationStatus object if found any else None
VerificationStatus object if found any else None
"""
"""
try
:
try
:
return
self
.
checkpoint_status
.
filter
(
user_id
=
user_id
)
.
latest
()
# pylint: disable=
E1101
return
self
.
checkpoint_status
.
filter
(
user_id
=
user_id
)
.
latest
()
# pylint: disable=
no-member
except
ObjectDoesNotExist
:
except
ObjectDoesNotExist
:
return
None
return
None
@classmethod
@classmethod
def
get_verification_checkpoint
(
cls
,
course_id
,
checkpoint_name
):
def
get_verification_checkpoint
(
cls
,
course_id
,
checkpoint_location
):
"""Get the verification checkpoint for given course_id and checkpoint name
"""Get the verification checkpoint for given 'course_id' and
checkpoint name.
Arguments:
Arguments:
course_id(CourseKey): CourseKey
course_id(CourseKey): CourseKey
checkpoint_
name(str): checkpoint name
checkpoint_
location(str): Verification checkpoint location
Returns:
Returns:
VerificationCheckpoint object if exists otherwise None
VerificationCheckpoint object if exists otherwise None
"""
"""
try
:
try
:
return
cls
.
objects
.
get
(
course_id
=
course_id
,
checkpoint_
name
=
checkpoint_name
)
return
cls
.
objects
.
get
(
course_id
=
course_id
,
checkpoint_
location
=
checkpoint_location
)
except
cls
.
DoesNotExist
:
except
cls
.
DoesNotExist
:
return
None
return
None
class
VerificationStatus
(
models
.
Model
):
class
VerificationStatus
(
models
.
Model
):
"""A verification status represents a user’s progress
"""This model is an append-only table that represents user status changes
through the verification process for a particular checkpoint
during the verification process.
Model is an append-only table that represents the user status changes in
verification process
"""
A verification status represents a user’s progress through the verification
process for a particular checkpoint.
"""
VERIFICATION_STATUS_CHOICES
=
(
VERIFICATION_STATUS_CHOICES
=
(
(
"submitted"
,
"submitted"
),
(
"submitted"
,
"submitted"
),
(
"approved"
,
"approved"
),
(
"approved"
,
"approved"
),
...
@@ -973,56 +997,43 @@ class VerificationStatus(models.Model):
...
@@ -973,56 +997,43 @@ class VerificationStatus(models.Model):
response
=
models
.
TextField
(
null
=
True
,
blank
=
True
)
response
=
models
.
TextField
(
null
=
True
,
blank
=
True
)
error
=
models
.
TextField
(
null
=
True
,
blank
=
True
)
error
=
models
.
TextField
(
null
=
True
,
blank
=
True
)
# This field is used to save location of Reverification module in courseware
location_id
=
models
.
CharField
(
null
=
True
,
blank
=
True
,
max_length
=
255
,
help_text
=
ugettext_lazy
(
"Usage id of Reverification XBlock."
)
)
class
Meta
(
object
):
# pylint: disable=missing-docstring
class
Meta
(
object
):
# pylint: disable=missing-docstring
get_latest_by
=
"timestamp"
get_latest_by
=
"timestamp"
verbose_name
=
"Verification Status"
verbose_name
=
"Verification Status"
verbose_name_plural
=
"Verification Statuses"
verbose_name_plural
=
"Verification Statuses"
@classmethod
@classmethod
def
add_verification_status
(
cls
,
checkpoint
,
user
,
status
,
location_id
=
None
):
def
add_verification_status
(
cls
,
checkpoint
,
user
,
status
):
"""
Create new verification status object
"""
Create new verification status object.
Arguments:
Arguments:
checkpoint(VerificationCheckpoint): VerificationCheckpoint object
checkpoint(VerificationCheckpoint): VerificationCheckpoint object
user(User): user object
user(User): user object
status(str): St
ring representing the st
atus from VERIFICATION_STATUS_CHOICES
status(str): Status from VERIFICATION_STATUS_CHOICES
location_id(str): Usage key of Reverification XBlock
Returns:
Returns:
None
None
"""
"""
cls
.
objects
.
create
(
checkpoint
=
checkpoint
,
user
=
user
,
status
=
status
,
location_id
=
location_id
)
cls
.
objects
.
create
(
checkpoint
=
checkpoint
,
user
=
user
,
status
=
status
)
@classmethod
@classmethod
def
add_status_from_checkpoints
(
cls
,
checkpoints
,
user
,
status
):
def
add_status_from_checkpoints
(
cls
,
checkpoints
,
user
,
status
):
""" Create new verification status objects against the given checkpoints
"""Create new verification status objects for a user against the given
checkpoints.
Arguments:
Arguments:
checkpoints(list): list of VerificationCheckpoint objects
checkpoints(list): list of VerificationCheckpoint objects
user(User): user object
user(User): user object
status(str): String representing the status from VERIFICATION_STATUS_CHOICES
status(str): Status from VERIFICATION_STATUS_CHOICES
Returns:
Returns:
None
None
"""
"""
for
checkpoint
in
checkpoints
:
for
checkpoint
in
checkpoints
:
# get 'location_id' from last entry (if it exists) and add it in
cls
.
objects
.
create
(
checkpoint
=
checkpoint
,
user
=
user
,
status
=
status
)
# new entry
try
:
location_id
=
cls
.
objects
.
filter
(
checkpoint
=
checkpoint
)
.
latest
()
.
location_id
except
cls
.
DoesNotExist
:
location_id
=
None
cls
.
objects
.
create
(
checkpoint
=
checkpoint
,
user
=
user
,
status
=
status
,
location_id
=
location_id
)
@classmethod
@classmethod
def
get_user_attempts
(
cls
,
user_id
,
course_key
,
related_assessment
,
location_id
):
def
get_user_attempts
(
cls
,
user_id
,
course_key
,
related_assessment
_location
):
"""
"""
Get re-verification attempts against a user for a given 'checkpoint'
Get re-verification attempts against a user for a given 'checkpoint'
and 'course_id'.
and 'course_id'.
...
@@ -1030,34 +1041,32 @@ class VerificationStatus(models.Model):
...
@@ -1030,34 +1041,32 @@ class VerificationStatus(models.Model):
Arguments:
Arguments:
user_id(str): User Id string
user_id(str): User Id string
course_key(str): A CourseKey of a course
course_key(str): A CourseKey of a course
related_assessment(str): Verification checkpoint name
related_assessment_location(str): Verification checkpoint location
location_id(str): Location of Reverification XBlock in courseware
Returns:
Returns:
c
ount of re-verification attempts
C
ount of re-verification attempts
"""
"""
return
cls
.
objects
.
filter
(
return
cls
.
objects
.
filter
(
user_id
=
user_id
,
user_id
=
user_id
,
checkpoint__course_id
=
course_key
,
checkpoint__course_id
=
course_key
,
checkpoint__checkpoint_name
=
related_assessment
,
checkpoint__checkpoint_location
=
related_assessment_location
,
location_id
=
location_id
,
status
=
"submitted"
status
=
"submitted"
)
.
count
()
)
.
count
()
@classmethod
@classmethod
def
get_location_id
(
cls
,
photo_verification
):
def
get_location_id
(
cls
,
photo_verification
):
"""
Return the location id of xblock
"""
Get the location ID of reverification XBlock.
Args:
Args:
photo_verification(
SoftwareSecurePhotoVerification
): SoftwareSecurePhotoVerification object
photo_verification(
object
): SoftwareSecurePhotoVerification object
Return:
Return:
Location Id of
xb
lock if any else empty string
Location Id of
XB
lock if any else empty string
"""
"""
try
:
try
:
ver_status
=
cls
.
objects
.
filter
(
checkpoint__photo_verification
=
photo_verification
)
.
latest
()
ver
ification
_status
=
cls
.
objects
.
filter
(
checkpoint__photo_verification
=
photo_verification
)
.
latest
()
return
ver
_status
.
location_id
return
ver
ification_status
.
checkpoint
.
checkpoint_location
except
cls
.
DoesNotExist
:
except
cls
.
DoesNotExist
:
return
""
return
""
...
@@ -1071,17 +1080,16 @@ class InCourseReverificationConfiguration(ConfigurationModel):
...
@@ -1071,17 +1080,16 @@ class InCourseReverificationConfiguration(ConfigurationModel):
When the flag is enabled, the "in-course re-verification" feature
When the flag is enabled, the "in-course re-verification" feature
will be enabled.
will be enabled.
"""
"""
pass
pass
class
SkippedReverification
(
models
.
Model
):
class
SkippedReverification
(
models
.
Model
):
"""
"""Model for tracking skipped Reverification of a user against a specific
Model for tracking skipped Reverification of a user against a specific
course.
course.
If user skipped the Reverification for a specific course then in future
user cannot see the reverification link.
If a user skipped a Reverification checkpoint for a specific course then in
future that user cannot see the reverification link.
"""
"""
user
=
models
.
ForeignKey
(
User
)
user
=
models
.
ForeignKey
(
User
)
course_id
=
CourseKeyField
(
max_length
=
255
,
db_index
=
True
)
course_id
=
CourseKeyField
(
max_length
=
255
,
db_index
=
True
)
...
@@ -1093,12 +1101,13 @@ class SkippedReverification(models.Model):
...
@@ -1093,12 +1101,13 @@ class SkippedReverification(models.Model):
@classmethod
@classmethod
def
add_skipped_reverification_attempt
(
cls
,
checkpoint
,
user_id
,
course_id
):
def
add_skipped_reverification_attempt
(
cls
,
checkpoint
,
user_id
,
course_id
):
"""
Create skipped reverification object
"""
Create skipped reverification object.
Arguments:
Arguments:
checkpoint(VerificationCheckpoint): VerificationCheckpoint object
checkpoint(VerificationCheckpoint): VerificationCheckpoint object
user_id(str): User Id of currently logged in user
user_id(str): User Id of currently logged in user
course_id(CourseKey): CourseKey
course_id(CourseKey): CourseKey
Returns:
Returns:
None
None
"""
"""
...
@@ -1106,11 +1115,13 @@ class SkippedReverification(models.Model):
...
@@ -1106,11 +1115,13 @@ class SkippedReverification(models.Model):
@classmethod
@classmethod
def
check_user_skipped_reverification_exists
(
cls
,
user
,
course_id
):
def
check_user_skipped_reverification_exists
(
cls
,
user
,
course_id
):
"""Check user skipped re-verification attempt exists against specific course
"""Check existence of a user's skipped re-verification attempt for a
specific course.
Arguments:
Arguments:
user(User): user object
user(User): user object
course_id(CourseKey): CourseKey
course_id(CourseKey): CourseKey
Returns:
Returns:
Boolean
Boolean
"""
"""
...
...
lms/djangoapps/verify_student/services.py
View file @
803bc553
...
@@ -9,6 +9,7 @@ from django.core.urlresolvers import reverse
...
@@ -9,6 +9,7 @@ from django.core.urlresolvers import reverse
from
django.db
import
IntegrityError
from
django.db
import
IntegrityError
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.keys
import
CourseKey
from
verify_student.models
import
VerificationCheckpoint
,
VerificationStatus
,
SkippedReverification
from
verify_student.models
import
VerificationCheckpoint
,
VerificationStatus
,
SkippedReverification
...
@@ -20,19 +21,19 @@ class ReverificationService(object):
...
@@ -20,19 +21,19 @@ class ReverificationService(object):
Reverification XBlock service
Reverification XBlock service
"""
"""
def
get_status
(
self
,
user_id
,
course_id
,
related_assessment
):
def
get_status
(
self
,
user_id
,
course_id
,
related_assessment_location
):
"""
"""Get verification attempt status against a user for a given
Get verification attempt status against a user for a given 'checkpoint'
'checkpoint' and 'course_id'.
and 'course_id'.
Args:
Args:
user_id(str): User Id string
user_id(str): User Id string
course_id(str): A string of course id
course_id(str): A string of course id
related_assessment
(str): Verification checkpoint name
related_assessment
_location(str): Location of Reverification XBlock
Returns:
Returns:
"skipped" if has skip the re-verification or Verification Status string if
"skipped" if the user has skipped the re-verification or
any attempt submitted by user else None
Verification Status string if the user has submitted photo
verification attempt else None
"""
"""
course_key
=
CourseKey
.
from_string
(
course_id
)
course_key
=
CourseKey
.
from_string
(
course_id
)
has_skipped
=
SkippedReverification
.
check_user_skipped_reverification_exists
(
user_id
,
course_key
)
has_skipped
=
SkippedReverification
.
check_user_skipped_reverification_exists
(
user_id
,
course_key
)
...
@@ -42,69 +43,73 @@ class ReverificationService(object):
...
@@ -42,69 +43,73 @@ class ReverificationService(object):
checkpoint_status
=
VerificationStatus
.
objects
.
filter
(
checkpoint_status
=
VerificationStatus
.
objects
.
filter
(
user_id
=
user_id
,
user_id
=
user_id
,
checkpoint__course_id
=
course_key
,
checkpoint__course_id
=
course_key
,
checkpoint__checkpoint_
name
=
related_assessment
checkpoint__checkpoint_
location
=
related_assessment_location
)
.
latest
()
)
.
latest
()
return
checkpoint_status
.
status
return
checkpoint_status
.
status
except
ObjectDoesNotExist
:
except
ObjectDoesNotExist
:
return
None
return
None
def
start_verification
(
self
,
course_id
,
related_assessment
,
item_id
):
def
start_verification
(
self
,
course_id
,
related_assessment_location
):
"""
"""Create re-verification link against a verification checkpoint.
Create re-verification link against a verification checkpoint.
Args:
Args:
course_id(str): A string of course id
course_id(str): A string of course id
related_assessment
(str): Verification checkpoint name
related_assessment
_location(str): Location of Reverification XBlock
Returns:
Returns:
Re-verification link
Re-verification link
"""
"""
course_key
=
CourseKey
.
from_string
(
course_id
)
course_key
=
CourseKey
.
from_string
(
course_id
)
VerificationCheckpoint
.
objects
.
get_or_create
(
course_id
=
course_key
,
checkpoint_name
=
related_assessment
)
VerificationCheckpoint
.
objects
.
get_or_create
(
course_id
=
course_key
,
checkpoint_location
=
related_assessment_location
)
re_verification_link
=
reverse
(
re_verification_link
=
reverse
(
'verify_student_incourse_reverify'
,
'verify_student_incourse_reverify'
,
args
=
(
args
=
(
unicode
(
course_key
),
unicode
(
course_key
),
unicode
(
related_assessment
),
unicode
(
related_assessment_location
)
unicode
(
item_id
)
)
)
)
)
return
re_verification_link
return
re_verification_link
def
skip_verification
(
self
,
checkpoint_name
,
user_id
,
course_id
):
def
skip_verification
(
self
,
user_id
,
course_id
,
related_assessment_location
):
"""
"""
Add skipped verification attempt entry for a user against a given
Add skipped verification attempt entry against a given 'checkpoint'
'checkpoint'.
Args:
Args:
checkpoint_name(str): Verification checkpoint name
user_id(str): User Id string
user_id(str): User Id string
course_id(str): A string of course_id
course_id(str): A string of course_id
related_assessment_location(str): Location of Reverification XBlock
Returns:
Returns:
None
None
"""
"""
course_key
=
CourseKey
.
from_string
(
course_id
)
course_key
=
CourseKey
.
from_string
(
course_id
)
checkpoint
=
VerificationCheckpoint
.
objects
.
get
(
course_id
=
course_key
,
checkpoint_name
=
checkpoint_name
)
checkpoint
=
VerificationCheckpoint
.
objects
.
get
(
course_id
=
course_key
,
checkpoint_location
=
related_assessment_location
)
# if user do not already skipped the attempt for this course only then he can skip
# user can skip a reverification attempt only if that user has not already
# skipped an attempt
try
:
try
:
SkippedReverification
.
add_skipped_reverification_attempt
(
checkpoint
,
user_id
,
course_key
)
SkippedReverification
.
add_skipped_reverification_attempt
(
checkpoint
,
user_id
,
course_key
)
except
IntegrityError
:
except
IntegrityError
:
log
.
exception
(
"Skipped attempt already exists for user
%
s: with course
%
s:"
,
user_id
,
unicode
(
course_id
))
log
.
exception
(
"Skipped attempt already exists for user
%
s: with course
%
s:"
,
user_id
,
unicode
(
course_id
))
def
get_attempts
(
self
,
user_id
,
course_id
,
related_assessment
,
location_id
):
def
get_attempts
(
self
,
user_id
,
course_id
,
related_assessment_location
):
"""
"""Get re-verification attempts against a user for a given 'checkpoint'
Get re-verification attempts against a user for a given 'checkpoint'
and 'course_id'.
and 'course_id'.
Args:
Args:
user_id(str): User Id string
user_id(str): User Id string
course_id(str): A string of course id
course_id(str): A string of course id
related_assessment(str): Verification checkpoint name
related_assessment_location(str): Location of Reverification XBlock
location_id(str): Location of Reverification XBlock in courseware
Returns:
Returns:
Number of re-verification attempts of a user
Number of re-verification attempts of a user
"""
"""
course_key
=
CourseKey
.
from_string
(
course_id
)
course_key
=
CourseKey
.
from_string
(
course_id
)
return
VerificationStatus
.
get_user_attempts
(
user_id
,
course_key
,
related_assessment
,
location_id
)
return
VerificationStatus
.
get_user_attempts
(
user_id
,
course_key
,
related_assessment
_location
)
lms/djangoapps/verify_student/tests/test_models.py
View file @
803bc553
...
@@ -6,7 +6,6 @@ import requests.exceptions
...
@@ -6,7 +6,6 @@ import requests.exceptions
import
pytz
import
pytz
from
django.conf
import
settings
from
django.conf
import
settings
from
django.test
import
TestCase
from
django.db.utils
import
IntegrityError
from
django.db.utils
import
IntegrityError
from
mock
import
patch
from
mock
import
patch
from
nose.tools
import
assert_is_none
,
assert_equals
,
assert_raises
,
assert_true
,
assert_false
# pylint: disable=E0611
from
nose.tools
import
assert_is_none
,
assert_equals
,
assert_raises
,
assert_true
,
assert_false
# pylint: disable=E0611
...
@@ -486,69 +485,92 @@ class TestPhotoVerification(ModuleStoreTestCase):
...
@@ -486,69 +485,92 @@ class TestPhotoVerification(ModuleStoreTestCase):
class
VerificationCheckpointTest
(
ModuleStoreTestCase
):
class
VerificationCheckpointTest
(
ModuleStoreTestCase
):
"""Tests for the VerificationCheckpoint model. """
"""Tests for the VerificationCheckpoint model. """
MIDTERM
=
"midterm"
FINAL
=
"final"
def
setUp
(
self
):
def
setUp
(
self
):
super
(
VerificationCheckpointTest
,
self
)
.
setUp
()
super
(
VerificationCheckpointTest
,
self
)
.
setUp
()
self
.
user
=
UserFactory
.
create
()
self
.
user
=
UserFactory
.
create
()
self
.
course
=
CourseFactory
.
create
()
self
.
course
=
CourseFactory
.
create
()
self
.
checkpoint_midterm
=
u'i4x://{org}/{course}/edx-reverification-block/midterm_uuid'
.
format
(
org
=
self
.
course
.
id
.
org
,
course
=
self
.
course
.
id
.
course
)
self
.
checkpoint_final
=
u'i4x://{org}/{course}/edx-reverification-block/final_uuid'
.
format
(
org
=
self
.
course
.
id
.
org
,
course
=
self
.
course
.
id
.
course
)
@ddt.data
(
MIDTERM
,
FINAL
)
@ddt.data
(
'midterm'
,
'final'
)
def
test_get_verification_checkpoint
(
self
,
check_point
):
def
test_get_verification_checkpoint
(
self
,
checkpoint
):
"""testing class method of VerificationCheckpoint. create the object and then uses the class method to get the
verification check point.
"""
"""
# create the VerificationCheckpoint checkpoint
Test that a reverification checkpoint is created properly.
ver_check_point
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_name
=
check_point
)
"""
checkpoint_location
=
u'i4x://{org}/{course}/edx-reverification-block/{checkpoint}'
.
format
(
org
=
self
.
course
.
id
.
org
,
course
=
self
.
course
.
id
.
course
,
checkpoint
=
checkpoint
)
# create the 'VerificationCheckpoint' checkpoint
verification_checkpoint
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_location
=
checkpoint_location
)
self
.
assertEqual
(
self
.
assertEqual
(
VerificationCheckpoint
.
get_verification_checkpoint
(
self
.
course
.
id
,
check
_point
),
VerificationCheckpoint
.
get_verification_checkpoint
(
self
.
course
.
id
,
check
point_location
),
ver
_check_
point
ver
ification_check
point
)
)
def
test_get_verification_checkpoint_for_not_existing_values
(
self
):
def
test_get_verification_checkpoint_for_not_existing_values
(
self
):
"""
testing class method of VerificationCheckpoint. create the object and then uses the class method to get the
"""
Test that 'get_verification_checkpoint' method returns None if user
verification check point
.
tries to access a checkpoint with an invalid location
.
"""
"""
# create the VerificationCheckpoint checkpoint
# create the VerificationCheckpoint checkpoint
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_
name
=
self
.
MIDTERM
)
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_
location
=
self
.
checkpoint_midterm
)
# get verification for not existing checkpoint
# get verification for a non existing checkpoint
self
.
assertEqual
(
VerificationCheckpoint
.
get_verification_checkpoint
(
self
.
course
.
id
,
'abc'
),
None
)
self
.
assertEqual
(
VerificationCheckpoint
.
get_verification_checkpoint
(
self
.
course
.
id
,
u'i4x://edX/DemoX/edx-reverification-block/invalid_location'
),
None
)
def
test_unique_together_constraint
(
self
):
def
test_unique_together_constraint
(
self
):
"""testing the unique together contraint.
"""
Test the unique together constraint.
"""
"""
# create the VerificationCheckpoint checkpoint
# create the VerificationCheckpoint checkpoint
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_
name
=
self
.
MIDTERM
)
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_
location
=
self
.
checkpoint_midterm
)
# create the VerificationCheckpoint checkpoint with same course id and checkpoint name
# test creating the VerificationCheckpoint checkpoint with same course
# id and checkpoint name
with
self
.
assertRaises
(
IntegrityError
):
with
self
.
assertRaises
(
IntegrityError
):
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_
name
=
self
.
MIDTERM
)
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_
location
=
self
.
checkpoint_midterm
)
def
test_add_verification_attempt_software_secure
(
self
):
def
test_add_verification_attempt_software_secure
(
self
):
"""testing manytomany relationship. adding softwaresecure attempt to the verification checkpoints.
"""
Test adding Software Secure photo verification attempts for the
reverification checkpoints.
"""
"""
# adding two check points.
# adding two check points.
check_point1
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_name
=
self
.
MIDTERM
)
first_checkpoint
=
VerificationCheckpoint
.
objects
.
create
(
check_point2
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_name
=
self
.
FINAL
)
course_id
=
self
.
course
.
id
,
checkpoint_location
=
self
.
checkpoint_midterm
)
second_checkpoint
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_location
=
self
.
checkpoint_final
)
#
Make an attempt and added to the checkpoint1.
#
make an attempt for the 'first_checkpoint'
check_point1
.
add_verification_attempt
(
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
))
first_checkpoint
.
add_verification_attempt
(
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
))
self
.
assertEqual
(
check_point1
.
photo_verification
.
count
(),
1
)
self
.
assertEqual
(
first_checkpoint
.
photo_verification
.
count
(),
1
)
#
Make an other attempt and added to the checkpoint1.
#
make another attempt for the 'first_checkpoint'
check_point1
.
add_verification_attempt
(
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
))
first_checkpoint
.
add_verification_attempt
(
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
))
self
.
assertEqual
(
check_point1
.
photo_verification
.
count
(),
2
)
self
.
assertEqual
(
first_checkpoint
.
photo_verification
.
count
(),
2
)
# make new attempt
and adding to the checkpoint2
# make new attempt
for the 'second_checkpoint'
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
)
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
)
check_point2
.
add_verification_attempt
(
attempt
)
second_checkpoint
.
add_verification_attempt
(
attempt
)
self
.
assertEqual
(
check_point2
.
photo_verification
.
count
(),
1
)
self
.
assertEqual
(
second_checkpoint
.
photo_verification
.
count
(),
1
)
# remove the attempt from
checkpoint2
# remove the attempt from
'second_checkpoint'
check_point2
.
photo_verification
.
remove
(
attempt
)
second_checkpoint
.
photo_verification
.
remove
(
attempt
)
self
.
assertEqual
(
check_point2
.
photo_verification
.
count
(),
0
)
self
.
assertEqual
(
second_checkpoint
.
photo_verification
.
count
(),
0
)
@ddt.ddt
@ddt.ddt
...
@@ -559,15 +581,20 @@ class VerificationStatusTest(ModuleStoreTestCase):
...
@@ -559,15 +581,20 @@ class VerificationStatusTest(ModuleStoreTestCase):
super
(
VerificationStatusTest
,
self
)
.
setUp
()
super
(
VerificationStatusTest
,
self
)
.
setUp
()
self
.
user
=
UserFactory
.
create
()
self
.
user
=
UserFactory
.
create
()
self
.
course
=
CourseFactory
.
create
()
self
.
course
=
CourseFactory
.
create
()
self
.
check_point1
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_name
=
"midterm"
)
self
.
check_point2
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_name
=
"final"
)
self
.
first_checkpoint_location
=
u'i4x://{org}/{course}/edx-reverification-block/first_checkpoint_uuid'
.
format
(
self
.
dummy_reverification_item_id_1
=
'i4x://{}/{}/edx-reverification-block/related_assessment_1'
.
format
(
org
=
self
.
course
.
id
.
org
,
course
=
self
.
course
.
id
.
course
self
.
course
.
location
.
org
,
self
.
course
.
location
.
course
)
)
self
.
dummy_reverification_item_id_2
=
'i4x://{}/{}/edx-reverification-block/related_assessment_2'
.
format
(
self
.
first_checkpoint
=
VerificationCheckpoint
.
objects
.
create
(
self
.
course
.
location
.
org
,
course_id
=
self
.
course
.
id
,
self
.
course
.
location
.
course
checkpoint_location
=
self
.
first_checkpoint_location
)
self
.
second_checkpoint_location
=
u'i4x://{org}/{course}/edx-reverification-block/second_checkpoint_uuid'
.
\
format
(
org
=
self
.
course
.
id
.
org
,
course
=
self
.
course
.
id
.
course
)
self
.
second_checkpoint
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_location
=
self
.
second_checkpoint_location
)
)
@ddt.data
(
'submitted'
,
"approved"
,
"denied"
,
"error"
)
@ddt.data
(
'submitted'
,
"approved"
,
"denied"
,
"error"
)
...
@@ -576,149 +603,159 @@ class VerificationStatusTest(ModuleStoreTestCase):
...
@@ -576,149 +603,159 @@ class VerificationStatusTest(ModuleStoreTestCase):
# adding verification status
# adding verification status
VerificationStatus
.
add_verification_status
(
VerificationStatus
.
add_verification_status
(
checkpoint
=
self
.
check_point1
,
checkpoint
=
self
.
first_checkpoint
,
user
=
self
.
user
,
user
=
self
.
user
,
status
=
status
,
status
=
status
location_id
=
self
.
dummy_reverification_item_id_1
)
)
#
getting the status from db
#
test the status from database
result
=
VerificationStatus
.
objects
.
filter
(
checkpoint
=
self
.
check_point1
)[
0
]
result
=
VerificationStatus
.
objects
.
filter
(
checkpoint
=
self
.
first_checkpoint
)[
0
]
self
.
assertEqual
(
result
.
status
,
status
)
self
.
assertEqual
(
result
.
status
,
status
)
self
.
assertEqual
(
result
.
user
,
self
.
user
)
self
.
assertEqual
(
result
.
user
,
self
.
user
)
@ddt.data
(
"approved"
,
"denied"
,
"error"
)
@ddt.data
(
"approved"
,
"denied"
,
"error"
)
def
test_add_status_from_checkpoints
(
self
,
status
):
def
test_add_status_from_checkpoints
(
self
,
status
):
""" Adding verification status for checkpoints list after submitting sspv. """
"""Test verification status for reverification checkpoints after
submitting software secure photo verification.
"""
# add initial verification status for checkpoints
# add initial verification status for checkpoints
initial_status
=
"submitted"
initial_status
=
"submitted"
VerificationStatus
.
add_verification_status
(
VerificationStatus
.
add_verification_status
(
checkpoint
=
self
.
check_point1
,
checkpoint
=
self
.
first_checkpoint
,
user
=
self
.
user
,
user
=
self
.
user
,
status
=
initial_status
,
status
=
initial_status
location_id
=
self
.
dummy_reverification_item_id_1
)
)
VerificationStatus
.
add_verification_status
(
VerificationStatus
.
add_verification_status
(
checkpoint
=
self
.
check_point2
,
checkpoint
=
self
.
second_checkpoint
,
user
=
self
.
user
,
user
=
self
.
user
,
status
=
initial_status
,
status
=
initial_status
location_id
=
self
.
dummy_reverification_item_id_2
)
)
# now add verification status for multiple checkpoint points
# now add verification status for multiple checkpoint points
VerificationStatus
.
add_status_from_checkpoints
(
VerificationStatus
.
add_status_from_checkpoints
(
checkpoints
=
[
self
.
check_point1
,
self
.
check_point2
],
user
=
self
.
user
,
status
=
status
checkpoints
=
[
self
.
first_checkpoint
,
self
.
second_checkpoint
],
user
=
self
.
user
,
status
=
status
)
)
# test that verification status entries with new status have been added
# test that verification status entries with new status have been added
# for both checkpoints
and all entries have related 'location_id'.
# for both checkpoints
result
=
VerificationStatus
.
objects
.
filter
(
user
=
self
.
user
,
checkpoint
=
self
.
check_point1
)
result
=
VerificationStatus
.
objects
.
filter
(
user
=
self
.
user
,
checkpoint
=
self
.
first_checkpoint
)
self
.
assertEqual
(
len
(
result
),
len
(
self
.
check_point1
.
checkpoint_status
.
all
()))
self
.
assertEqual
(
len
(
result
),
len
(
self
.
first_checkpoint
.
checkpoint_status
.
all
()))
self
.
assertEqual
(
self
.
assertEqual
(
list
(
result
.
values_list
(
'
location_id
'
,
flat
=
True
)),
list
(
result
.
values_list
(
'
checkpoint__checkpoint_location
'
,
flat
=
True
)),
list
(
self
.
check_point1
.
checkpoint_status
.
all
()
.
values_list
(
'location_id
'
,
flat
=
True
))
list
(
self
.
first_checkpoint
.
checkpoint_status
.
values_list
(
'checkpoint__checkpoint_location
'
,
flat
=
True
))
)
)
result
=
VerificationStatus
.
objects
.
filter
(
user
=
self
.
user
,
checkpoint
=
self
.
check_point2
)
self
.
assertEqual
(
len
(
result
),
len
(
self
.
check_point2
.
checkpoint_status
.
all
()))
result
=
VerificationStatus
.
objects
.
filter
(
user
=
self
.
user
,
checkpoint
=
self
.
second_checkpoint
)
self
.
assertEqual
(
len
(
result
),
len
(
self
.
second_checkpoint
.
checkpoint_status
.
all
()))
self
.
assertEqual
(
self
.
assertEqual
(
list
(
result
.
values_list
(
'
location_id
'
,
flat
=
True
)),
list
(
result
.
values_list
(
'
checkpoint__checkpoint_location
'
,
flat
=
True
)),
list
(
self
.
check_point2
.
checkpoint_status
.
all
()
.
values_list
(
'location_id
'
,
flat
=
True
))
list
(
self
.
second_checkpoint
.
checkpoint_status
.
values_list
(
'checkpoint__checkpoint_location
'
,
flat
=
True
))
)
)
def
test_get_location_id
(
self
):
def
test_get_location_id
(
self
):
""" Getting location id for a specific checkpoint """
"""
Getting location id for a specific checkpoint.
"""
# creating software secure attempt against checkpoint
# creating software secure attempt against checkpoint
self
.
check_point1
.
add_verification_attempt
(
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
))
self
.
first_checkpoint
.
add_verification_attempt
(
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
))
# add initial verification status for checkpoint
# add initial verification status for checkpoint
VerificationStatus
.
add_verification_status
(
VerificationStatus
.
add_verification_status
(
checkpoint
=
self
.
check_point1
,
checkpoint
=
self
.
first_checkpoint
,
user
=
self
.
user
,
user
=
self
.
user
,
status
=
'submitted'
,
status
=
'submitted'
,
location_id
=
self
.
dummy_reverification_item_id_1
)
)
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
filter
(
user
=
self
.
user
)
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
filter
(
user
=
self
.
user
)
self
.
assertIsNotNone
(
VerificationStatus
.
get_location_id
(
attempt
))
self
.
assertIsNotNone
(
VerificationStatus
.
get_location_id
(
attempt
))
self
.
assertEqual
(
VerificationStatus
.
get_location_id
(
None
),
''
)
self
.
assertEqual
(
VerificationStatus
.
get_location_id
(
None
),
''
)
def
test_get_user_attempts
(
self
):
def
test_get_user_attempts
(
self
):
"""
# adding verification status
Test adding verification status.
"""
VerificationStatus
.
add_verification_status
(
VerificationStatus
.
add_verification_status
(
checkpoint
=
self
.
check_point1
,
checkpoint
=
self
.
first_checkpoint
,
user
=
self
.
user
,
user
=
self
.
user
,
status
=
'submitted'
,
status
=
'submitted'
location_id
=
self
.
dummy_reverification_item_id_1
)
)
self
.
assertEqual
(
VerificationStatus
.
get_user_attempts
(
self
.
assertEqual
(
course_key
=
self
.
course
.
id
,
VerificationStatus
.
get_user_attempts
(
user_id
=
self
.
user
.
id
,
user_id
=
self
.
user
.
id
,
related_assessment
=
'midterm'
,
location_id
=
self
.
dummy_reverification_item_id_1
),
1
)
course_key
=
self
.
course
.
id
,
related_assessment_location
=
self
.
first_checkpoint_location
),
1
)
class
SkippedReverificationTest
(
ModuleStoreTestCase
):
class
SkippedReverificationTest
(
ModuleStoreTestCase
):
"""Tests for the SkippedReverification model. """
"""
Tests for the SkippedReverification model.
"""
def
setUp
(
self
):
def
setUp
(
self
):
super
(
SkippedReverificationTest
,
self
)
.
setUp
()
super
(
SkippedReverificationTest
,
self
)
.
setUp
()
self
.
user
=
UserFactory
.
create
()
self
.
user
=
UserFactory
.
create
()
self
.
course
=
CourseFactory
.
create
()
self
.
course
=
CourseFactory
.
create
()
self
.
checkpoint
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_name
=
"midterm"
)
dummy_checkpoint_location
=
u'i4x://edX/DemoX/edx-reverification-block/midterm_uuid'
self
.
checkpoint
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_location
=
dummy_checkpoint_location
)
def
test_add_skipped_attempts
(
self
):
def
test_add_skipped_attempts
(
self
):
"""adding skipped re-verification object using class method."""
"""
Test 'add_skipped_reverification_attempt' method.
"""
# add
ing
verification status
# add verification status
SkippedReverification
.
add_skipped_reverification_attempt
(
SkippedReverification
.
add_skipped_reverification_attempt
(
checkpoint
=
self
.
checkpoint
,
user_id
=
self
.
user
.
id
,
course_id
=
unicode
(
self
.
course
.
id
)
checkpoint
=
self
.
checkpoint
,
user_id
=
self
.
user
.
id
,
course_id
=
unicode
(
self
.
course
.
id
)
)
)
#
getting the status from db
#
test the status of skipped reverification from database
result
=
SkippedReverification
.
objects
.
filter
(
course_id
=
self
.
course
.
id
)[
0
]
result
=
SkippedReverification
.
objects
.
filter
(
course_id
=
self
.
course
.
id
)[
0
]
self
.
assertEqual
(
result
.
checkpoint
,
self
.
checkpoint
)
self
.
assertEqual
(
result
.
checkpoint
,
self
.
checkpoint
)
self
.
assertEqual
(
result
.
user
,
self
.
user
)
self
.
assertEqual
(
result
.
user
,
self
.
user
)
self
.
assertEqual
(
result
.
course_id
,
self
.
course
.
id
)
self
.
assertEqual
(
result
.
course_id
,
self
.
course
.
id
)
def
test_unique_constraint
(
self
):
def
test_unique_constraint
(
self
):
"""
adding skipped re-verification with same user and course id will
"""
Test that adding skipped re-verification with same user and course
raise integrity exception
id will raise 'IntegrityError' exception.
"""
"""
# add verification object
# adding verification object
SkippedReverification
.
add_skipped_reverification_attempt
(
SkippedReverification
.
add_skipped_reverification_attempt
(
checkpoint
=
self
.
checkpoint
,
user_id
=
self
.
user
.
id
,
course_id
=
unicode
(
self
.
course
.
id
)
checkpoint
=
self
.
checkpoint
,
user_id
=
self
.
user
.
id
,
course_id
=
unicode
(
self
.
course
.
id
)
)
)
with
self
.
assertRaises
(
IntegrityError
):
with
self
.
assertRaises
(
IntegrityError
):
SkippedReverification
.
add_skipped_reverification_attempt
(
SkippedReverification
.
add_skipped_reverification_attempt
(
checkpoint
=
self
.
checkpoint
,
user_id
=
self
.
user
.
id
,
course_id
=
unicode
(
self
.
course
.
id
)
checkpoint
=
self
.
checkpoint
,
user_id
=
self
.
user
.
id
,
course_id
=
unicode
(
self
.
course
.
id
)
)
)
#
C
reate skipped attempt for different user
#
c
reate skipped attempt for different user
user2
=
UserFactory
.
create
()
user2
=
UserFactory
.
create
()
SkippedReverification
.
add_skipped_reverification_attempt
(
SkippedReverification
.
add_skipped_reverification_attempt
(
checkpoint
=
self
.
checkpoint
,
user_id
=
user2
.
id
,
course_id
=
unicode
(
self
.
course
.
id
)
checkpoint
=
self
.
checkpoint
,
user_id
=
user2
.
id
,
course_id
=
unicode
(
self
.
course
.
id
)
)
)
#
getting the status from db
#
test the status of skipped reverification from database
result
=
SkippedReverification
.
objects
.
filter
(
user
=
user2
)[
0
]
result
=
SkippedReverification
.
objects
.
filter
(
user
=
user2
)[
0
]
self
.
assertEqual
(
result
.
checkpoint
,
self
.
checkpoint
)
self
.
assertEqual
(
result
.
checkpoint
,
self
.
checkpoint
)
self
.
assertEqual
(
result
.
user
,
user2
)
self
.
assertEqual
(
result
.
user
,
user2
)
self
.
assertEqual
(
result
.
course_id
,
self
.
course
.
id
)
self
.
assertEqual
(
result
.
course_id
,
self
.
course
.
id
)
def
test_check_user_skipped_reverification_exists
(
self
):
def
test_check_user_skipped_reverification_exists
(
self
):
"""Checking check_user_skipped_reverification_exists method returns boolean status"""
"""
Test the 'check_user_skipped_reverification_exists' method's response.
# adding verification status
"""
# add verification status
SkippedReverification
.
add_skipped_reverification_attempt
(
SkippedReverification
.
add_skipped_reverification_attempt
(
checkpoint
=
self
.
checkpoint
,
user_id
=
self
.
user
.
id
,
course_id
=
unicode
(
self
.
course
.
id
)
checkpoint
=
self
.
checkpoint
,
user_id
=
self
.
user
.
id
,
course_id
=
unicode
(
self
.
course
.
id
)
)
)
self
.
assertTrue
(
self
.
assertTrue
(
SkippedReverification
.
check_user_skipped_reverification_exists
(
course_id
=
self
.
course
.
id
,
user
=
self
.
user
)
SkippedReverification
.
check_user_skipped_reverification_exists
(
course_id
=
self
.
course
.
id
,
user
=
self
.
user
)
)
)
...
...
lms/djangoapps/verify_student/tests/test_services.py
View file @
803bc553
...
@@ -31,89 +31,101 @@ class TestReverificationService(ModuleStoreTestCase):
...
@@ -31,89 +31,101 @@ class TestReverificationService(ModuleStoreTestCase):
min_price
=
100
,
min_price
=
100
,
)
)
self
.
item
=
ItemFactory
.
create
(
parent
=
course
,
category
=
'chapter'
,
display_name
=
'Test Section'
)
self
.
item
=
ItemFactory
.
create
(
parent
=
course
,
category
=
'chapter'
,
display_name
=
'Test Section'
)
self
.
final_checkpoint_location
=
u'i4x://{org}/{course}/edx-reverification-block/final_uuid'
.
format
(
org
=
self
.
course_key
.
org
,
course
=
self
.
course_key
.
course
)
@ddt.data
(
"final_term"
,
"mid_term"
)
@ddt.data
(
'final'
,
'midterm'
)
def
test_start_verification
(
self
,
checkpoint_name
):
def
test_start_verification
(
self
,
checkpoint_name
):
"""
"""Test the 'start_verification' service method.
Test the 'start_verification' service method. If checkpoint exists for
a specific course then return the checkpoint otherwise create that
Check that if a reverification checkpoint exists for a specific course
checkpoint.
then 'start_verification' method returns that checkpoint otherwise it
creates that checkpoint.
"""
"""
reverification_service
=
ReverificationService
()
reverification_service
=
ReverificationService
()
reverification_service
.
start_verification
(
unicode
(
self
.
course_key
),
checkpoint_name
,
self
.
item
.
location
)
checkpoint_location
=
u'i4x://{org}/{course}/edx-reverification-block/{checkpoint}'
.
format
(
org
=
self
.
course_key
.
org
,
course
=
self
.
course_key
.
course
,
checkpoint
=
checkpoint_name
)
expected_url
=
(
expected_url
=
(
'/verify_student/reverify'
'/verify_student/reverify'
'/{course_key}'
'/{course_key}'
'/{checkpoint_name}'
'/{checkpoint_location}/'
'/{usage_id}/'
)
.
format
(
course_key
=
unicode
(
self
.
course_key
),
checkpoint_location
=
checkpoint_location
)
)
.
format
(
course_key
=
unicode
(
self
.
course_key
),
checkpoint_name
=
checkpoint_name
,
usage_id
=
self
.
item
.
location
)
self
.
assertEqual
(
self
.
assertEqual
(
expected_url
,
reverification_service
.
start_verification
(
unicode
(
self
.
course_key
),
checkpoint_location
)
,
reverification_service
.
start_verification
(
unicode
(
self
.
course_key
),
checkpoint_name
,
self
.
item
.
location
)
expected_url
)
)
def
test_get_status
(
self
):
def
test_get_status
(
self
):
"""Test the verification statuses of a user for a given 'checkpoint'
and 'course_id'.
"""
"""
Test the verification statuses of a user for a given 'checkpoint' and
'course_id'.
"""
checkpoint_name
=
'final_term'
reverification_service
=
ReverificationService
()
reverification_service
=
ReverificationService
()
self
.
assertIsNone
(
reverification_service
.
get_status
(
self
.
user
.
id
,
unicode
(
self
.
course_key
),
checkpoint_name
))
self
.
assertIsNone
(
reverification_service
.
get_status
(
self
.
user
.
id
,
unicode
(
self
.
course_key
),
self
.
final_checkpoint_location
)
)
checkpoint_obj
=
VerificationCheckpoint
.
objects
.
create
(
checkpoint_obj
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
unicode
(
self
.
course_key
),
course_id
=
unicode
(
self
.
course_key
),
checkpoint_
name
=
checkpoint_name
checkpoint_
location
=
self
.
final_checkpoint_location
)
)
VerificationStatus
.
objects
.
create
(
checkpoint
=
checkpoint_obj
,
user
=
self
.
user
,
status
=
'submitted'
)
VerificationStatus
.
objects
.
create
(
checkpoint
=
checkpoint_obj
,
user
=
self
.
user
,
status
=
'submitted'
)
self
.
assertEqual
(
self
.
assertEqual
(
reverification_service
.
get_status
(
self
.
user
.
id
,
unicode
(
self
.
course_key
),
checkpoint_name
),
reverification_service
.
get_status
(
self
.
user
.
id
,
unicode
(
self
.
course_key
),
self
.
final_checkpoint_location
),
'submitted'
'submitted'
)
)
VerificationStatus
.
objects
.
create
(
checkpoint
=
checkpoint_obj
,
user
=
self
.
user
,
status
=
'
submitt
ed'
)
VerificationStatus
.
objects
.
create
(
checkpoint
=
checkpoint_obj
,
user
=
self
.
user
,
status
=
'
approv
ed'
)
self
.
assertEqual
(
self
.
assertEqual
(
reverification_service
.
get_status
(
self
.
user
.
id
,
unicode
(
self
.
course_key
),
checkpoint_name
),
reverification_service
.
get_status
(
self
.
user
.
id
,
unicode
(
self
.
course_key
),
self
.
final_checkpoint_location
),
'
submitt
ed'
'
approv
ed'
)
)
def
test_skip_verification
(
self
):
def
test_skip_verification
(
self
):
"""
"""
Adding the test skip verification attempt for the user
Test adding skip attempt of a user for a reverification checkpoint.
"""
"""
checkpoint_name
=
'final_term'
reverification_service
=
ReverificationService
()
reverification_service
=
ReverificationService
()
VerificationCheckpoint
.
objects
.
create
(
VerificationCheckpoint
.
objects
.
create
(
course_id
=
unicode
(
self
.
course_key
),
course_id
=
unicode
(
self
.
course_key
),
checkpoint_
name
=
checkpoint_name
checkpoint_
location
=
self
.
final_checkpoint_location
)
)
reverification_service
.
skip_verification
(
checkpoint_name
,
self
.
user
.
id
,
unicode
(
self
.
course_key
))
reverification_service
.
skip_verification
(
self
.
user
.
id
,
unicode
(
self
.
course_key
),
self
.
final_checkpoint_location
)
self
.
assertEqual
(
1
,
SkippedReverification
.
objects
.
filter
(
user
=
self
.
user
,
course_id
=
self
.
course_key
)
.
count
())
self
.
assertEqual
(
SkippedReverification
.
objects
.
filter
(
user
=
self
.
user
,
course_id
=
self
.
course_key
)
.
count
(),
1
)
reverification_service
.
skip_verification
(
checkpoint_name
,
self
.
user
.
id
,
unicode
(
self
.
course_key
))
# now test that a user can have only one entry for a skipped
self
.
assertEqual
(
1
,
SkippedReverification
.
objects
.
filter
(
user
=
self
.
user
,
course_id
=
self
.
course_key
)
.
count
())
# reverification for a course
reverification_service
.
skip_verification
(
self
.
user
.
id
,
unicode
(
self
.
course_key
),
self
.
final_checkpoint_location
)
self
.
assertEqual
(
SkippedReverification
.
objects
.
filter
(
user
=
self
.
user
,
course_id
=
self
.
course_key
)
.
count
(),
1
)
def
test_get_attempts
(
self
):
def
test_get_attempts
(
self
):
"""
"""Check verification attempts count against a user for a given
Check verification attempts count against a user for a given
'checkpoint' and 'course_id'.
'checkpoint' and 'course_id'.
"""
"""
checkpoint_name
=
'final_term'
reverification_service
=
ReverificationService
()
reverification_service
=
ReverificationService
()
course_id
=
unicode
(
self
.
course_key
)
course_id
=
unicode
(
self
.
course_key
)
self
.
assertEqual
(
self
.
assertEqual
(
reverification_service
.
get_attempts
(
self
.
user
.
id
,
course_id
,
checkpoint_name
,
location_id
=
None
),
reverification_service
.
get_attempts
(
self
.
user
.
id
,
course_id
,
self
.
final_checkpoint_location
),
0
0
)
)
# now create a checkpoint and add user's entry against it then test
# now create a checkpoint and add user's entry against it then test
# that the 'get_attempts' service method returns count accordingly
# that the 'get_attempts' service method returns correct count
checkpoint_obj
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
course_id
,
checkpoint_name
=
checkpoint_name
)
checkpoint_obj
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
course_id
,
checkpoint_location
=
self
.
final_checkpoint_location
)
VerificationStatus
.
objects
.
create
(
checkpoint
=
checkpoint_obj
,
user
=
self
.
user
,
status
=
'submitted'
)
VerificationStatus
.
objects
.
create
(
checkpoint
=
checkpoint_obj
,
user
=
self
.
user
,
status
=
'submitted'
)
self
.
assertEqual
(
self
.
assertEqual
(
reverification_service
.
get_attempts
(
self
.
user
.
id
,
course_id
,
checkpoint_name
,
location_id
=
None
),
reverification_service
.
get_attempts
(
self
.
user
.
id
,
course_id
,
self
.
final_checkpoint_location
),
1
1
)
)
lms/djangoapps/verify_student/tests/test_views.py
View file @
803bc553
...
@@ -2,42 +2,41 @@
...
@@ -2,42 +2,41 @@
"""
"""
Tests of verify_student views.
Tests of verify_student views.
"""
"""
import
json
import
json
import
urllib
import
urllib
from
datetime
import
timedelta
,
datetime
from
datetime
import
timedelta
,
datetime
from
uuid
import
uuid4
from
uuid
import
uuid4
from
django.test.utils
import
override_settings
import
ddt
import
httpretty
import
mock
import
mock
from
mock
import
patch
,
Mock
,
ANY
from
django.utils
import
timezone
import
pytz
import
pytz
import
ddt
from
bs4
import
BeautifulSoup
from
django.test.client
import
Client
from
mock
import
patch
,
Mock
,
ANY
from
django.test
import
TestCase
from
django.conf
import
settings
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.core.exceptions
import
ObjectDoesNotExist
from
django.core.exceptions
import
ObjectDoesNotExist
from
django.core
import
mail
from
django.core
import
mail
import
httpretty
from
django.test
import
TestCase
from
bs4
import
BeautifulSoup
from
django.test.client
import
Client
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
django.test.utils
import
override_settings
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
from
django.utils
import
timezone
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore
import
ModuleStoreEnum
from
xmodule.modulestore.tests.factories
import
check_mongo_calls
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
opaque_keys.edx.locator
import
CourseLocator
from
opaque_keys.edx.locator
import
CourseLocator
from
microsite_configuration
import
microsite
from
openedx.core.djangoapps.user_api.accounts.api
import
get_account_settings
from
course_modes.models
import
CourseMode
from
course_modes.tests.factories
import
CourseModeFactory
from
commerce.tests
import
TEST_PAYMENT_DATA
,
TEST_API_URL
,
TEST_API_SIGNING_KEY
from
commerce.tests
import
TEST_PAYMENT_DATA
,
TEST_API_URL
,
TEST_API_SIGNING_KEY
from
embargo.test_utils
import
restrict_course
from
microsite_configuration
import
microsite
from
openedx.core.djangoapps.user_api.accounts.api
import
get_account_settings
from
shoppingcart.models
import
Order
,
CertificateItem
from
student.tests.factories
import
UserFactory
,
CourseEnrollmentFactory
from
student.tests.factories
import
UserFactory
,
CourseEnrollmentFactory
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
from
course_modes.tests.factories
import
CourseModeFactory
from
util.date_utils
import
get_default_time_display
from
course_modes.models
import
CourseMode
from
shoppingcart.models
import
Order
,
CertificateItem
from
embargo.test_utils
import
restrict_course
from
util.testing
import
UrlResetMixin
from
util.testing
import
UrlResetMixin
from
verify_student.views
import
(
from
verify_student.views
import
(
checkout_with_ecommerce_service
,
checkout_with_ecommerce_service
,
...
@@ -48,7 +47,11 @@ from verify_student.models import (
...
@@ -48,7 +47,11 @@ from verify_student.models import (
SoftwareSecurePhotoVerification
,
VerificationCheckpoint
,
SoftwareSecurePhotoVerification
,
VerificationCheckpoint
,
InCourseReverificationConfiguration
,
VerificationStatus
InCourseReverificationConfiguration
,
VerificationStatus
)
)
from
util.date_utils
import
get_default_time_display
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore
import
ModuleStoreEnum
from
xmodule.modulestore.tests.factories
import
check_mongo_calls
def
mock_render_to_response
(
*
args
,
**
kwargs
):
def
mock_render_to_response
(
*
args
,
**
kwargs
):
...
@@ -1598,16 +1601,9 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase):
...
@@ -1598,16 +1601,9 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase):
mock_send_email
.
assert_called_once_with
(
self
.
user
.
id
,
subject
,
ANY
)
mock_send_email
.
assert_called_once_with
(
self
.
user
.
id
,
subject
,
ANY
)
def
create_reverification_xblock
(
self
):
def
create_reverification_xblock
(
self
):
""" Create the reverification xblock
"""
"""
# Create checkpoint
Create the reverification XBlock.
checkpoint
=
VerificationCheckpoint
(
course_id
=
self
.
course_id
,
checkpoint_name
=
"midterm"
)
"""
checkpoint
.
save
()
# Add a re-verification attempt
checkpoint
.
add_verification_attempt
(
self
.
attempt
)
# Create the 'edx-reverification-block' in course tree
# Create the 'edx-reverification-block' in course tree
section
=
ItemFactory
.
create
(
parent
=
self
.
course
,
category
=
'chapter'
,
display_name
=
'Test Section'
)
section
=
ItemFactory
.
create
(
parent
=
self
.
course
,
category
=
'chapter'
,
display_name
=
'Test Section'
)
subsection
=
ItemFactory
.
create
(
parent
=
section
,
category
=
'sequential'
,
display_name
=
'Test Subsection'
)
subsection
=
ItemFactory
.
create
(
parent
=
section
,
category
=
'sequential'
,
display_name
=
'Test Subsection'
)
...
@@ -1618,13 +1614,20 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase):
...
@@ -1618,13 +1614,20 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase):
display_name
=
'Test Verification Block'
display_name
=
'Test Verification Block'
)
)
# Create checkpoint
checkpoint
=
VerificationCheckpoint
(
course_id
=
self
.
course_id
,
checkpoint_location
=
reverification
.
location
)
checkpoint
.
save
()
# Add a re-verification attempt
checkpoint
.
add_verification_attempt
(
self
.
attempt
)
# Add a re-verification attempt status for the user
# Add a re-verification attempt status for the user
VerificationStatus
.
add_verification_status
(
checkpoint
,
self
.
user
,
"submitted"
,
reverification
.
location
)
VerificationStatus
.
add_verification_status
(
checkpoint
,
self
.
user
,
"submitted"
)
class
TestReverifyView
(
ModuleStoreTestCase
):
class
TestReverifyView
(
ModuleStoreTestCase
):
"""
"""
Tests for the reverification views
Tests for the reverification views
.
"""
"""
def
setUp
(
self
):
def
setUp
(
self
):
super
(
TestReverifyView
,
self
)
.
setUp
()
super
(
TestReverifyView
,
self
)
.
setUp
()
...
@@ -1675,7 +1678,6 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
...
@@ -1675,7 +1678,6 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
Tests for the incourse reverification views.
Tests for the incourse reverification views.
"""
"""
IMAGE_DATA
=
"abcd,1234"
IMAGE_DATA
=
"abcd,1234"
MIDTERM
=
"midterm"
def
build_course
(
self
):
def
build_course
(
self
):
"""
"""
...
@@ -1695,7 +1697,7 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
...
@@ -1695,7 +1697,7 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
section
=
ItemFactory
.
create
(
parent
=
self
.
course
,
category
=
'chapter'
,
display_name
=
'Test Section'
)
section
=
ItemFactory
.
create
(
parent
=
self
.
course
,
category
=
'chapter'
,
display_name
=
'Test Section'
)
subsection
=
ItemFactory
.
create
(
parent
=
section
,
category
=
'sequential'
,
display_name
=
'Test Subsection'
)
subsection
=
ItemFactory
.
create
(
parent
=
section
,
category
=
'sequential'
,
display_name
=
'Test Subsection'
)
vertical
=
ItemFactory
.
create
(
parent
=
subsection
,
category
=
'vertical'
,
display_name
=
'Test Unit'
)
vertical
=
ItemFactory
.
create
(
parent
=
subsection
,
category
=
'vertical'
,
display_name
=
'Test Unit'
)
reverification
=
ItemFactory
.
create
(
self
.
reverification
=
ItemFactory
.
create
(
parent
=
vertical
,
parent
=
vertical
,
category
=
'edx-reverification-block'
,
category
=
'edx-reverification-block'
,
display_name
=
'Test Verification Block'
display_name
=
'Test Verification Block'
...
@@ -1703,7 +1705,8 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
...
@@ -1703,7 +1705,8 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
self
.
section_location
=
section
.
location
self
.
section_location
=
section
.
location
self
.
subsection_location
=
subsection
.
location
self
.
subsection_location
=
subsection
.
location
self
.
vertical_location
=
vertical
.
location
self
.
vertical_location
=
vertical
.
location
self
.
reverification_location
=
reverification
.
location
self
.
reverification_location
=
unicode
(
self
.
reverification
.
location
)
self
.
reverification_assessment
=
self
.
reverification
.
related_assessment
def
setUp
(
self
):
def
setUp
(
self
):
super
(
TestInCourseReverifyView
,
self
)
.
setUp
()
super
(
TestInCourseReverifyView
,
self
)
.
setUp
()
...
@@ -1727,12 +1730,12 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
...
@@ -1727,12 +1730,12 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
def
test_incourse_reverify_feature_flag_get
(
self
):
def
test_incourse_reverify_feature_flag_get
(
self
):
self
.
config
.
enabled
=
False
self
.
config
.
enabled
=
False
self
.
config
.
save
()
self
.
config
.
save
()
response
=
self
.
client
.
get
(
self
.
_get_url
(
self
.
course_key
,
self
.
MIDTERM
))
response
=
self
.
client
.
get
(
self
.
_get_url
(
self
.
course_key
,
self
.
reverification_location
))
self
.
assertEquals
(
response
.
status_code
,
404
)
self
.
assertEquals
(
response
.
status_code
,
404
)
@patch.dict
(
settings
.
FEATURES
,
{
'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'
:
True
})
@patch.dict
(
settings
.
FEATURES
,
{
'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'
:
True
})
def
test_incourse_reverify_invalid_course_get
(
self
):
def
test_incourse_reverify_invalid_course_get
(
self
):
response
=
self
.
client
.
get
(
self
.
_get_url
(
"invalid/course/key"
,
self
.
MIDTERM
))
response
=
self
.
client
.
get
(
self
.
_get_url
(
"invalid/course/key"
,
self
.
reverification_location
))
self
.
assertEquals
(
response
.
status_code
,
404
)
self
.
assertEquals
(
response
.
status_code
,
404
)
...
@@ -1744,7 +1747,7 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
...
@@ -1744,7 +1747,7 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
@patch.dict
(
settings
.
FEATURES
,
{
'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'
:
True
})
@patch.dict
(
settings
.
FEATURES
,
{
'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'
:
True
})
def
test_incourse_reverify_initial_redirect_get
(
self
):
def
test_incourse_reverify_initial_redirect_get
(
self
):
self
.
_create_checkpoint
()
self
.
_create_checkpoint
()
response
=
self
.
client
.
get
(
self
.
_get_url
(
self
.
course_key
,
self
.
MIDTERM
))
response
=
self
.
client
.
get
(
self
.
_get_url
(
self
.
course_key
,
self
.
reverification_location
))
url
=
reverse
(
'verify_student_verify_now'
,
kwargs
=
{
"course_id"
:
unicode
(
self
.
course_key
)})
url
=
reverse
(
'verify_student_verify_now'
,
kwargs
=
{
"course_id"
:
unicode
(
self
.
course_key
)})
self
.
assertRedirects
(
response
,
url
)
self
.
assertRedirects
(
response
,
url
)
...
@@ -1752,20 +1755,24 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
...
@@ -1752,20 +1755,24 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
@override_settings
(
SEGMENT_IO_LMS_KEY
=
"foobar"
)
@override_settings
(
SEGMENT_IO_LMS_KEY
=
"foobar"
)
@patch.dict
(
settings
.
FEATURES
,
{
'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'
:
True
,
'SEGMENT_IO_LMS'
:
True
})
@patch.dict
(
settings
.
FEATURES
,
{
'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'
:
True
,
'SEGMENT_IO_LMS'
:
True
})
def
test_incourse_reverify_get
(
self
):
def
test_incourse_reverify_get
(
self
):
"""
Test incourse reverification.
"""
self
.
_create_checkpoint
()
self
.
_create_checkpoint
()
self
.
_create_initial_verification
()
self
.
_create_initial_verification
()
response
=
self
.
client
.
get
(
self
.
_get_url
(
self
.
course_key
,
self
.
MIDTERM
))
response
=
self
.
client
.
get
(
self
.
_get_url
(
self
.
course_key
,
self
.
reverification_location
))
self
.
assertEquals
(
response
.
status_code
,
200
)
self
.
assertEquals
(
response
.
status_code
,
200
)
#Verify Google Analytics event fired after successfully submiting the picture
# verify that Google Analytics event fires after successfully
# submitting the photo verification
self
.
mock_tracker
.
track
.
assert_called_once_with
(
# pylint: disable=no-member
self
.
mock_tracker
.
track
.
assert_called_once_with
(
# pylint: disable=no-member
self
.
user
.
id
,
# pylint: disable=no-member
self
.
user
.
id
,
# pylint: disable=no-member
EVENT_NAME_USER_ENTERED_INCOURSE_REVERIFY_VIEW
,
EVENT_NAME_USER_ENTERED_INCOURSE_REVERIFY_VIEW
,
{
{
'category'
:
"verification"
,
'category'
:
"verification"
,
'label'
:
unicode
(
self
.
course_key
),
'label'
:
unicode
(
self
.
course_key
),
'checkpoint'
:
self
.
MIDTERM
'checkpoint'
:
self
.
reverification_assessment
},
},
context
=
{
context
=
{
...
@@ -1776,22 +1783,19 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
...
@@ -1776,22 +1783,19 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
self
.
mock_tracker
.
reset_mock
()
self
.
mock_tracker
.
reset_mock
()
@patch.dict
(
settings
.
FEATURES
,
{
'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'
:
True
})
@patch.dict
(
settings
.
FEATURES
,
{
'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'
:
True
})
@patch
(
'verify_student.views.render_to_response'
,
render_mock
)
def
test_checkpoint_post
(
self
):
def
test_invalid_checkpoint_post
(
self
):
"""Verify that POST requests including an invalid checkpoint location
results in a 400 response.
response
=
self
.
client
.
post
(
self
.
_get_url
(
self
.
course_key
,
self
.
MIDTERM
))
"""
self
.
assertEquals
(
response
.
status_code
,
200
)
response
=
self
.
client
.
post
(
self
.
_get_url
(
self
.
course_key
,
self
.
reverification_location
))
((
template
,
context
),
_kwargs
)
=
render_mock
.
call_args
# pylint: disable=unpacking-non-sequence
self
.
assertEquals
(
response
.
status_code
,
400
)
self
.
assertIn
(
'incourse_reverify'
,
template
)
self
.
assertTrue
(
context
[
'error'
])
@patch.dict
(
settings
.
FEATURES
,
{
'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'
:
True
})
@patch.dict
(
settings
.
FEATURES
,
{
'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'
:
True
})
def
test_incourse_reverify_initial_redirect_post
(
self
):
def
test_incourse_reverify_initial_redirect_post
(
self
):
self
.
_create_checkpoint
()
self
.
_create_checkpoint
()
response
=
self
.
client
.
post
(
self
.
_get_url
(
self
.
course_key
,
self
.
reverification_location
))
response
=
self
.
client
.
post
(
self
.
_get_url
(
self
.
course_key
,
self
.
MIDTERM
))
url
=
reverse
(
'verify_student_verify_now'
,
kwargs
=
{
"course_id"
:
unicode
(
self
.
course_key
)})
url
=
reverse
(
'verify_student_verify_now'
,
kwargs
=
{
"course_id"
:
unicode
(
self
.
course_key
)})
self
.
assertRedirects
(
response
,
url
)
self
.
assertRedirects
(
response
,
url
)
@patch.dict
(
settings
.
FEATURES
,
{
'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'
:
True
})
@patch.dict
(
settings
.
FEATURES
,
{
'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'
:
True
})
...
@@ -1799,7 +1803,7 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
...
@@ -1799,7 +1803,7 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
self
.
_create_checkpoint
()
self
.
_create_checkpoint
()
self
.
_create_initial_verification
()
self
.
_create_initial_verification
()
response
=
self
.
client
.
post
(
self
.
_get_url
(
self
.
course_key
,
self
.
MIDTERM
),
{
"face_image"
:
""
})
response
=
self
.
client
.
post
(
self
.
_get_url
(
self
.
course_key
,
self
.
reverification_location
),
{
"face_image"
:
""
})
self
.
assertEqual
(
response
.
status_code
,
400
)
self
.
assertEqual
(
response
.
status_code
,
400
)
@override_settings
(
SEGMENT_IO_LMS_KEY
=
"foobar"
)
@override_settings
(
SEGMENT_IO_LMS_KEY
=
"foobar"
)
...
@@ -1808,19 +1812,22 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
...
@@ -1808,19 +1812,22 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
self
.
_create_checkpoint
()
self
.
_create_checkpoint
()
self
.
_create_initial_verification
()
self
.
_create_initial_verification
()
response
=
self
.
client
.
post
(
self
.
_get_url
(
self
.
course_key
,
self
.
MIDTERM
),
{
"face_image"
:
self
.
IMAGE_DATA
})
response
=
self
.
client
.
post
(
self
.
_get_url
(
self
.
course_key
,
self
.
reverification_location
),
{
"face_image"
:
self
.
IMAGE_DATA
}
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
#Verify Google Analytics event fired after successfully submiting the picture
# test that Google Analytics event firs after successfully submitting
# photo verification
self
.
mock_tracker
.
track
.
assert_called_once_with
(
# pylint: disable=no-member
self
.
mock_tracker
.
track
.
assert_called_once_with
(
# pylint: disable=no-member
self
.
user
.
id
,
# pylint: disable=no-member
self
.
user
.
id
,
EVENT_NAME_USER_SUBMITTED_INCOURSE_REVERIFY
,
EVENT_NAME_USER_SUBMITTED_INCOURSE_REVERIFY
,
{
{
'category'
:
"verification"
,
'category'
:
"verification"
,
'label'
:
unicode
(
self
.
course_key
),
'label'
:
unicode
(
self
.
course_key
),
'checkpoint'
:
self
.
MIDTERM
'checkpoint'
:
self
.
reverification_assessment
},
},
context
=
{
context
=
{
'Google Analytics'
:
'Google Analytics'
:
{
'clientId'
:
None
}
{
'clientId'
:
None
}
...
@@ -1833,37 +1840,43 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
...
@@ -1833,37 +1840,43 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
self
.
config
.
enabled
=
False
self
.
config
.
enabled
=
False
self
.
config
.
save
()
self
.
config
.
save
()
response
=
self
.
client
.
post
(
self
.
_get_url
(
self
.
course_key
,
self
.
MIDTERM
))
response
=
self
.
client
.
post
(
self
.
_get_url
(
self
.
course_key
,
self
.
reverification_location
))
self
.
assertEquals
(
response
.
status_code
,
404
)
self
.
assertEquals
(
response
.
status_code
,
404
)
def
_create_checkpoint
(
self
):
def
_create_checkpoint
(
self
):
"""helper method for creating checkpoint"""
"""
checkpoint
=
VerificationCheckpoint
(
course_id
=
self
.
course_key
,
checkpoint_name
=
self
.
MIDTERM
)
Helper method for creating a reverification checkpoint.
"""
checkpoint
=
VerificationCheckpoint
(
course_id
=
self
.
course_key
,
checkpoint_location
=
self
.
reverification_location
)
checkpoint
.
save
()
checkpoint
.
save
()
def
_create_initial_verification
(
self
):
def
_create_initial_verification
(
self
):
"""helper method for initial verification"""
"""
Helper method for initial verification.
"""
attempt
=
SoftwareSecurePhotoVerification
(
user
=
self
.
user
)
attempt
=
SoftwareSecurePhotoVerification
(
user
=
self
.
user
)
attempt
.
mark_ready
()
attempt
.
mark_ready
()
attempt
.
save
()
attempt
.
save
()
attempt
.
submit
()
attempt
.
submit
()
def
_get_url
(
self
,
course_key
,
checkpoint
):
def
_get_url
(
self
,
course_key
,
checkpoint_location
):
"""contruct the url.
"""
Construct the reverification url.
Arguments:
Arguments:
course_key (unicode): The ID of the course.
course_key (unicode): The ID of the course
checkpoint (str): The verification checkpoint
checkpoint_location (str): Location of verification checkpoint
Returns:
Returns:
url
url
"""
"""
return
reverse
(
'verify_student_incourse_reverify'
,
return
reverse
(
'verify_student_incourse_reverify'
,
kwargs
=
{
kwargs
=
{
"course_id"
:
unicode
(
course_key
),
"course_id"
:
unicode
(
course_key
),
"checkpoint_name"
:
checkpoint
,
"usage_id"
:
checkpoint_location
"usage_id"
:
unicode
(
self
.
reverification_location
)
}
}
)
)
class
TestEmailMessageWithCustomICRVBlock
(
ModuleStoreTestCase
):
class
TestEmailMessageWithCustomICRVBlock
(
ModuleStoreTestCase
):
...
@@ -1875,8 +1888,6 @@ class TestEmailMessageWithCustomICRVBlock(ModuleStoreTestCase):
...
@@ -1875,8 +1888,6 @@ class TestEmailMessageWithCustomICRVBlock(ModuleStoreTestCase):
"""
"""
Build up a course tree with a Reverificaiton xBlock.
Build up a course tree with a Reverificaiton xBlock.
"""
"""
# pylint: disable=attribute-defined-outside-init
self
.
course_key
=
SlashSeparatedCourseKey
(
"Robot"
,
"999"
,
"Test_Course"
)
self
.
course_key
=
SlashSeparatedCourseKey
(
"Robot"
,
"999"
,
"Test_Course"
)
self
.
course
=
CourseFactory
.
create
(
org
=
'Robot'
,
number
=
'999'
,
display_name
=
'Test Course'
)
self
.
course
=
CourseFactory
.
create
(
org
=
'Robot'
,
number
=
'999'
,
display_name
=
'Test Course'
)
self
.
due_date
=
datetime
(
2015
,
6
,
22
,
tzinfo
=
pytz
.
UTC
)
self
.
due_date
=
datetime
(
2015
,
6
,
22
,
tzinfo
=
pytz
.
UTC
)
...
@@ -1901,40 +1912,41 @@ class TestEmailMessageWithCustomICRVBlock(ModuleStoreTestCase):
...
@@ -1901,40 +1912,41 @@ class TestEmailMessageWithCustomICRVBlock(ModuleStoreTestCase):
self
.
section_location
=
section
.
location
self
.
section_location
=
section
.
location
self
.
subsection_location
=
subsection
.
location
self
.
subsection_location
=
subsection
.
location
self
.
vertical_location
=
vertical
.
location
self
.
vertical_location
=
vertical
.
location
self
.
reverification_location
=
self
.
reverification
.
location
self
.
reverification_location
=
unicode
(
self
.
reverification
.
location
)
self
.
assessment
=
"midterm"
self
.
assessment
=
self
.
reverification
.
related_assessment
self
.
re_verification_link
=
reverse
(
self
.
re_verification_link
=
reverse
(
'verify_student_incourse_reverify'
,
'verify_student_incourse_reverify'
,
args
=
(
args
=
(
unicode
(
self
.
course_key
),
unicode
(
self
.
course_key
),
unicode
(
self
.
assessment
),
self
.
reverification_location
unicode
(
self
.
reverification_location
)
)
)
)
)
def
setUp
(
self
):
def
setUp
(
self
):
"""
Setup method for testing photo verification email messages.
"""
super
(
TestEmailMessageWithCustomICRVBlock
,
self
)
.
setUp
()
super
(
TestEmailMessageWithCustomICRVBlock
,
self
)
.
setUp
()
self
.
build_course
()
self
.
build_course
()
self
.
check_point
=
VerificationCheckpoint
.
objects
.
create
(
self
.
check_point
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_
name
=
self
.
assessment
course_id
=
self
.
course
.
id
,
checkpoint_
location
=
self
.
reverification_location
)
)
self
.
check_point
.
add_verification_attempt
(
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
))
self
.
check_point
.
add_verification_attempt
(
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
))
VerificationStatus
.
add_verification_status
(
VerificationStatus
.
add_verification_status
(
checkpoint
=
self
.
check_point
,
checkpoint
=
self
.
check_point
,
user
=
self
.
user
,
user
=
self
.
user
,
status
=
'submitted'
,
status
=
'submitted'
location_id
=
self
.
reverification_location
)
)
self
.
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
filter
(
user
=
self
.
user
)
self
.
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
filter
(
user
=
self
.
user
)
def
test_approved_email_message
(
self
):
def
test_approved_email_message
(
self
):
"""
Test email message for approved photo verification.
"""
subject
,
body
=
_compose_message_reverification_email
(
subject
,
body
=
_compose_message_reverification_email
(
self
.
course
.
id
,
self
.
user
.
id
,
"midterm"
,
self
.
attempt
,
"approved"
,
True
self
.
course
.
id
,
self
.
user
.
id
,
self
.
reverification_location
,
"approved"
,
True
)
)
self
.
assertIn
(
self
.
assertIn
(
...
@@ -1944,13 +1956,12 @@ class TestEmailMessageWithCustomICRVBlock(ModuleStoreTestCase):
...
@@ -1944,13 +1956,12 @@ class TestEmailMessageWithCustomICRVBlock(ModuleStoreTestCase):
),
),
body
body
)
)
self
.
assertIn
(
"Re-verification Status"
,
subject
)
self
.
assertIn
(
"Re-verification Status"
,
subject
)
def
test_denied_email_message_with_valid_due_date_and_attempts_allowed
(
self
):
def
test_denied_email_message_with_valid_due_date_and_attempts_allowed
(
self
):
__
,
body
=
_compose_message_reverification_email
(
__
,
body
=
_compose_message_reverification_email
(
self
.
course
.
id
,
self
.
user
.
id
,
"midterm"
,
self
.
attempt
,
"denied"
,
True
self
.
course
.
id
,
self
.
user
.
id
,
self
.
reverification_location
,
"denied"
,
True
)
)
self
.
assertIn
(
self
.
assertIn
(
...
@@ -1975,7 +1986,7 @@ class TestEmailMessageWithCustomICRVBlock(ModuleStoreTestCase):
...
@@ -1975,7 +1986,7 @@ class TestEmailMessageWithCustomICRVBlock(ModuleStoreTestCase):
return_value
=
datetime
(
2016
,
1
,
1
,
tzinfo
=
timezone
.
utc
)
return_value
=
datetime
(
2016
,
1
,
1
,
tzinfo
=
timezone
.
utc
)
with
patch
.
object
(
timezone
,
'now'
,
return_value
=
return_value
):
with
patch
.
object
(
timezone
,
'now'
,
return_value
=
return_value
):
__
,
body
=
_compose_message_reverification_email
(
__
,
body
=
_compose_message_reverification_email
(
self
.
course
.
id
,
self
.
user
.
id
,
"midterm"
,
self
.
attempt
,
"denied"
,
True
self
.
course
.
id
,
self
.
user
.
id
,
self
.
reverification_location
,
"denied"
,
True
)
)
self
.
assertIn
(
self
.
assertIn
(
...
@@ -1991,7 +2002,7 @@ class TestEmailMessageWithCustomICRVBlock(ModuleStoreTestCase):
...
@@ -1991,7 +2002,7 @@ class TestEmailMessageWithCustomICRVBlock(ModuleStoreTestCase):
def
test_check_num_queries
(
self
):
def
test_check_num_queries
(
self
):
# Get the re-verification block to check the call made
# Get the re-verification block to check the call made
with
check_mongo_calls
(
2
):
with
check_mongo_calls
(
2
):
ver_block
=
modulestore
()
.
get_item
(
self
.
reverification
_
location
)
ver_block
=
modulestore
()
.
get_item
(
self
.
reverification
.
location
)
# Expect that the verification block is fetched
# Expect that the verification block is fetched
self
.
assertIsNotNone
(
ver_block
)
self
.
assertIsNotNone
(
ver_block
)
...
@@ -2006,8 +2017,6 @@ class TestEmailMessageWithDefaultICRVBlock(ModuleStoreTestCase):
...
@@ -2006,8 +2017,6 @@ class TestEmailMessageWithDefaultICRVBlock(ModuleStoreTestCase):
"""
"""
Build up a course tree with a Reverificaiton xBlock.
Build up a course tree with a Reverificaiton xBlock.
"""
"""
# pylint: disable=attribute-defined-outside-init
self
.
course_key
=
SlashSeparatedCourseKey
(
"Robot"
,
"999"
,
"Test_Course"
)
self
.
course_key
=
SlashSeparatedCourseKey
(
"Robot"
,
"999"
,
"Test_Course"
)
self
.
course
=
CourseFactory
.
create
(
org
=
'Robot'
,
number
=
'999'
,
display_name
=
'Test Course'
)
self
.
course
=
CourseFactory
.
create
(
org
=
'Robot'
,
number
=
'999'
,
display_name
=
'Test Course'
)
...
@@ -2030,15 +2039,14 @@ class TestEmailMessageWithDefaultICRVBlock(ModuleStoreTestCase):
...
@@ -2030,15 +2039,14 @@ class TestEmailMessageWithDefaultICRVBlock(ModuleStoreTestCase):
self
.
section_location
=
section
.
location
self
.
section_location
=
section
.
location
self
.
subsection_location
=
subsection
.
location
self
.
subsection_location
=
subsection
.
location
self
.
vertical_location
=
vertical
.
location
self
.
vertical_location
=
vertical
.
location
self
.
reverification_location
=
self
.
reverification
.
location
self
.
reverification_location
=
unicode
(
self
.
reverification
.
location
)
self
.
assessment
=
"midterm"
self
.
assessment
=
self
.
reverification
.
related_assessment
self
.
re_verification_link
=
reverse
(
self
.
re_verification_link
=
reverse
(
'verify_student_incourse_reverify'
,
'verify_student_incourse_reverify'
,
args
=
(
args
=
(
unicode
(
self
.
course_key
),
unicode
(
self
.
course_key
),
unicode
(
self
.
assessment
),
self
.
reverification_location
unicode
(
self
.
reverification_location
)
)
)
)
)
...
@@ -2047,7 +2055,7 @@ class TestEmailMessageWithDefaultICRVBlock(ModuleStoreTestCase):
...
@@ -2047,7 +2055,7 @@ class TestEmailMessageWithDefaultICRVBlock(ModuleStoreTestCase):
self
.
build_course
()
self
.
build_course
()
self
.
check_point
=
VerificationCheckpoint
.
objects
.
create
(
self
.
check_point
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
self
.
course
.
id
,
checkpoint_
name
=
self
.
assessment
course_id
=
self
.
course
.
id
,
checkpoint_
location
=
self
.
reverification_location
)
)
self
.
check_point
.
add_verification_attempt
(
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
))
self
.
check_point
.
add_verification_attempt
(
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
))
self
.
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
filter
(
user
=
self
.
user
)
self
.
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
filter
(
user
=
self
.
user
)
...
@@ -2057,12 +2065,11 @@ class TestEmailMessageWithDefaultICRVBlock(ModuleStoreTestCase):
...
@@ -2057,12 +2065,11 @@ class TestEmailMessageWithDefaultICRVBlock(ModuleStoreTestCase):
VerificationStatus
.
add_verification_status
(
VerificationStatus
.
add_verification_status
(
checkpoint
=
self
.
check_point
,
checkpoint
=
self
.
check_point
,
user
=
self
.
user
,
user
=
self
.
user
,
status
=
'submitted'
,
status
=
'submitted'
location_id
=
self
.
reverification_location
)
)
__
,
body
=
_compose_message_reverification_email
(
__
,
body
=
_compose_message_reverification_email
(
self
.
course
.
id
,
self
.
user
.
id
,
"midterm"
,
self
.
attempt
,
"denied"
,
True
self
.
course
.
id
,
self
.
user
.
id
,
self
.
reverification_location
,
"denied"
,
True
)
)
self
.
assertIn
(
self
.
assertIn
(
...
@@ -2082,11 +2089,10 @@ class TestEmailMessageWithDefaultICRVBlock(ModuleStoreTestCase):
...
@@ -2082,11 +2089,10 @@ class TestEmailMessageWithDefaultICRVBlock(ModuleStoreTestCase):
VerificationStatus
.
add_verification_status
(
VerificationStatus
.
add_verification_status
(
checkpoint
=
self
.
check_point
,
checkpoint
=
self
.
check_point
,
user
=
self
.
user
,
user
=
self
.
user
,
status
=
'submitted'
,
status
=
'submitted'
location_id
=
self
.
reverification_location
)
)
__
,
body
=
_compose_message_reverification_email
(
__
,
body
=
_compose_message_reverification_email
(
self
.
course
.
id
,
self
.
user
.
id
,
"midterm"
,
self
.
attempt
,
"denied"
,
True
self
.
course
.
id
,
self
.
user
.
id
,
self
.
reverification_location
,
"denied"
,
True
)
)
self
.
assertIn
(
self
.
assertIn
(
...
@@ -2104,12 +2110,11 @@ class TestEmailMessageWithDefaultICRVBlock(ModuleStoreTestCase):
...
@@ -2104,12 +2110,11 @@ class TestEmailMessageWithDefaultICRVBlock(ModuleStoreTestCase):
VerificationStatus
.
add_verification_status
(
VerificationStatus
.
add_verification_status
(
checkpoint
=
self
.
check_point
,
checkpoint
=
self
.
check_point
,
user
=
self
.
user
,
user
=
self
.
user
,
status
=
'error'
,
status
=
'error'
location_id
=
self
.
reverification_location
)
)
__
,
body
=
_compose_message_reverification_email
(
__
,
body
=
_compose_message_reverification_email
(
self
.
course
.
id
,
self
.
user
.
id
,
"midterm"
,
self
.
attempt
,
"denied"
,
True
self
.
course
.
id
,
self
.
user
.
id
,
self
.
reverification_location
,
"denied"
,
True
)
)
self
.
assertIn
(
self
.
assertIn
(
...
@@ -2131,6 +2136,6 @@ class TestEmailMessageWithDefaultICRVBlock(ModuleStoreTestCase):
...
@@ -2131,6 +2136,6 @@ class TestEmailMessageWithDefaultICRVBlock(ModuleStoreTestCase):
def
test_error_on_compose_email
(
self
):
def
test_error_on_compose_email
(
self
):
resp
=
_compose_message_reverification_email
(
resp
=
_compose_message_reverification_email
(
self
.
course
.
id
,
self
.
user
.
id
,
"midterm"
,
self
.
attempt
,
"denied"
,
True
self
.
course
.
id
,
self
.
user
.
id
,
u'i4x://edX/DemoX/edx-reverification-block/invalid_location'
,
"denied"
,
True
)
)
self
.
assertIsNone
(
resp
)
self
.
assertIsNone
(
resp
)
lms/djangoapps/verify_student/urls.py
View file @
803bc553
...
@@ -116,9 +116,8 @@ urlpatterns = patterns(
...
@@ -116,9 +116,8 @@ urlpatterns = patterns(
# Users are sent to this end-point from within courseware
# Users are sent to this end-point from within courseware
# to re-verify their identities by re-submitting face photos.
# to re-verify their identities by re-submitting face photos.
url
(
url
(
r'^reverify/{course_id}/{
checkpoint}/{
usage_id}/$'
.
format
(
r'^reverify/{course_id}/{usage_id}/$'
.
format
(
course_id
=
settings
.
COURSE_ID_PATTERN
,
course_id
=
settings
.
COURSE_ID_PATTERN
,
checkpoint
=
settings
.
CHECKPOINT_PATTERN
,
usage_id
=
settings
.
USAGE_ID_PATTERN
usage_id
=
settings
.
USAGE_ID_PATTERN
),
),
views
.
InCourseReverifyView
.
as_view
(),
views
.
InCourseReverifyView
.
as_view
(),
...
...
lms/djangoapps/verify_student/views.py
View file @
803bc553
"""
"""
Views for the verification flow
Views for the verification flow
"""
"""
import
datetime
import
decimal
import
json
import
json
import
logging
import
logging
import
decimal
import
datetime
from
collections
import
namedtuple
from
collections
import
namedtuple
from
pytz
import
UTC
from
pytz
import
UTC
from
django.utils
import
timezone
from
ipware.ip
import
get_ip
from
ipware.ip
import
get_ip
from
django.conf
import
settings
from
django.conf
import
settings
from
django.contrib.auth.decorators
import
login_required
from
django.core.mail
import
send_mail
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.http
import
(
from
django.http
import
(
HttpResponse
,
HttpResponseBadRequest
,
HttpResponse
,
HttpResponseBadRequest
,
HttpResponseRedirect
,
Http404
HttpResponseRedirect
,
Http404
)
)
from
django.contrib.auth.models
import
User
from
django.shortcuts
import
redirect
from
django.shortcuts
import
redirect
from
django.utils
import
timezone
from
django.utils.decorators
import
method_decorator
from
django.utils.translation
import
ugettext
as
_
,
ugettext_lazy
from
django.views.decorators.csrf
import
csrf_exempt
from
django.views.decorators.csrf
import
csrf_exempt
from
django.views.decorators.http
import
require_POST
from
django.views.decorators.http
import
require_POST
from
django.views.generic.base
import
View
,
RedirectView
from
django.views.generic.base
import
View
,
RedirectView
from
django.utils.decorators
import
method_decorator
from
django.utils.translation
import
ugettext
as
_
,
ugettext_lazy
import
analytics
from
django.contrib.auth.decorators
import
login_required
from
eventtracking
import
tracker
from
django.core.mail
import
send_mail
from
ecommerce_api_client.exceptions
import
SlumberBaseException
from
opaque_keys.edx.keys
import
CourseKey
,
UsageKey
from
opaque_keys.edx.keys
import
CourseKey
,
UsageKey
from
opaque_keys
import
InvalidKeyError
from
opaque_keys
import
InvalidKeyError
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
,
NoPathToItem
from
commerce
import
ecommerce_api_client
from
course_modes.models
import
CourseMode
from
courseware.url_helpers
import
get_redirect_url
from
ecommerce_api_client.exceptions
import
SlumberBaseException
from
edxmako.shortcuts
import
render_to_response
,
render_to_string
from
edxmako.shortcuts
import
render_to_response
,
render_to_string
from
openedx.core.djangoapps.user_api.accounts.api
import
get_account_settings
,
update_account_settings
from
embargo
import
api
as
embargo_api
from
microsite_configuration
import
microsite
from
openedx.core.djangoapps.user_api.accounts
import
NAME_MIN_LENGTH
from
openedx.core.djangoapps.user_api.accounts
import
NAME_MIN_LENGTH
from
openedx.core.djangoapps.user_api.accounts.api
import
get_account_settings
,
update_account_settings
from
openedx.core.djangoapps.user_api.errors
import
UserNotFound
,
AccountValidationError
from
openedx.core.djangoapps.user_api.errors
import
UserNotFound
,
AccountValidationError
from
commerce
import
ecommerce_api_client
from
course_modes.models
import
CourseMode
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
from
student.views
import
reverification_info
from
shoppingcart.models
import
Order
,
CertificateItem
from
shoppingcart.models
import
Order
,
CertificateItem
from
shoppingcart.processors
import
(
from
shoppingcart.processors
import
(
get_signed_purchase_params
,
get_purchase_endpoint
get_signed_purchase_params
,
get_purchase_endpoint
)
)
from
verify_student.ssencrypt
import
has_valid_signature
from
verify_student.models
import
(
from
verify_student.models
import
(
SoftwareSecurePhotoVerification
,
SoftwareSecurePhotoVerification
,
VerificationCheckpoint
,
VerificationCheckpoint
,
VerificationStatus
,
VerificationStatus
,
InCourseReverificationConfiguration
)
InCourseReverificationConfiguration
from
reverification.models
import
MidcourseReverificationWindow
)
import
ssencrypt
from
.exceptions
import
WindowExpiredException
from
microsite_configuration
import
microsite
from
embargo
import
api
as
embargo_api
from
util.json_request
import
JsonResponse
from
util.json_request
import
JsonResponse
from
util.date_utils
import
get_default_time_display
from
util.date_utils
import
get_default_time_display
from
eventtracking
import
tracker
from
xmodule.modulestore.django
import
modulestore
import
analytics
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
,
NoPathToItem
from
courseware.url_helpers
import
get_redirect_url
from
django.contrib.auth.models
import
User
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
EVENT_NAME_USER_ENTERED_INCOURSE_REVERIFY_VIEW
=
'edx.bi.reverify.started'
EVENT_NAME_USER_ENTERED_INCOURSE_REVERIFY_VIEW
=
'edx.bi.reverify.started'
EVENT_NAME_USER_SUBMITTED_INCOURSE_REVERIFY
=
'edx.bi.reverify.submitted'
EVENT_NAME_USER_SUBMITTED_INCOURSE_REVERIFY
=
'edx.bi.reverify.submitted'
...
@@ -859,40 +856,40 @@ def submit_photos_for_verification(request):
...
@@ -859,40 +856,40 @@ def submit_photos_for_verification(request):
def
_compose_message_reverification_email
(
def
_compose_message_reverification_email
(
course_key
,
user_id
,
relate
s_assessment
,
photo_verifi
cation
,
status
,
is_secure
course_key
,
user_id
,
relate
d_assessment_lo
cation
,
status
,
is_secure
):
# pylint: disable=invalid-name
):
# pylint: disable=invalid-name
""" Composes subject and message for email
"""
Compose subject and message for photo reverification email.
Args:
Args:
course_key(CourseKey): CourseKey object
course_key(CourseKey): CourseKey object
user_id(str): User Id
user_id(str): User Id
relate
s_assessment(str): related assessment name
relate
d_assessment_location(str): Location of reverification XBlock
photo_verification(QuerySet
/SoftwareSecure): A query set of SoftwareSecure objects or SoftwareSecure objec
photo_verification(QuerySet
): Queryset of SoftwareSecure objects
status(str):
a
pproval status
status(str):
A
pproval status
is_secure(Bool): Is running on secure protocol or not
is_secure(Bool): Is running on secure protocol or not
Returns:
Returns:
None if any error occurred else Tuple of subject and message strings
None if any error occurred else Tuple of subject and message strings
"""
"""
try
:
try
:
location_id
=
VerificationStatus
.
get_location_id
(
photo_verification
)
usage_key
=
UsageKey
.
from_string
(
related_assessment_location
)
usage_key
=
UsageKey
.
from_string
(
location_id
)
reverification_block
=
modulestore
()
.
get_item
(
usage_key
)
course
=
modulestore
()
.
get_course
(
course_key
)
course
=
modulestore
()
.
get_course
(
course_key
)
redirect_url
=
get_redirect_url
(
course_key
,
usage_key
.
replace
(
course_key
=
course_key
))
redirect_url
=
get_redirect_url
(
course_key
,
usage_key
.
replace
(
course_key
=
course_key
))
subject
=
"Re-verification Status"
subject
=
"Re-verification Status"
context
=
{
context
=
{
"status"
:
status
,
"status"
:
status
,
"course_name"
:
course
.
display_name_with_default
,
"course_name"
:
course
.
display_name_with_default
,
"assessment"
:
re
lates
_assessment
,
"assessment"
:
re
verification_block
.
related
_assessment
,
"courseware_url"
:
redirect_url
"courseware_url"
:
redirect_url
}
}
reverification_block
=
modulestore
()
.
get_item
(
usage_key
)
# Allowed attempts is 1 if not set on verification block
# Allowed attempts is 1 if not set on verification block
allowed_attempts
=
1
if
reverification_block
.
attempts
==
0
else
reverification_block
.
attempts
allowed_attempts
=
1
if
reverification_block
.
attempts
==
0
else
reverification_block
.
attempts
user_attempts
=
VerificationStatus
.
get_user_attempts
(
user_id
,
course_key
,
relate
s_assessment
,
location_id
)
user_attempts
=
VerificationStatus
.
get_user_attempts
(
user_id
,
course_key
,
relate
d_assessment_location
)
left_attempts
=
allowed_attempts
-
user_attempts
left_attempts
=
allowed_attempts
-
user_attempts
is_attempt_allowed
=
left_attempts
>
0
is_attempt_allowed
=
left_attempts
>
0
verification_open
=
True
verification_open
=
True
...
@@ -911,8 +908,7 @@ def _compose_message_reverification_email(
...
@@ -911,8 +908,7 @@ def _compose_message_reverification_email(
'verify_student_incourse_reverify'
,
'verify_student_incourse_reverify'
,
args
=
(
args
=
(
unicode
(
course_key
),
unicode
(
course_key
),
unicode
(
relates_assessment
),
related_assessment_location
unicode
(
location_id
)
)
)
)
)
context
[
"reverify_link"
]
=
re_verification_link
context
[
"reverify_link"
]
=
re_verification_link
...
@@ -972,7 +968,7 @@ def results_callback(request):
...
@@ -972,7 +968,7 @@ def results_callback(request):
"Date"
:
request
.
META
.
get
(
"HTTP_DATE"
,
""
)
"Date"
:
request
.
META
.
get
(
"HTTP_DATE"
,
""
)
}
}
sig_valid
=
ssencrypt
.
has_valid_signature
(
has_valid_signature
(
"POST"
,
"POST"
,
headers
,
headers
,
body_dict
,
body_dict
,
...
@@ -1029,10 +1025,10 @@ def results_callback(request):
...
@@ -1029,10 +1025,10 @@ def results_callback(request):
if
checkpoints
:
if
checkpoints
:
user_id
=
attempt
.
user
.
id
user_id
=
attempt
.
user
.
id
course_key
=
checkpoints
[
0
]
.
course_id
course_key
=
checkpoints
[
0
]
.
course_id
relate
s_assessment
=
checkpoints
[
0
]
.
checkpoint_name
relate
d_assessment_location
=
checkpoints
[
0
]
.
checkpoint_location
subject
,
message
=
_compose_message_reverification_email
(
subject
,
message
=
_compose_message_reverification_email
(
course_key
,
user_id
,
relate
s_assessment
,
attempt
,
status
,
request
.
is_secure
()
course_key
,
user_id
,
relate
d_assessment_location
,
status
,
request
.
is_secure
()
)
)
_send_email
(
user_id
,
subject
,
message
)
_send_email
(
user_id
,
subject
,
message
)
...
@@ -1122,11 +1118,20 @@ class InCourseReverifyView(View):
...
@@ -1122,11 +1118,20 @@ class InCourseReverifyView(View):
Does not need to worry about pricing
Does not need to worry about pricing
"""
"""
@method_decorator
(
login_required
)
@method_decorator
(
login_required
)
def
get
(
self
,
request
,
course_id
,
checkpoint_name
,
usage_id
):
def
get
(
self
,
request
,
course_id
,
usage_id
):
""" Display the view for face photo submission"""
"""Display the view for face photo submission.
# Check the in-course re-verification is enabled or not
Args:
request(HttpRequest): HttpRequest object
course_id(str): A string of course id
usage_id(str): Location of Reverification XBlock in courseware
Returns:
HttpResponse
"""
# Check that in-course re-verification is enabled or not
incourse_reverify_enabled
=
InCourseReverificationConfiguration
.
current
()
.
enabled
incourse_reverify_enabled
=
InCourseReverificationConfiguration
.
current
()
.
enabled
if
not
incourse_reverify_enabled
:
if
not
incourse_reverify_enabled
:
log
.
error
(
log
.
error
(
u"In-course reverification is not enabled. "
u"In-course reverification is not enabled. "
...
@@ -1139,49 +1144,49 @@ class InCourseReverifyView(View):
...
@@ -1139,49 +1144,49 @@ class InCourseReverifyView(View):
course_key
=
CourseKey
.
from_string
(
course_id
)
course_key
=
CourseKey
.
from_string
(
course_id
)
course
=
modulestore
()
.
get_course
(
course_key
)
course
=
modulestore
()
.
get_course
(
course_key
)
if
course
is
None
:
if
course
is
None
:
log
.
error
(
u"Could not find course
%
s
for in-course reverification."
,
course_key
)
log
.
error
(
u"Could not find course
'
%
s'
for in-course reverification."
,
course_key
)
raise
Http404
raise
Http404
checkpoint
=
VerificationCheckpoint
.
get_verification_checkpoint
(
course_key
,
checkpoint_name
)
checkpoint
=
VerificationCheckpoint
.
get_verification_checkpoint
(
course_key
,
usage_id
)
if
checkpoint
is
None
:
if
checkpoint
is
None
:
log
.
error
(
log
.
error
(
u"No verification checkpoint exists for the "
u"No verification checkpoint exists for the "
u"course
%
s and checkpoint name
%
s
."
,
u"course
'
%
s' and checkpoint location '
%
s'
."
,
course_key
,
checkpoint_name
course_key
,
usage_id
)
)
raise
Http404
raise
Http404
init_verification
=
SoftwareSecurePhotoVerification
.
get_initial_verification
(
user
)
init
ial
_verification
=
SoftwareSecurePhotoVerification
.
get_initial_verification
(
user
)
if
not
init_verification
:
if
not
init
ial
_verification
:
return
self
.
_redirect_no_initial_verification
(
user
,
course_key
)
return
self
.
_redirect_no_initial_verification
(
user
,
course_key
)
# emit the reverification event
# emit the reverification event
self
.
_track_reverification_events
(
self
.
_track_reverification_events
(
EVENT_NAME_USER_ENTERED_INCOURSE_REVERIFY_VIEW
,
user
.
id
,
course_id
,
checkpoint_name
EVENT_NAME_USER_ENTERED_INCOURSE_REVERIFY_VIEW
,
user
.
id
,
course_id
,
checkpoint
.
checkpoint
_name
)
)
context
=
{
context
=
{
'course_key'
:
unicode
(
course_key
),
'course_key'
:
unicode
(
course_key
),
'course_name'
:
course
.
display_name_with_default
,
'course_name'
:
course
.
display_name_with_default
,
'checkpoint_name'
:
checkpoint_name
,
'checkpoint_name'
:
checkpoint
.
checkpoint
_name
,
'platform_name'
:
settings
.
PLATFORM_NAME
,
'platform_name'
:
settings
.
PLATFORM_NAME
,
'usage_id'
:
usage_id
'usage_id'
:
usage_id
}
}
return
render_to_response
(
"verify_student/incourse_reverify.html"
,
context
)
return
render_to_response
(
"verify_student/incourse_reverify.html"
,
context
)
@method_decorator
(
login_required
)
@method_decorator
(
login_required
)
def
post
(
self
,
request
,
course_id
,
checkpoint_name
,
usage_id
):
def
post
(
self
,
request
,
course_id
,
usage_id
):
"""Submits the re-verification attempt to SoftwareSecure
"""Submits the re-verification attempt to SoftwareSecure
.
Args:
Args:
request(HttpRequest): HttpRequest object
request(HttpRequest): HttpRequest object
course_id(str): Course Id
course_id(str): Course Id
checkpoint_name(str): Checkpoint nam
e
usage_id(str): Location of Reverification XBlock in coursewar
e
Returns:
Returns:
HttpResponse with status_code 400 if photo is missing or any error
HttpResponse with status_code 400 if photo is missing or any error
or redirect to the verification flow if initial verification
doesn't exist otherwise
or redirect to the verification flow if initial verification
Https
Response with status code 200
doesn't exist otherwise Http
Response with status code 200
"""
"""
# Check the in-course re-verification is enabled or not
# Check the in-course re-verification is enabled or not
incourse_reverify_enabled
=
InCourseReverificationConfiguration
.
current
()
.
enabled
incourse_reverify_enabled
=
InCourseReverificationConfiguration
.
current
()
.
enabled
...
@@ -1196,20 +1201,18 @@ class InCourseReverifyView(View):
...
@@ -1196,20 +1201,18 @@ class InCourseReverifyView(View):
raise
Http404
(
u"Invalid course_key or usage_key"
)
raise
Http404
(
u"Invalid course_key or usage_key"
)
course
=
modulestore
()
.
get_course
(
course_key
)
course
=
modulestore
()
.
get_course
(
course_key
)
checkpoint
=
VerificationCheckpoint
.
get_verification_checkpoint
(
course_key
,
checkpoint_name
)
if
course
is
None
:
log
.
error
(
u"Invalid course id '
%
s'"
,
course_id
)
return
HttpResponseBadRequest
(
_
(
"Invalid course location."
))
checkpoint
=
VerificationCheckpoint
.
get_verification_checkpoint
(
course_key
,
usage_id
)
if
checkpoint
is
None
:
if
checkpoint
is
None
:
log
.
error
(
"Checkpoint is not defined. Could not submit verification attempt for user
%
s"
,
log
.
error
(
request
.
user
.
id
)
u"Checkpoint is not defined. Could not submit verification attempt"
context
=
{
u" for user '
%
s', course '
%
s' and checkpoint location '
%
s'."
,
'course_key'
:
unicode
(
course_key
),
request
.
user
.
id
,
course_key
,
usage_id
'course_name'
:
course
.
display_name_with_default
,
)
'checkpoint_name'
:
checkpoint_name
,
return
HttpResponseBadRequest
(
_
(
"Invalid checkpoint location."
))
'error'
:
True
,
'errorMsg'
:
_
(
"No checkpoint found"
),
'platform_name'
:
settings
.
PLATFORM_NAME
,
'usage_id'
:
usage_id
}
return
render_to_response
(
"verify_student/incourse_reverify.html"
,
context
)
init_verification
=
SoftwareSecurePhotoVerification
.
get_initial_verification
(
user
)
init_verification
=
SoftwareSecurePhotoVerification
.
get_initial_verification
(
user
)
if
not
init_verification
:
if
not
init_verification
:
...
@@ -1220,47 +1223,51 @@ class InCourseReverifyView(View):
...
@@ -1220,47 +1223,51 @@ class InCourseReverifyView(View):
request
.
user
,
request
.
POST
[
'face_image'
],
init_verification
.
photo_id_key
request
.
user
,
request
.
POST
[
'face_image'
],
init_verification
.
photo_id_key
)
)
checkpoint
.
add_verification_attempt
(
attempt
)
checkpoint
.
add_verification_attempt
(
attempt
)
VerificationStatus
.
add_verification_status
(
checkpoint
,
user
,
"submitted"
,
usage_id
)
VerificationStatus
.
add_verification_status
(
checkpoint
,
user
,
"submitted"
)
# emit the reverification event
# emit the reverification event
self
.
_track_reverification_events
(
self
.
_track_reverification_events
(
EVENT_NAME_USER_SUBMITTED_INCOURSE_REVERIFY
,
user
.
id
,
course_id
,
checkpoint_name
EVENT_NAME_USER_SUBMITTED_INCOURSE_REVERIFY
,
user
.
id
,
course_id
,
checkpoint
.
checkpoint
_name
)
)
try
:
redirect_url
=
get_redirect_url
(
course_key
,
usage_key
)
redirect_url
=
get_redirect_url
(
course_key
,
usage_key
)
response
=
JsonResponse
({
'url'
:
redirect_url
})
except
(
ItemNotFoundError
,
NoPathToItem
):
except
(
ItemNotFoundError
,
NoPathToItem
):
log
.
warning
(
log
.
warning
(
u"Could not find redirect URL for location
%
s in course
%
s"
,
course_key
,
usage_key
)
u"Could not find redirect URL for location
%
s in course
%
s"
,
course_key
,
usage_key
)
redirect_url
=
reverse
(
"courseware"
,
args
=
(
unicode
(
course_key
),))
redirect_url
=
reverse
(
"courseware"
,
args
=
(
unicode
(
course_key
),))
response
=
JsonResponse
({
'url'
:
redirect_url
})
return
JsonResponse
({
'url'
:
redirect_url
})
except
Http404
as
expt
:
except
Http404
as
expt
:
log
.
exception
(
"Invalid location during photo verification."
)
log
.
exception
(
"Invalid location during photo verification."
)
return
HttpResponseBadRequest
(
expt
.
message
)
response
=
HttpResponseBadRequest
(
expt
.
message
)
except
IndexError
:
except
IndexError
:
log
.
exception
(
"Invalid image data during photo verification."
)
log
.
exception
(
"Invalid image data during photo verification."
)
return
HttpResponseBadRequest
(
_
(
"Invalid image data during photo verification."
))
response
=
HttpResponseBadRequest
(
_
(
"Invalid image data during photo verification."
))
except
Exception
:
# pylint: disable=broad-except
except
Exception
:
# pylint: disable=broad-except
log
.
exception
(
"Could not submit verification attempt for user
%
s."
,
request
.
user
.
id
)
log
.
exception
(
"Could not submit verification attempt for user
%
s."
,
request
.
user
.
id
)
msg
=
_
(
"Could not submit photos"
)
msg
=
_
(
"Could not submit photos"
)
return
HttpResponseBadRequest
(
msg
)
response
=
HttpResponseBadRequest
(
msg
)
return
response
def
_track_reverification_events
(
self
,
event_name
,
user_id
,
course_id
,
checkpoint
):
# pylint: disable=invalid-name
def
_track_reverification_events
(
self
,
event_name
,
user_id
,
course_id
,
checkpoint
):
# pylint: disable=invalid-name
"""Track re-verification events for user against course checkpoints
"""Track re-verification events for a user against a reverification
checkpoint of a course.
Arguments:
Arguments:
user_id (str): The ID of the user generting the certificate.
event_name (str): Name of event being tracked
course_id (unicode): id associated with the course
user_id (str): The ID of the user
checkpoint (str): checkpoint name
course_id (unicode): ID associated with the course
checkpoint (str): Checkpoint name
Returns:
Returns:
None
None
"""
"""
log
.
info
(
log
.
info
(
u"In-course reverification: event
%
s occurred for user
%
s in course
%
s at checkpoint
%
s
"
,
u"In-course reverification: event
%
s occurred for user
'
%
s' in course '
%
s' at checkpoint '
%
s'
"
,
event_name
,
user_id
,
course_id
,
checkpoint
event_name
,
user_id
,
course_id
,
checkpoint
)
)
...
...
lms/static/js/verify_student/incourse_reverify.js
View file @
803bc553
...
@@ -20,7 +20,6 @@
...
@@ -20,7 +20,6 @@
return
new
edx
.
verify_student
.
InCourseReverifyView
({
return
new
edx
.
verify_student
.
InCourseReverifyView
({
courseKey
:
el
.
data
(
'course-key'
),
courseKey
:
el
.
data
(
'course-key'
),
checkpointName
:
el
.
data
(
'checkpoint-name'
),
platformName
:
el
.
data
(
'platform-name'
),
platformName
:
el
.
data
(
'platform-name'
),
usageId
:
el
.
data
(
'usage-id'
),
usageId
:
el
.
data
(
'usage-id'
),
errorModel
:
errorView
.
model
errorModel
:
errorView
.
model
...
...
lms/static/js/verify_student/models/reverification_model.js
View file @
803bc553
...
@@ -16,7 +16,6 @@ var edx = edx || {};
...
@@ -16,7 +16,6 @@ var edx = edx || {};
defaults
:
{
defaults
:
{
courseKey
:
''
,
courseKey
:
''
,
checkpointName
:
''
,
faceImage
:
''
,
faceImage
:
''
,
usageId
:
''
usageId
:
''
},
},
...
@@ -28,9 +27,8 @@ var edx = edx || {};
...
@@ -28,9 +27,8 @@ var edx = edx || {};
face_image
:
model
.
get
(
'faceImage'
)
face_image
:
model
.
get
(
'faceImage'
)
},
},
url
=
_
.
str
.
sprintf
(
url
=
_
.
str
.
sprintf
(
'/verify_student/reverify/%(courseKey)s/%(
checkpointName)s/%(
usageId)s/'
,
{
'/verify_student/reverify/%(courseKey)s/%(usageId)s/'
,
{
courseKey
:
model
.
get
(
'courseKey'
),
courseKey
:
model
.
get
(
'courseKey'
),
checkpointName
:
model
.
get
(
'checkpointName'
),
usageId
:
model
.
get
(
'usageId'
)
usageId
:
model
.
get
(
'usageId'
)
}
}
);
);
...
...
lms/static/js/verify_student/views/incourse_reverify_view.js
View file @
803bc553
...
@@ -26,14 +26,12 @@
...
@@ -26,14 +26,12 @@
this
.
errorModel
=
obj
.
errorModel
||
null
;
this
.
errorModel
=
obj
.
errorModel
||
null
;
this
.
courseKey
=
obj
.
courseKey
||
null
;
this
.
courseKey
=
obj
.
courseKey
||
null
;
this
.
checkpointName
=
obj
.
checkpointName
||
null
;
this
.
platformName
=
obj
.
platformName
||
null
;
this
.
platformName
=
obj
.
platformName
||
null
;
this
.
usageId
=
obj
.
usageId
||
null
;
this
.
usageId
=
obj
.
usageId
||
null
;
this
.
model
=
new
edx
.
verify_student
.
ReverificationModel
({
this
.
model
=
new
edx
.
verify_student
.
ReverificationModel
({
courseKey
:
this
.
courseKey
,
courseKey
:
this
.
courseKey
,
checkpointName
:
this
.
checkpointName
,
usageId
:
this
.
usageId
usageId
:
this
.
usageId
});
});
...
@@ -47,7 +45,6 @@
...
@@ -47,7 +45,6 @@
$
(
this
.
templateId
).
html
(),
$
(
this
.
templateId
).
html
(),
{
{
courseKey
:
this
.
courseKey
,
courseKey
:
this
.
courseKey
,
checkpointName
:
this
.
checkpointName
,
platformName
:
this
.
platformName
platformName
:
this
.
platformName
}
}
);
);
...
...
lms/templates/verify_student/incourse_reverify.html
View file @
803bc553
...
@@ -43,7 +43,6 @@ from verify_student.views import PayAndVerifyView
...
@@ -43,7 +43,6 @@ from verify_student.views import PayAndVerifyView
<div
id=
"incourse-reverify-container"
<div
id=
"incourse-reverify-container"
class=
"incourse-reverify"
class=
"incourse-reverify"
data-course-key=
'${course_key}'
data-course-key=
'${course_key}'
data-checkpoint-name=
'${checkpoint_name}'
data-platform-name=
'${platform_name}'
data-platform-name=
'${platform_name}'
data-usage-id=
'${usage_id}'
data-usage-id=
'${usage_id}'
></div>
></div>
...
...
requirements/edx/github.txt
View file @
803bc553
...
@@ -50,7 +50,7 @@ git+https://github.com/hmarr/django-debug-toolbar-mongo.git@b0686a76f1ce3532088c
...
@@ -50,7 +50,7 @@ git+https://github.com/hmarr/django-debug-toolbar-mongo.git@b0686a76f1ce3532088c
git+https://github.com/edx/edx-lint.git@8bf82a32ecb8598c415413df66f5232ab8d974e9#egg=edx_lint==0.2.1
git+https://github.com/edx/edx-lint.git@8bf82a32ecb8598c415413df66f5232ab8d974e9#egg=edx_lint==0.2.1
-e git+https://github.com/edx/xblock-utils.git@db22bc40fd2a75458a3c66d057f88aff5a7383e6#egg=xblock-utils
-e git+https://github.com/edx/xblock-utils.git@db22bc40fd2a75458a3c66d057f88aff5a7383e6#egg=xblock-utils
-e git+https://github.com/edx-solutions/xblock-google-drive.git@138e6fa0bf3a2013e904a085b9fed77dab7f3f21#egg=xblock-google-drive
-e git+https://github.com/edx-solutions/xblock-google-drive.git@138e6fa0bf3a2013e904a085b9fed77dab7f3f21#egg=xblock-google-drive
-e git+https://github.com/edx/edx-reverification-block.git@
2ff0d21f6614874067168bd244e68d8215041f3b
#egg=edx-reverification-block
-e git+https://github.com/edx/edx-reverification-block.git@
03da85753d5f563a22c1282c0e89fcb2e828b8c1
#egg=edx-reverification-block
git+https://github.com/edx/ecommerce-api-client.git@1.0.0#egg=ecommerce-api-client==1.0.0
git+https://github.com/edx/ecommerce-api-client.git@1.0.0#egg=ecommerce-api-client==1.0.0
# Third Party XBlocks
# Third Party XBlocks
...
...
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