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
7fa9d73a
Commit
7fa9d73a
authored
Oct 15, 2015
by
Ahsan Ulhaq
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update Credit Eligibility Table for Skips
ECOM-2551
parent
6fb89fee
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
104 additions
and
25 deletions
+104
-25
lms/djangoapps/courseware/views.py
+4
-1
lms/djangoapps/verify_student/services.py
+19
-0
lms/djangoapps/verify_student/tests/test_services.py
+59
-21
lms/templates/courseware/progress.html
+6
-3
openedx/core/djangoapps/credit/models.py
+1
-0
openedx/core/djangoapps/credit/tests/test_api.py
+15
-0
No files found.
lms/djangoapps/courseware/views.py
View file @
7fa9d73a
...
...
@@ -1045,6 +1045,9 @@ def _credit_course_requirements(course_key, student):
if
not
(
settings
.
FEATURES
.
get
(
"ENABLE_CREDIT_ELIGIBILITY"
,
False
)
and
is_credit_course
(
course_key
)):
return
None
# Credit requirement statuses for which user does not remain eligible to get credit.
non_eligible_statuses
=
[
'failed'
,
'declined'
]
# Retrieve the status of the user for each eligibility requirement in the course.
# For each requirement, the user's status is either "satisfied", "failed", or None.
# In this context, `None` means that we don't know the user's status, either because
...
...
@@ -1068,7 +1071,7 @@ def _credit_course_requirements(course_key, student):
# If the user has *failed* any requirements (for example, if a photo verification is denied),
# then the user is NOT eligible for credit.
elif
any
(
requirement
[
'status'
]
==
'failed'
for
requirement
in
requirement_statuses
):
elif
any
(
requirement
[
'status'
]
in
non_eligible_statuses
for
requirement
in
requirement_statuses
):
eligibility_status
=
"not_eligible"
# Otherwise, the user may be eligible for credit, but the user has not
...
...
lms/djangoapps/verify_student/services.py
View file @
7fa9d73a
...
...
@@ -95,6 +95,7 @@ class ReverificationService(object):
course_id
=
course_key
,
checkpoint_location
=
related_assessment_location
)
user
=
User
.
objects
.
get
(
id
=
user_id
)
# user can skip a reverification attempt only if that user has not already
# skipped an attempt
...
...
@@ -102,6 +103,24 @@ class ReverificationService(object):
SkippedReverification
.
add_skipped_reverification_attempt
(
checkpoint
,
user_id
,
course_key
)
except
IntegrityError
:
log
.
exception
(
"Skipped attempt already exists for user
%
s: with course
%
s:"
,
user_id
,
unicode
(
course_id
))
return
try
:
# Avoid circular import
from
openedx.core.djangoapps.credit.api
import
set_credit_requirement_status
# As a user skips the reverification it declines to fulfill the requirement so
# requirement sets to declined.
set_credit_requirement_status
(
user
.
username
,
course_key
,
'reverification'
,
checkpoint
.
checkpoint_location
,
status
=
'declined'
)
except
Exception
as
err
:
# pylint: disable=broad-except
log
.
error
(
"Unable to add credit requirement status for user with id
%
d:
%
s"
,
user_id
,
err
)
def
get_attempts
(
self
,
user_id
,
course_id
,
related_assessment_location
):
"""Get re-verification attempts against a user for a given 'checkpoint'
...
...
lms/djangoapps/verify_student/tests/test_services.py
View file @
7fa9d73a
...
...
@@ -4,6 +4,8 @@ Tests of re-verification service.
import
ddt
from
opaque_keys.edx.keys
import
CourseKey
from
course_modes.models
import
CourseMode
from
course_modes.tests.factories
import
CourseModeFactory
from
student.models
import
CourseEnrollment
...
...
@@ -11,6 +13,8 @@ from student.tests.factories import UserFactory
from
verify_student.models
import
VerificationCheckpoint
,
VerificationStatus
,
SkippedReverification
from
verify_student.services
import
ReverificationService
from
openedx.core.djangoapps.credit.api
import
get_credit_requirement_status
,
set_credit_requirements
from
openedx.core.djangoapps.credit.models
import
CreditCourse
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
...
...
@@ -25,20 +29,22 @@ class TestReverificationService(ModuleStoreTestCase):
super
(
TestReverificationService
,
self
)
.
setUp
()
self
.
user
=
UserFactory
.
create
(
username
=
"rusty"
,
password
=
"test"
)
course
=
CourseFactory
.
create
(
org
=
'Robot'
,
number
=
'999'
,
display_name
=
'Test Course'
)
self
.
course_
key
=
course
.
id
self
.
course
=
CourseFactory
.
create
(
org
=
'Robot'
,
number
=
'999'
,
display_name
=
'Test Course'
)
self
.
course_
id
=
self
.
course
.
id
CourseModeFactory
(
mode_slug
=
"verified"
,
course_id
=
self
.
course_
key
,
course_id
=
self
.
course_
id
,
min_price
=
100
,
)
self
.
item
=
ItemFactory
.
create
(
parent
=
course
,
category
=
'chapter'
,
display_name
=
'Test Section'
)
self
.
course_key
=
CourseKey
.
from_string
(
unicode
(
self
.
course_id
))
self
.
item
=
ItemFactory
.
create
(
parent
=
self
.
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
org
=
self
.
course_
id
.
org
,
course
=
self
.
course_id
.
course
)
# Enroll in a verified mode
self
.
enrollment
=
CourseEnrollment
.
enroll
(
self
.
user
,
self
.
course_
key
,
mode
=
CourseMode
.
VERIFIED
)
self
.
enrollment
=
CourseEnrollment
.
enroll
(
self
.
user
,
self
.
course_
id
,
mode
=
CourseMode
.
VERIFIED
)
@ddt.data
(
'final'
,
'midterm'
)
def
test_start_verification
(
self
,
checkpoint_name
):
...
...
@@ -50,16 +56,16 @@ class TestReverificationService(ModuleStoreTestCase):
"""
reverification_service
=
ReverificationService
()
checkpoint_location
=
u'i4x://{org}/{course}/edx-reverification-block/{checkpoint}'
.
format
(
org
=
self
.
course_
key
.
org
,
course
=
self
.
course_key
.
course
,
checkpoint
=
checkpoint_name
org
=
self
.
course_
id
.
org
,
course
=
self
.
course_id
.
course
,
checkpoint
=
checkpoint_name
)
expected_url
=
(
'/verify_student/reverify'
'/{course_key}'
'/{checkpoint_location}/'
)
.
format
(
course_key
=
unicode
(
self
.
course_
key
),
checkpoint_location
=
checkpoint_location
)
)
.
format
(
course_key
=
unicode
(
self
.
course_
id
),
checkpoint_location
=
checkpoint_location
)
self
.
assertEqual
(
reverification_service
.
start_verification
(
unicode
(
self
.
course_
key
),
checkpoint_location
),
reverification_service
.
start_verification
(
unicode
(
self
.
course_
id
),
checkpoint_location
),
expected_url
)
...
...
@@ -69,22 +75,22 @@ class TestReverificationService(ModuleStoreTestCase):
"""
reverification_service
=
ReverificationService
()
self
.
assertIsNone
(
reverification_service
.
get_status
(
self
.
user
.
id
,
unicode
(
self
.
course_
key
),
self
.
final_checkpoint_location
)
reverification_service
.
get_status
(
self
.
user
.
id
,
unicode
(
self
.
course_
id
),
self
.
final_checkpoint_location
)
)
checkpoint_obj
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
unicode
(
self
.
course_
key
),
course_id
=
unicode
(
self
.
course_
id
),
checkpoint_location
=
self
.
final_checkpoint_location
)
VerificationStatus
.
objects
.
create
(
checkpoint
=
checkpoint_obj
,
user
=
self
.
user
,
status
=
'submitted'
)
self
.
assertEqual
(
reverification_service
.
get_status
(
self
.
user
.
id
,
unicode
(
self
.
course_
key
),
self
.
final_checkpoint_location
),
reverification_service
.
get_status
(
self
.
user
.
id
,
unicode
(
self
.
course_
id
),
self
.
final_checkpoint_location
),
'submitted'
)
VerificationStatus
.
objects
.
create
(
checkpoint
=
checkpoint_obj
,
user
=
self
.
user
,
status
=
'approved'
)
self
.
assertEqual
(
reverification_service
.
get_status
(
self
.
user
.
id
,
unicode
(
self
.
course_
key
),
self
.
final_checkpoint_location
),
reverification_service
.
get_status
(
self
.
user
.
id
,
unicode
(
self
.
course_
id
),
self
.
final_checkpoint_location
),
'approved'
)
...
...
@@ -94,36 +100,68 @@ class TestReverificationService(ModuleStoreTestCase):
"""
reverification_service
=
ReverificationService
()
VerificationCheckpoint
.
objects
.
create
(
course_id
=
unicode
(
self
.
course_
key
),
course_id
=
unicode
(
self
.
course_
id
),
checkpoint_location
=
self
.
final_checkpoint_location
)
reverification_service
.
skip_verification
(
self
.
user
.
id
,
unicode
(
self
.
course_
key
),
self
.
final_checkpoint_location
)
reverification_service
.
skip_verification
(
self
.
user
.
id
,
unicode
(
self
.
course_
id
),
self
.
final_checkpoint_location
)
self
.
assertEqual
(
SkippedReverification
.
objects
.
filter
(
user
=
self
.
user
,
course_id
=
self
.
course_
key
)
.
count
(),
SkippedReverification
.
objects
.
filter
(
user
=
self
.
user
,
course_id
=
self
.
course_
id
)
.
count
(),
1
)
# now test that a user can have only one entry for a skipped
# reverification for a course
reverification_service
.
skip_verification
(
self
.
user
.
id
,
unicode
(
self
.
course_
key
),
self
.
final_checkpoint_location
)
reverification_service
.
skip_verification
(
self
.
user
.
id
,
unicode
(
self
.
course_
id
),
self
.
final_checkpoint_location
)
self
.
assertEqual
(
SkippedReverification
.
objects
.
filter
(
user
=
self
.
user
,
course_id
=
self
.
course_
key
)
.
count
(),
SkippedReverification
.
objects
.
filter
(
user
=
self
.
user
,
course_id
=
self
.
course_
id
)
.
count
(),
1
)
# testing service for skipped attempt.
self
.
assertEqual
(
reverification_service
.
get_status
(
self
.
user
.
id
,
unicode
(
self
.
course_
key
),
self
.
final_checkpoint_location
),
reverification_service
.
get_status
(
self
.
user
.
id
,
unicode
(
self
.
course_
id
),
self
.
final_checkpoint_location
),
'skipped'
)
def
test_declined_verification_on_skip
(
self
):
"""Test that status with value 'declined' is added in credit
requirement status model when a user skip's an ICRV.
"""
reverification_service
=
ReverificationService
()
checkpoint
=
VerificationCheckpoint
.
objects
.
create
(
course_id
=
unicode
(
self
.
course_id
),
checkpoint_location
=
self
.
final_checkpoint_location
)
# Create credit course and set credit requirements.
CreditCourse
.
objects
.
create
(
course_key
=
self
.
course_key
,
enabled
=
True
)
set_credit_requirements
(
self
.
course_key
,
[
{
"namespace"
:
"reverification"
,
"name"
:
checkpoint
.
checkpoint_location
,
"display_name"
:
"Assessment 1"
,
"criteria"
:
{},
}
]
)
reverification_service
.
skip_verification
(
self
.
user
.
id
,
unicode
(
self
.
course_id
),
self
.
final_checkpoint_location
)
requirement_status
=
get_credit_requirement_status
(
self
.
course_key
,
self
.
user
.
username
,
'reverification'
,
checkpoint
.
checkpoint_location
)
self
.
assertEqual
(
SkippedReverification
.
objects
.
filter
(
user
=
self
.
user
,
course_id
=
self
.
course_id
)
.
count
(),
1
)
self
.
assertEqual
(
len
(
requirement_status
),
1
)
self
.
assertEqual
(
requirement_status
[
0
]
.
get
(
'name'
),
checkpoint
.
checkpoint_location
)
self
.
assertEqual
(
requirement_status
[
0
]
.
get
(
'status'
),
'declined'
)
def
test_get_attempts
(
self
):
"""Check verification attempts count against a user for a given
'checkpoint' and 'course_id'.
"""
reverification_service
=
ReverificationService
()
course_id
=
unicode
(
self
.
course_
key
)
course_id
=
unicode
(
self
.
course_
id
)
self
.
assertEqual
(
reverification_service
.
get_attempts
(
self
.
user
.
id
,
course_id
,
self
.
final_checkpoint_location
),
0
...
...
@@ -147,5 +185,5 @@ class TestReverificationService(ModuleStoreTestCase):
# Should be marked as "skipped" (opted out)
service
=
ReverificationService
()
status
=
service
.
get_status
(
self
.
user
.
id
,
unicode
(
self
.
course_
key
),
self
.
final_checkpoint_location
)
status
=
service
.
get_status
(
self
.
user
.
id
,
unicode
(
self
.
course_
id
),
self
.
final_checkpoint_location
)
self
.
assertEqual
(
status
,
service
.
NON_VERIFIED_TRACK
)
lms/templates/courseware/progress.html
View file @
7fa9d73a
...
...
@@ -132,10 +132,13 @@ from django.utils.http import urlquote_plus
%if requirement['status'] == 'submitted':
<span
class=
"requirement-submitted"
>
${_("Verification Submitted")}
</span>
%elif requirement['status'] == 'failed':
<i
class=
"fa fa-times"
></i>
<i
class=
"fa fa-times"
aria-hidden=
"true"
></i>
<span>
${_("Verification Failed" )}
</span>
%elif requirement['status'] == 'declined':
<i
class=
"fa fa-times"
aria-hidden=
"true"
></i>
<span>
${_("Verification Declined" )}
</span>
%elif requirement['status'] == 'satisfied':
<i
class=
"fa fa-check"
></i>
<i
class=
"fa fa-check"
aria-hidden=
"true"
></i>
% if requirement['namespace'] == 'grade' and 'final_grade' in requirement['reason']:
<span>
${int(requirement['reason']['final_grade'] * 100)}%
</span>
% else:
...
...
@@ -149,7 +152,7 @@ from django.utils.http import urlquote_plus
</div>
%endfor
</div>
<button
class=
"detail-collapse"
aria-live=
"polite"
><i
class=
"fa fa-caret-up"
></i>
<button
class=
"detail-collapse"
aria-live=
"polite"
><i
class=
"fa fa-caret-up"
aria-hidden=
"true"
></i>
<span
class=
"requirement-detail"
>
${_("Less")}
</span>
</button>
</div>
...
...
openedx/core/djangoapps/credit/models.py
View file @
7fa9d73a
...
...
@@ -411,6 +411,7 @@ class CreditRequirementStatus(TimeStampedModel):
REQUIREMENT_STATUS_CHOICES
=
(
(
"satisfied"
,
"satisfied"
),
(
"failed"
,
"failed"
),
(
"declined"
,
"declined"
),
)
username
=
models
.
CharField
(
max_length
=
255
,
db_index
=
True
)
...
...
openedx/core/djangoapps/credit/tests/test_api.py
View file @
7fa9d73a
...
...
@@ -315,6 +315,21 @@ class CreditRequirementApiTests(CreditApiTestBase):
req_status
=
api
.
get_credit_requirement_status
(
self
.
course_key
,
"staff"
,
namespace
=
"grade"
,
name
=
"grade"
)
self
.
assertEqual
(
req_status
[
0
][
"status"
],
"failed"
)
# Set the requirement to "declined" and check that it's actually set
api
.
set_credit_requirement_status
(
"staff"
,
self
.
course_key
,
"reverification"
,
"i4x://edX/DemoX/edx-reverification-block/assessment_uuid"
,
status
=
"declined"
)
req_status
=
api
.
get_credit_requirement_status
(
self
.
course_key
,
"staff"
,
namespace
=
"reverification"
,
name
=
"i4x://edX/DemoX/edx-reverification-block/assessment_uuid"
)
self
.
assertEqual
(
req_status
[
0
][
"status"
],
"declined"
)
def
test_remove_credit_requirement_status
(
self
):
self
.
add_credit_course
()
requirements
=
[
...
...
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