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
d2535bd0
Commit
d2535bd0
authored
Oct 15, 2015
by
Tasawer
Committed by
Tasawer Nawaz
Oct 20, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
New Model added for Configuration of ICRV status emails.
ECOM-2552
parent
ad889344
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
203 additions
and
5 deletions
+203
-5
lms/djangoapps/verify_student/admin.py
+9
-1
lms/djangoapps/verify_student/migrations/0015_auto__add_icrvstatusemailsconfiguration.py
+145
-0
lms/djangoapps/verify_student/models.py
+9
-0
lms/djangoapps/verify_student/tests/test_views.py
+36
-2
lms/djangoapps/verify_student/views.py
+4
-2
No files found.
lms/djangoapps/verify_student/admin.py
View file @
d2535bd0
# encoding: utf-8
"""
Admin site configurations for verify_student.
"""
from
config_models.admin
import
ConfigurationModelAdmin
from
ratelimitbackend
import
admin
from
verify_student.models
import
(
IcrvStatusEmailsConfiguration
,
SkippedReverification
,
SoftwareSecurePhotoVerification
,
VerificationStatus
,
SkippedReverification
,
)
...
...
@@ -54,3 +61,4 @@ class SkippedReverificationAdmin(admin.ModelAdmin):
admin
.
site
.
register
(
SoftwareSecurePhotoVerification
,
SoftwareSecurePhotoVerificationAdmin
)
admin
.
site
.
register
(
SkippedReverification
,
SkippedReverificationAdmin
)
admin
.
site
.
register
(
VerificationStatus
,
VerificationStatusAdmin
)
admin
.
site
.
register
(
IcrvStatusEmailsConfiguration
,
ConfigurationModelAdmin
)
lms/djangoapps/verify_student/migrations/0015_auto__add_icrvstatusemailsconfiguration.py
0 → 100644
View file @
d2535bd0
# -*- 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
):
# Adding model 'IcrvStatusEmailsConfiguration'
db
.
create_table
(
'verify_student_icrvstatusemailsconfiguration'
,
(
(
'id'
,
self
.
gf
(
'django.db.models.fields.AutoField'
)(
primary_key
=
True
)),
(
'change_date'
,
self
.
gf
(
'django.db.models.fields.DateTimeField'
)(
auto_now_add
=
True
,
blank
=
True
)),
(
'changed_by'
,
self
.
gf
(
'django.db.models.fields.related.ForeignKey'
)(
to
=
orm
[
'auth.User'
],
null
=
True
,
on_delete
=
models
.
PROTECT
)),
(
'enabled'
,
self
.
gf
(
'django.db.models.fields.BooleanField'
)(
default
=
False
)),
))
db
.
send_create_signal
(
'verify_student'
,
[
'IcrvStatusEmailsConfiguration'
])
def
backwards
(
self
,
orm
):
# Deleting model 'IcrvStatusEmailsConfiguration'
db
.
delete_table
(
'verify_student_icrvstatusemailsconfiguration'
)
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'
})
},
'verify_student.historicalverificationdeadline'
:
{
'Meta'
:
{
'ordering'
:
"(u'-history_date', u'-history_id')"
,
'object_name'
:
'HistoricalVerificationDeadline'
},
'course_key'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'deadline'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{}),
u'history_date'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{}),
u'history_id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
u'history_type'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'1'
}),
u'history_user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"u'+'"
,
'null'
:
'True'
,
'on_delete'
:
'models.SET_NULL'
,
'to'
:
"orm['auth.User']"
}),
'id'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'db_index'
:
'True'
,
'blank'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
})
},
'verify_student.icrvstatusemailsconfiguration'
:
{
'Meta'
:
{
'ordering'
:
"('-change_date',)"
,
'object_name'
:
'IcrvStatusEmailsConfiguration'
},
'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.incoursereverificationconfiguration'
:
{
'Meta'
:
{
'ordering'
:
"('-change_date',)"
,
'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'
},
'copy_id_photo_from'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['verify_student.SoftwareSecurePhotoVerification']"
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'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'
:
"'1dacd5f0-d7f0-452f-b2ed-6e2756547305'"
,
'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']"
})
},
'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.verificationdeadline'
:
{
'Meta'
:
{
'object_name'
:
'VerificationDeadline'
},
'course_key'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'deadline'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
})
},
'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'
]
lms/djangoapps/verify_student/models.py
View file @
d2535bd0
...
...
@@ -1308,6 +1308,15 @@ class InCourseReverificationConfiguration(ConfigurationModel):
pass
class
IcrvStatusEmailsConfiguration
(
ConfigurationModel
):
"""Toggle in-course reverification (ICRV) status emails
Disabled by default. When disabled, ICRV status emails will not be sent.
When enabled, ICRV status emails are sent.
"""
pass
class
SkippedReverification
(
models
.
Model
):
"""Model for tracking skipped Reverification of a user against a specific
course.
...
...
lms/djangoapps/verify_student/tests/test_views.py
View file @
d2535bd0
...
...
@@ -48,7 +48,8 @@ from verify_student.views import (
)
from
verify_student.models
import
(
VerificationDeadline
,
SoftwareSecurePhotoVerification
,
VerificationCheckpoint
,
VerificationStatus
VerificationCheckpoint
,
VerificationStatus
,
IcrvStatusEmailsConfiguration
,
)
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
...
...
@@ -1716,6 +1717,8 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase):
"""
Test for verification passed.
"""
# Verify that ICRV status email was sent when config is enabled
IcrvStatusEmailsConfiguration
.
objects
.
create
(
enabled
=
True
)
self
.
create_reverification_xblock
()
data
=
{
...
...
@@ -1737,16 +1740,47 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase):
self
.
assertEqual
(
attempt
.
status
,
u'approved'
)
self
.
assertEquals
(
response
.
content
,
'OK!'
)
# Verify that photo re-verification status email was sent
self
.
assertEqual
(
len
(
mail
.
outbox
),
1
)
self
.
assertEqual
(
"Re-verification Status"
,
mail
.
outbox
[
0
]
.
subject
)
@mock.patch
(
'verify_student.ssencrypt.has_valid_signature'
,
mock
.
Mock
(
side_effect
=
mocked_has_valid_signature
))
def
test_icrv_status_email_with_disable_config
(
self
):
"""
Verify that photo re-verification status email was not sent when config is disable
"""
IcrvStatusEmailsConfiguration
.
objects
.
create
(
enabled
=
False
)
self
.
create_reverification_xblock
()
data
=
{
"EdX-ID"
:
self
.
receipt_id
,
"Result"
:
"PASS"
,
"Reason"
:
""
,
"MessageType"
:
"You have been verified."
}
json_data
=
json
.
dumps
(
data
)
response
=
self
.
client
.
post
(
reverse
(
'verify_student_results_callback'
),
data
=
json_data
,
content_type
=
'application/json'
,
HTTP_AUTHORIZATION
=
'test BBBBBBBBBBBBBBBBBBBB:testing'
,
HTTP_DATE
=
'testdate'
)
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
get
(
receipt_id
=
self
.
receipt_id
)
self
.
assertEqual
(
attempt
.
status
,
u'approved'
)
self
.
assertEquals
(
response
.
content
,
'OK!'
)
self
.
assertEqual
(
len
(
mail
.
outbox
),
0
)
@mock.patch
(
'verify_student.views._send_email'
)
@mock.patch
(
'verify_student.ssencrypt.has_valid_signature'
,
mock
.
Mock
(
side_effect
=
mocked_has_valid_signature
))
def
test_reverification_on_callback
(
self
,
mock_send_email
):
"""
Test software secure callback flow for re-verification.
"""
IcrvStatusEmailsConfiguration
.
objects
.
create
(
enabled
=
True
)
# Create the 'edx-reverification-block' in course tree
self
.
create_reverification_xblock
()
...
...
lms/djangoapps/verify_student/views.py
View file @
d2535bd0
...
...
@@ -52,6 +52,7 @@ from verify_student.models import (
SoftwareSecurePhotoVerification
,
VerificationCheckpoint
,
VerificationStatus
,
IcrvStatusEmailsConfiguration
,
)
from
verify_student.image
import
decode_image_data
,
InvalidImageData
from
util.json_request
import
JsonResponse
...
...
@@ -1312,8 +1313,9 @@ def results_callback(request):
checkpoints
=
VerificationCheckpoint
.
objects
.
filter
(
photo_verification
=
attempt
)
.
all
()
VerificationStatus
.
add_status_from_checkpoints
(
checkpoints
=
checkpoints
,
user
=
attempt
.
user
,
status
=
status
)
# If this is re-verification then send the update email
if
checkpoints
:
# Trigger ICRV email only if ICRV status emails config is enabled
icrv_status_emails
=
IcrvStatusEmailsConfiguration
.
current
()
if
icrv_status_emails
.
enabled
and
checkpoints
:
user_id
=
attempt
.
user
.
id
course_key
=
checkpoints
[
0
]
.
course_id
related_assessment_location
=
checkpoints
[
0
]
.
checkpoint_location
...
...
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