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
dacfcc98
Commit
dacfcc98
authored
8 years ago
by
Mushtaq Ali
Committed by
Qubad786
8 years ago
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Generate certificates for verified users with audit certificate statues - ECOM-5012
parent
ada4e082
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
111 additions
and
8 deletions
+111
-8
common/djangoapps/student/models.py
+11
-6
lms/djangoapps/instructor/tests/test_certificates.py
+71
-0
lms/djangoapps/instructor/views/api.py
+7
-1
lms/djangoapps/instructor/views/instructor_dashboard.py
+6
-0
lms/djangoapps/instructor_task/api.py
+6
-1
lms/djangoapps/instructor_task/tasks_helper.py
+4
-0
lms/templates/instructor/instructor_dashboard_2/certificates.html
+6
-0
No files found.
common/djangoapps/student/models.py
View file @
dacfcc98
...
...
@@ -943,12 +943,17 @@ class CourseEnrollmentManager(models.Manager):
return
is_course_full
def
users_enrolled_in
(
self
,
course_id
):
"""Return a queryset of User for every user enrolled in the course."""
return
User
.
objects
.
filter
(
courseenrollment__course_id
=
course_id
,
courseenrollment__is_active
=
True
)
def
users_enrolled_in
(
self
,
course_id
,
mode
=
None
):
"""
Returns a queryset of User for every user enrolled in the course.
course_id (CourseKey): The key of the course associated with the enrollment.
mode (String): The enrolled mode of the users.
"""
_query
=
{
'courseenrollment__course_id'
:
course_id
,
'courseenrollment__is_active'
:
True
}
if
mode
:
_query
[
'courseenrollment__mode'
]
=
mode
return
User
.
objects
.
filter
(
**
_query
)
def
enrollment_counts
(
self
,
course_id
):
"""
...
...
This diff is collapsed.
Click to expand it.
lms/djangoapps/instructor/tests/test_certificates.py
View file @
dacfcc98
...
...
@@ -3,12 +3,19 @@ import contextlib
import
ddt
import
mock
import
json
import
pytz
from
datetime
import
datetime
,
timedelta
from
nose.plugins.attrib
import
attr
from
django.core.urlresolvers
import
reverse
from
django.core.exceptions
import
ObjectDoesNotExist
from
django.test.utils
import
override_settings
from
django.conf
import
settings
from
mock
import
patch
,
PropertyMock
from
course_modes.models
import
CourseMode
from
xmodule.modulestore.tests.django_utils
import
SharedModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
config_models.models
import
cache
...
...
@@ -344,6 +351,70 @@ class CertificatesInstructorApiTest(SharedModuleStoreTestCase):
u'the "Pending Tasks" section.'
)
@patch
(
'lms.djangoapps.grades.new.course_grade.CourseGrade.summary'
,
PropertyMock
(
return_value
=
{
'grade'
:
'Pass'
,
'percent'
:
0.75
})
)
@override_settings
(
AUDIT_CERT_CUTOFF_DATE
=
datetime
.
now
(
pytz
.
UTC
)
-
timedelta
(
days
=
1
))
@ddt.data
(
(
'verified'
,
'ID Verified'
,
True
),
(
'unverified'
,
'Not ID Verified'
,
False
)
)
@ddt.unpack
def
test_verified_users_with_audit_certs
(
self
,
expected_cert_status
,
verification_output
,
user_verified
):
"""
Test that `verified_users_with_audit_certs` option regenerates certificate for verified users with audit
certificates get certificate.
Scenario:
User enrolled in course as audit,
User passed the course as audit so they have `audit_passing` certificate status,
User switched to verified mode and is ID verified,
Regenerate certificates for `verified_users_with_audit_certs` is run,
Modified certificate status is `verified` if user is ID verified otherwise `unverified`.
"""
# Check that user is enrolled in audit mode.
enrollment
=
CourseEnrollment
.
get_enrollment
(
self
.
user
,
self
.
course
.
id
)
self
.
assertEqual
(
enrollment
.
mode
,
CourseMode
.
AUDIT
)
# Generate certificate for user and check that user has a audit passing certificate.
with
patch
(
'student.models.CourseEnrollment.refund_cutoff_date'
)
as
cutoff_date
:
cutoff_date
.
return_value
=
datetime
.
now
(
pytz
.
UTC
)
-
timedelta
(
minutes
=
5
)
cert_status
=
certs_api
.
generate_user_certificates
(
student
=
self
.
user
,
course_key
=
self
.
course
.
id
,
course
=
self
.
course
)
self
.
assertEqual
(
cert_status
,
CertificateStatuses
.
audit_passing
)
# Update user enrollment mode to verified mode.
enrollment
.
update_enrollment
(
mode
=
'verified'
)
self
.
assertEqual
(
enrollment
.
mode
,
CourseMode
.
VERIFIED
)
with
patch
(
'lms.djangoapps.verify_student.models.SoftwareSecurePhotoVerification.user_is_verified'
)
as
user_verify
:
user_verify
.
return_value
=
user_verified
# Login the client and access the url with 'certificate_statuses'
self
.
client
.
login
(
username
=
self
.
global_staff
.
username
,
password
=
'test'
)
url
=
reverse
(
'start_certificate_regeneration'
,
kwargs
=
{
'course_id'
:
unicode
(
self
.
course
.
id
)})
response
=
self
.
client
.
post
(
url
,
data
=
{
'certificate_statuses'
:
[
'verified_users_with_audit_certs'
]})
# Assert 200 status code in response
self
.
assertEqual
(
response
.
status_code
,
200
)
res_json
=
json
.
loads
(
response
.
content
)
# Assert request is successful
self
.
assertTrue
(
res_json
[
'success'
])
# Assert success message
self
.
assertEqual
(
res_json
[
'message'
],
u'Certificate regeneration task has been started. You can view the status of the generation task in '
u'the "Pending Tasks" section.'
)
# Check user has a not audit passing certificate now.
cert
=
certs_api
.
get_certificate_for_user
(
self
.
user
.
username
,
self
.
course
.
id
)
self
.
assertNotEqual
(
cert
[
'status'
],
CertificateStatuses
.
audit_passing
)
self
.
assertEqual
(
cert
[
'status'
],
expected_cert_status
)
def
test_certificate_regeneration_error
(
self
):
"""
Test certificate regeneration errors out when accessed with either empty list of 'certificate_statuses' or
...
...
This diff is collapsed.
Click to expand it.
lms/djangoapps/instructor/views/api.py
View file @
dacfcc98
...
...
@@ -2862,7 +2862,13 @@ def start_certificate_regeneration(request, course_id):
)
# Check if the selected statuses are allowed
allowed_statuses
=
[
CertificateStatuses
.
downloadable
,
CertificateStatuses
.
error
,
CertificateStatuses
.
notpassing
]
allowed_statuses
=
[
CertificateStatuses
.
downloadable
,
CertificateStatuses
.
error
,
CertificateStatuses
.
notpassing
,
# verified users with audit passing and not passing certificate statuses.
'verified_users_with_audit_certs'
]
if
not
set
(
certificates_statuses
)
.
issubset
(
allowed_statuses
):
return
JsonResponse
(
{
'message'
:
_
(
'Please select certificate statuses from the list only.'
)},
...
...
This diff is collapsed.
Click to expand it.
lms/djangoapps/instructor/views/instructor_dashboard.py
View file @
dacfcc98
...
...
@@ -343,6 +343,11 @@ def _section_certificates(course):
for
certificate
in
GeneratedCertificate
.
get_unique_statuses
(
course_key
=
course
.
id
)
}
# Get the count of all course verified users with audit passing and audit not passing statuses.
verified_users_with_audit_certs
=
CourseEnrollment
.
objects
.
users_enrolled_in
(
course
.
id
,
mode
=
'verified'
)
.
filter
(
generatedcertificate__status__in
=
[
CertificateStatuses
.
audit_passing
,
CertificateStatuses
.
audit_notpassing
]
)
.
count
()
return
{
'section_key'
:
'certificates'
,
'section_display_name'
:
_
(
'Certificates'
),
...
...
@@ -354,6 +359,7 @@ def _section_certificates(course):
'html_cert_enabled'
:
html_cert_enabled
,
'active_certificate'
:
certs_api
.
get_active_web_certificate
(
course
),
'certificate_statuses_with_count'
:
certificate_statuses_with_count
,
'verified_users_with_audit_certs'
:
verified_users_with_audit_certs
,
'status'
:
CertificateStatuses
,
'certificate_generation_history'
:
CertificateGenerationHistory
.
objects
.
filter
(
course_id
=
course
.
id
)
.
order_by
(
"-created"
),
...
...
This diff is collapsed.
Click to expand it.
lms/djangoapps/instructor_task/api.py
View file @
dacfcc98
...
...
@@ -33,7 +33,7 @@ from lms.djangoapps.instructor_task.tasks import (
export_ora2_data
,
)
from
certificates.models
import
CertificateGenerationHistory
from
certificates.models
import
CertificateGenerationHistory
,
CertificateStatuses
from
lms.djangoapps.instructor_task.api_helper
import
(
check_arguments_for_rescoring
,
...
...
@@ -507,6 +507,11 @@ def regenerate_certificates(request, course_key, statuses_to_regenerate):
task_type
=
'regenerate_certificates_all_student'
task_input
=
{}
# Update task_input for verified users with audit passing and not passing certificate statuses.
if
'verified_users_with_audit_certs'
in
statuses_to_regenerate
:
task_input
.
update
({
"student_set"
:
'verified_users_with_audit_certs'
})
statuses_to_regenerate
=
[
CertificateStatuses
.
audit_passing
,
CertificateStatuses
.
audit_notpassing
]
task_input
.
update
({
"statuses_to_regenerate"
:
statuses_to_regenerate
})
task_class
=
generate_certificates
task_key
=
""
...
...
This diff is collapsed.
Click to expand it.
lms/djangoapps/instructor_task/tasks_helper.py
View file @
dacfcc98
...
...
@@ -1429,6 +1429,10 @@ def generate_students_certificates(
specific_student_id
=
task_input
.
get
(
'specific_student_id'
)
students_to_generate_certs_for
=
students_to_generate_certs_for
.
filter
(
id
=
specific_student_id
)
# Verified users with audit passing and not passing certificate statuses.
elif
student_set
==
"verified_users_with_audit_certs"
:
students_to_generate_certs_for
=
CourseEnrollment
.
objects
.
users_enrolled_in
(
course_id
,
mode
=
'verified'
)
task_progress
=
TaskProgress
(
action_name
,
students_to_generate_certs_for
.
count
(),
start_time
)
current_step
=
{
'step'
:
'Calculating students already have certificates'
}
...
...
This diff is collapsed.
Click to expand it.
lms/templates/instructor/instructor_dashboard_2/certificates.html
View file @
dacfcc98
...
...
@@ -130,6 +130,12 @@ from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_str
</div>
<div>
<label>
<input
id=
"certificate_status_verified_users_with_audit_certs}"
type=
"checkbox"
name=
"certificate_statuses"
value=
"verified_users_with_audit_certs"
>
${_("Regenerate for verified learners with audit certificates. ({count})").format(count=section_data['verified_users_with_audit_certs'])}
</label>
</div>
<div>
<label>
<input
id=
"certificate_status_${section_data['status'].error}"
type=
"checkbox"
name=
"certificate_statuses"
value=
"${section_data['status'].error}"
>
${_("Regenerate for learners in an error state. ({count})").format(count=section_data['certificate_statuses_with_count'].get(section_data['status'].error, 0))}
</label>
...
...
This diff is collapsed.
Click to expand it.
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