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
65c4f1df
Commit
65c4f1df
authored
May 22, 2015
by
Muhammad Shoaib
Committed by
Chris Dodge
Jun 02, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
SOL-236 Manual Enrollments
parent
9008548c
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
210 additions
and
30 deletions
+210
-30
common/djangoapps/student/migrations/0050_auto__add_manualenrollmentaudit.py
+0
-0
common/djangoapps/student/models.py
+75
-8
common/djangoapps/student/views.py
+11
-2
lms/djangoapps/instructor/enrollment.py
+3
-4
lms/djangoapps/instructor/paidcourse_enrollment_report.py
+8
-2
lms/djangoapps/instructor/tests/test_api.py
+0
-0
lms/djangoapps/instructor/tests/test_api_email_localization.py
+1
-1
lms/djangoapps/instructor/views/api.py
+65
-7
lms/djangoapps/instructor/views/instructor_dashboard.py
+5
-4
lms/djangoapps/instructor_task/tests/test_tasks_helper.py
+23
-1
lms/static/coffee/src/instructor_dashboard/membership.coffee
+9
-0
lms/templates/instructor/instructor_dashboard_2/membership.html
+10
-1
No files found.
common/djangoapps/student/migrations/0050_auto__add_manualenrollmentaudit.py
0 → 100644
View file @
65c4f1df
This diff is collapsed.
Click to expand it.
common/djangoapps/student/models.py
View file @
65c4f1df
...
...
@@ -20,7 +20,7 @@ from collections import defaultdict, OrderedDict
import
dogstats_wrapper
as
dog_stats_api
from
urllib
import
urlencode
from
django.utils.translation
import
ugettext
as
_
,
ugettext_lazy
from
django.utils.translation
import
ugettext
_lazy
as
_
from
django.conf
import
settings
from
django.utils
import
timezone
from
django.contrib.auth.models
import
User
...
...
@@ -59,6 +59,26 @@ log = logging.getLogger(__name__)
AUDIT_LOG
=
logging
.
getLogger
(
"audit"
)
SessionStore
=
import_module
(
settings
.
SESSION_ENGINE
)
.
SessionStore
# pylint: disable=invalid-name
UNENROLLED_TO_ALLOWEDTOENROLL
=
'from unenrolled to allowed to enroll'
ALLOWEDTOENROLL_TO_ENROLLED
=
'from allowed to enroll to enrolled'
ENROLLED_TO_ENROLLED
=
'from enrolled to enrolled'
ENROLLED_TO_UNENROLLED
=
'from enrolled to unenrolled'
UNENROLLED_TO_ENROLLED
=
'from unenrolled to enrolled'
ALLOWEDTOENROLL_TO_UNENROLLED
=
'from allowed to enroll to enrolled'
UNENROLLED_TO_UNENROLLED
=
'from unenrolled to unenrolled'
DEFAULT_TRANSITION_STATE
=
'N/A'
TRANSITION_STATES
=
(
(
UNENROLLED_TO_ALLOWEDTOENROLL
,
UNENROLLED_TO_ALLOWEDTOENROLL
),
(
ALLOWEDTOENROLL_TO_ENROLLED
,
ALLOWEDTOENROLL_TO_ENROLLED
),
(
ENROLLED_TO_ENROLLED
,
ENROLLED_TO_ENROLLED
),
(
ENROLLED_TO_UNENROLLED
,
ENROLLED_TO_UNENROLLED
),
(
UNENROLLED_TO_ENROLLED
,
UNENROLLED_TO_ENROLLED
),
(
ALLOWEDTOENROLL_TO_UNENROLLED
,
ALLOWEDTOENROLL_TO_UNENROLLED
),
(
UNENROLLED_TO_UNENROLLED
,
UNENROLLED_TO_UNENROLLED
),
(
DEFAULT_TRANSITION_STATE
,
DEFAULT_TRANSITION_STATE
)
)
class
AnonymousUserId
(
models
.
Model
):
"""
...
...
@@ -1291,6 +1311,53 @@ class CourseEnrollment(models.Model):
return
CourseMode
.
is_verified_slug
(
self
.
mode
)
class
ManualEnrollmentAudit
(
models
.
Model
):
"""
Table for tracking which enrollments were performed through manual enrollment.
"""
enrollment
=
models
.
ForeignKey
(
CourseEnrollment
,
null
=
True
)
enrolled_by
=
models
.
ForeignKey
(
User
,
null
=
True
)
enrolled_email
=
models
.
CharField
(
max_length
=
255
,
db_index
=
True
)
time_stamp
=
models
.
DateTimeField
(
auto_now_add
=
True
,
null
=
True
)
state_transition
=
models
.
CharField
(
max_length
=
255
,
choices
=
TRANSITION_STATES
)
reason
=
models
.
TextField
(
null
=
True
)
@classmethod
def
create_manual_enrollment_audit
(
cls
,
user
,
email
,
state_transition
,
reason
,
enrollment
=
None
):
"""
saves the student manual enrollment information
"""
cls
.
objects
.
create
(
enrolled_by
=
user
,
enrolled_email
=
email
,
state_transition
=
state_transition
,
reason
=
reason
,
enrollment
=
enrollment
)
@classmethod
def
get_manual_enrollment_by_email
(
cls
,
email
):
"""
if matches returns the most recent entry in the table filtered by email else returns None.
"""
try
:
manual_enrollment
=
cls
.
objects
.
filter
(
enrolled_email
=
email
)
.
latest
(
'time_stamp'
)
except
cls
.
DoesNotExist
:
manual_enrollment
=
None
return
manual_enrollment
@classmethod
def
get_manual_enrollment
(
cls
,
enrollment
):
"""
if matches returns the most recent entry in the table filtered by enrollment else returns None,
"""
try
:
manual_enrollment
=
cls
.
objects
.
filter
(
enrollment
=
enrollment
)
.
latest
(
'time_stamp'
)
except
cls
.
DoesNotExist
:
manual_enrollment
=
None
return
manual_enrollment
class
CourseEnrollmentAllowed
(
models
.
Model
):
"""
Table of users (specified by email address strings) who are allowed to enroll in a specified course.
...
...
@@ -1536,16 +1603,16 @@ class LinkedInAddToProfileConfiguration(ConfigurationModel):
"""
MODE_TO_CERT_NAME
=
{
"honor"
:
ugettext_lazy
(
u"{platform_name} Honor Code Certificate for {course_name}"
),
"verified"
:
ugettext_lazy
(
u"{platform_name} Verified Certificate for {course_name}"
),
"professional"
:
ugettext_lazy
(
u"{platform_name} Professional Certificate for {course_name}"
),
"no-id-professional"
:
ugettext_lazy
(
"honor"
:
_
(
u"{platform_name} Honor Code Certificate for {course_name}"
),
"verified"
:
_
(
u"{platform_name} Verified Certificate for {course_name}"
),
"professional"
:
_
(
u"{platform_name} Professional Certificate for {course_name}"
),
"no-id-professional"
:
_
(
u"{platform_name} Professional Certificate for {course_name}"
),
}
company_identifier
=
models
.
TextField
(
help_text
=
ugettext_lazy
(
help_text
=
_
(
u"The company identifier for the LinkedIn Add-to-Profile button "
u"e.g 0_0dPSPyS070e0HsE9HNz_13_d11_"
)
...
...
@@ -1558,7 +1625,7 @@ class LinkedInAddToProfileConfiguration(ConfigurationModel):
max_length
=
10
,
default
=
""
,
blank
=
True
,
help_text
=
ugettext_lazy
(
help_text
=
_
(
u"Short identifier for the LinkedIn partner used in the tracking code. "
u"(Example: 'edx') "
u"If no value is provided, tracking codes will not be sent to LinkedIn."
...
...
@@ -1699,5 +1766,5 @@ class LanguageProficiency(models.Model):
max_length
=
16
,
blank
=
False
,
choices
=
settings
.
ALL_LANGUAGES
,
help_text
=
ugettext_lazy
(
"The ISO 639-1 language code for this language."
)
help_text
=
_
(
"The ISO 639-1 language code for this language."
)
)
common/djangoapps/student/views.py
View file @
65c4f1df
...
...
@@ -55,7 +55,7 @@ from student.models import (
PendingEmailChange
,
CourseEnrollment
,
unique_id_for_user
,
CourseEnrollmentAllowed
,
UserStanding
,
LoginFailures
,
create_comments_service_user
,
PasswordHistory
,
UserSignupSource
,
DashboardConfiguration
,
LinkedInAddToProfileConfiguration
)
DashboardConfiguration
,
LinkedInAddToProfileConfiguration
,
ManualEnrollmentAudit
,
ALLOWEDTOENROLL_TO_ENROLLED
)
from
student.forms
import
AccountCreationForm
,
PasswordResetFormNoActive
from
verify_student.models
import
SoftwareSecurePhotoVerification
,
MidcourseReverificationWindow
...
...
@@ -1783,7 +1783,16 @@ def activate_account(request, key):
ceas
=
CourseEnrollmentAllowed
.
objects
.
filter
(
email
=
student
[
0
]
.
email
)
for
cea
in
ceas
:
if
cea
.
auto_enroll
:
CourseEnrollment
.
enroll
(
student
[
0
],
cea
.
course_id
)
enrollment
=
CourseEnrollment
.
enroll
(
student
[
0
],
cea
.
course_id
)
manual_enrollment_audit
=
ManualEnrollmentAudit
.
get_manual_enrollment_by_email
(
student
[
0
]
.
email
)
if
manual_enrollment_audit
is
not
None
:
# get the enrolled by user and reason from the ManualEnrollmentAudit table.
# then create a new ManualEnrollmentAudit table entry for the same email
# different transition state.
ManualEnrollmentAudit
.
create_manual_enrollment_audit
(
manual_enrollment_audit
.
enrolled_by
,
student
[
0
]
.
email
,
ALLOWEDTOENROLL_TO_ENROLLED
,
manual_enrollment_audit
.
reason
,
enrollment
)
# enroll student in any pending CCXs he/she may have if auto_enroll flag is set
if
settings
.
FEATURES
.
get
(
'CUSTOM_COURSES_EDX'
):
...
...
lms/djangoapps/instructor/enrollment.py
View file @
65c4f1df
...
...
@@ -100,7 +100,7 @@ def enroll_email(course_id, student_email, auto_enroll=False, email_students=Fal
representing state before and after the action.
"""
previous_state
=
EmailEnrollmentState
(
course_id
,
student_email
)
enrollment_obj
=
None
if
previous_state
.
user
:
# if the student is currently unenrolled, don't enroll them in their
# previous mode
...
...
@@ -108,7 +108,7 @@ def enroll_email(course_id, student_email, auto_enroll=False, email_students=Fal
if
previous_state
.
enrollment
:
course_mode
=
previous_state
.
mode
CourseEnrollment
.
enroll_by_email
(
student_email
,
course_id
,
course_mode
)
enrollment_obj
=
CourseEnrollment
.
enroll_by_email
(
student_email
,
course_id
,
course_mode
)
if
email_students
:
email_params
[
'message'
]
=
'enrolled_enroll'
email_params
[
'email_address'
]
=
student_email
...
...
@@ -125,7 +125,7 @@ def enroll_email(course_id, student_email, auto_enroll=False, email_students=Fal
after_state
=
EmailEnrollmentState
(
course_id
,
student_email
)
return
previous_state
,
after_state
return
previous_state
,
after_state
,
enrollment_obj
def
unenroll_email
(
course_id
,
student_email
,
email_students
=
False
,
email_params
=
None
,
language
=
None
):
...
...
@@ -141,7 +141,6 @@ def unenroll_email(course_id, student_email, email_students=False, email_params=
representing state before and after the action.
"""
previous_state
=
EmailEnrollmentState
(
course_id
,
student_email
)
if
previous_state
.
enrollment
:
CourseEnrollment
.
unenroll_by_email
(
student_email
,
course_id
)
if
email_students
:
...
...
lms/djangoapps/instructor/paidcourse_enrollment_report.py
View file @
65c4f1df
...
...
@@ -11,7 +11,7 @@ from instructor.enrollment_report import BaseAbstractEnrollmentReportProvider
from
microsite_configuration
import
microsite
from
shoppingcart.models
import
RegistrationCodeRedemption
,
PaidCourseRegistration
,
CouponRedemption
,
OrderItem
,
\
InvoiceTransaction
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
,
ManualEnrollmentAudit
class
PaidCourseEnrollmentReportProvider
(
BaseAbstractEnrollmentReportProvider
):
...
...
@@ -56,7 +56,13 @@ class PaidCourseEnrollmentReportProvider(BaseAbstractEnrollmentReportProvider):
elif
paid_course_reg_item
is
not
None
:
enrollment_source
=
_
(
'Credit Card - Individual'
)
else
:
enrollment_source
=
_
(
'Manually Enrolled'
)
manual_enrollment
=
ManualEnrollmentAudit
.
get_manual_enrollment
(
course_enrollment
)
if
manual_enrollment
is
not
None
:
enrollment_source
=
_
(
'manually enrolled by user_id {user_id}, enrollment state transition: {transition}'
)
.
format
(
user_id
=
manual_enrollment
.
enrolled_by_id
,
transition
=
manual_enrollment
.
state_transition
)
else
:
enrollment_source
=
_
(
'Manually Enrolled'
)
enrollment_date
=
course_enrollment
.
created
.
strftime
(
"
%
B
%
d,
%
Y"
)
currently_enrolled
=
course_enrollment
.
is_active
...
...
lms/djangoapps/instructor/tests/test_api.py
View file @
65c4f1df
This diff is collapsed.
Click to expand it.
lms/djangoapps/instructor/tests/test_api_email_localization.py
View file @
65c4f1df
...
...
@@ -42,7 +42,7 @@ class TestInstructorAPIEnrollmentEmailLocalization(ModuleStoreTestCase):
Update the current student enrollment status.
"""
url
=
reverse
(
'students_update_enrollment'
,
kwargs
=
{
'course_id'
:
self
.
course
.
id
.
to_deprecated_string
()})
args
=
{
'identifiers'
:
student_email
,
'email_students'
:
'true'
,
'action'
:
action
}
args
=
{
'identifiers'
:
student_email
,
'email_students'
:
'true'
,
'action'
:
action
,
'reason'
:
'testing'
}
response
=
self
.
client
.
post
(
url
,
args
)
return
response
...
...
lms/djangoapps/instructor/views/api.py
View file @
65c4f1df
...
...
@@ -31,7 +31,10 @@ import urllib
import
decimal
from
student
import
auth
from
student.roles
import
GlobalStaff
,
CourseSalesAdminRole
,
CourseFinanceAdminRole
from
util.file
import
store_uploaded_file
,
course_and_time_based_filename_generator
,
FileValidationException
,
UniversalNewlineIterator
from
util.file
import
(
store_uploaded_file
,
course_and_time_based_filename_generator
,
FileValidationException
,
UniversalNewlineIterator
)
from
util.json_request
import
JsonResponse
from
instructor.views.instructor_task_helpers
import
extract_email_features
,
extract_task_features
...
...
@@ -59,7 +62,10 @@ from shoppingcart.models import (
)
from
student.models
import
(
CourseEnrollment
,
unique_id_for_user
,
anonymous_id_for_user
,
UserProfile
,
Registration
,
EntranceExamConfiguration
UserProfile
,
Registration
,
EntranceExamConfiguration
,
ManualEnrollmentAudit
,
UNENROLLED_TO_ALLOWEDTOENROLL
,
ALLOWEDTOENROLL_TO_ENROLLED
,
ENROLLED_TO_ENROLLED
,
ENROLLED_TO_UNENROLLED
,
UNENROLLED_TO_ENROLLED
,
UNENROLLED_TO_UNENROLLED
,
ALLOWEDTOENROLL_TO_UNENROLLED
,
DEFAULT_TRANSITION_STATE
)
import
instructor_task.api
from
instructor_task.api_helper
import
AlreadyRunningError
...
...
@@ -413,7 +419,11 @@ def register_and_enroll_students(request, course_id): # pylint: disable=too-man
# make sure user is enrolled in course
if
not
CourseEnrollment
.
is_enrolled
(
user
,
course_id
):
CourseEnrollment
.
enroll
(
user
,
course_id
)
enrollment_obj
=
CourseEnrollment
.
enroll
(
user
,
course_id
)
reason
=
'Enrolling via csv upload'
ManualEnrollmentAudit
.
create_manual_enrollment_audit
(
request
.
user
,
email
,
UNENROLLED_TO_ENROLLED
,
reason
,
enrollment_obj
)
log
.
info
(
u'user
%
s enrolled in the course
%
s'
,
username
,
...
...
@@ -427,7 +437,11 @@ def register_and_enroll_students(request, course_id): # pylint: disable=too-man
password
=
generate_unique_password
(
generated_passwords
)
try
:
create_and_enroll_user
(
email
,
username
,
name
,
country
,
password
,
course_id
)
enrollment_obj
=
create_and_enroll_user
(
email
,
username
,
name
,
country
,
password
,
course_id
)
reason
=
'Enrolling via csv upload'
ManualEnrollmentAudit
.
create_manual_enrollment_audit
(
request
.
user
,
email
,
UNENROLLED_TO_ENROLLED
,
reason
,
enrollment_obj
)
except
IntegrityError
:
row_errors
.
append
({
'username'
:
username
,
'email'
:
email
,
'response'
:
_
(
'Username {user} already exists.'
)
.
format
(
user
=
username
)})
...
...
@@ -496,7 +510,7 @@ def create_and_enroll_user(email, username, name, country, password, course_id):
profile
.
save
()
# try to enroll the user in this course
CourseEnrollment
.
enroll
(
user
,
course_id
)
return
CourseEnrollment
.
enroll
(
user
,
course_id
)
@ensure_csrf_cookie
...
...
@@ -546,6 +560,18 @@ def students_update_enrollment(request, course_id):
identifiers
=
_split_input_list
(
identifiers_raw
)
auto_enroll
=
request
.
POST
.
get
(
'auto_enroll'
)
in
[
'true'
,
'True'
,
True
]
email_students
=
request
.
POST
.
get
(
'email_students'
)
in
[
'true'
,
'True'
,
True
]
is_white_label
=
CourseMode
.
is_white_label
(
course_id
)
reason
=
request
.
POST
.
get
(
'reason'
)
if
is_white_label
:
if
not
reason
:
return
JsonResponse
(
{
'action'
:
action
,
'results'
:
[{
'error'
:
True
}],
'auto_enroll'
:
auto_enroll
,
},
status
=
400
)
enrollment_obj
=
None
state_transition
=
DEFAULT_TRANSITION_STATE
email_params
=
{}
if
email_students
:
...
...
@@ -571,15 +597,44 @@ def students_update_enrollment(request, course_id):
# validity (obviously, cannot check if email actually /exists/,
# simply that it is plausibly valid)
validate_email
(
email
)
# Raises ValidationError if invalid
if
action
==
'enroll'
:
before
,
after
=
enroll_email
(
before
,
after
,
enrollment_obj
=
enroll_email
(
course_id
,
email
,
auto_enroll
,
email_students
,
email_params
,
language
=
language
)
before_enrollment
=
before
.
to_dict
()[
'enrollment'
]
before_user_registered
=
before
.
to_dict
()[
'user'
]
before_allowed
=
before
.
to_dict
()[
'allowed'
]
after_enrollment
=
after
.
to_dict
()[
'enrollment'
]
after_allowed
=
after
.
to_dict
()[
'allowed'
]
if
before_user_registered
:
if
after_enrollment
:
if
before_enrollment
:
state_transition
=
ENROLLED_TO_ENROLLED
else
:
if
before_allowed
:
state_transition
=
ALLOWEDTOENROLL_TO_ENROLLED
else
:
state_transition
=
UNENROLLED_TO_ENROLLED
else
:
if
after_allowed
:
state_transition
=
UNENROLLED_TO_ALLOWEDTOENROLL
elif
action
==
'unenroll'
:
before
,
after
=
unenroll_email
(
course_id
,
email
,
email_students
,
email_params
,
language
=
language
)
before_enrollment
=
before
.
to_dict
()[
'enrollment'
]
before_allowed
=
before
.
to_dict
()[
'allowed'
]
if
before_enrollment
:
state_transition
=
ENROLLED_TO_UNENROLLED
else
:
if
before_allowed
:
state_transition
=
ALLOWEDTOENROLL_TO_UNENROLLED
else
:
state_transition
=
UNENROLLED_TO_UNENROLLED
else
:
return
HttpResponseBadRequest
(
strip_tags
(
"Unrecognized action '{}'"
.
format
(
action
)
...
...
@@ -604,6 +659,9 @@ def students_update_enrollment(request, course_id):
})
else
:
ManualEnrollmentAudit
.
create_manual_enrollment_audit
(
request
.
user
,
email
,
state_transition
,
reason
,
enrollment_obj
)
results
.
append
({
'identifier'
:
identifier
,
'before'
:
before
.
to_dict
(),
...
...
lms/djangoapps/instructor/views/instructor_dashboard.py
View file @
65c4f1df
...
...
@@ -71,9 +71,11 @@ def instructor_dashboard_2(request, course_id):
if
not
access
[
'staff'
]:
raise
Http404
()
is_white_label
=
CourseMode
.
is_white_label
(
course_key
)
sections
=
[
_section_course_info
(
course
,
access
),
_section_membership
(
course
,
access
),
_section_membership
(
course
,
access
,
is_white_label
),
_section_cohort_management
(
course
,
access
),
_section_student_admin
(
course
,
access
),
_section_data_download
(
course
,
access
),
...
...
@@ -92,8 +94,6 @@ def instructor_dashboard_2(request, course_id):
unicode
(
course_key
),
len
(
paid_modes
)
)
is_white_label
=
CourseMode
.
is_white_label
(
course_key
)
if
(
settings
.
FEATURES
.
get
(
'INDIVIDUAL_DUE_DATES'
)
and
access
[
'instructor'
]):
sections
.
insert
(
3
,
_section_extensions
(
course
))
...
...
@@ -321,7 +321,7 @@ def _section_course_info(course, access):
return
section_data
def
_section_membership
(
course
,
access
):
def
_section_membership
(
course
,
access
,
is_white_label
):
""" Provide data for the corresponding dashboard section """
course_key
=
course
.
id
ccx_enabled
=
settings
.
FEATURES
.
get
(
'CUSTOM_COURSES_EDX'
,
False
)
and
course
.
enable_ccx
...
...
@@ -330,6 +330,7 @@ def _section_membership(course, access):
'section_display_name'
:
_
(
'Membership'
),
'access'
:
access
,
'ccx_is_enabled'
:
ccx_enabled
,
'is_white_label'
:
is_white_label
,
'enroll_button_url'
:
reverse
(
'students_update_enrollment'
,
kwargs
=
{
'course_id'
:
unicode
(
course_key
)}),
'unenroll_button_url'
:
reverse
(
'students_update_enrollment'
,
kwargs
=
{
'course_id'
:
unicode
(
course_key
)}),
'upload_student_csv_button_url'
:
reverse
(
'register_and_enroll_students'
,
kwargs
=
{
'course_id'
:
unicode
(
course_key
)}),
...
...
lms/djangoapps/instructor_task/tests/test_tasks_helper.py
View file @
65c4f1df
...
...
@@ -27,7 +27,7 @@ from openedx.core.djangoapps.user_api.partition_schemes import RandomUserPartiti
from
shoppingcart.models
import
Order
,
PaidCourseRegistration
,
CourseRegistrationCode
,
Invoice
,
\
CourseRegistrationCodeInvoiceItem
,
InvoiceTransaction
from
student.tests.factories
import
UserFactory
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
,
ManualEnrollmentAudit
,
ALLOWEDTOENROLL_TO_ENROLLED
from
verify_student.tests.factories
import
SoftwareSecurePhotoVerificationFactory
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
from
xmodule.partitions.partitions
import
Group
,
UserPartition
...
...
@@ -321,6 +321,28 @@ class TestInstructorDetailedEnrollmentReport(TestReportMixin, InstructorTaskCour
self
.
_verify_cell_data_in_csv
(
student
.
username
,
'Enrollment Source'
,
'Credit Card - Individual'
)
self
.
_verify_cell_data_in_csv
(
student
.
username
,
'Payment Status'
,
'purchased'
)
def
test_student_manually_enrolled_in_detailed_enrollment_source
(
self
):
"""
test to check the manually enrolled user enrollment report status
and enrollment source.
"""
student
=
UserFactory
()
enrollment
=
CourseEnrollment
.
enroll
(
student
,
self
.
course
.
id
)
ManualEnrollmentAudit
.
create_manual_enrollment_audit
(
self
.
instructor
,
student
.
email
,
ALLOWEDTOENROLL_TO_ENROLLED
,
'manually enrolling unenrolled user'
,
enrollment
)
task_input
=
{
'features'
:
[]}
with
patch
(
'instructor_task.tasks_helper._get_current_task'
):
result
=
upload_enrollment_report
(
None
,
None
,
self
.
course
.
id
,
task_input
,
'generating_enrollment_report'
)
enrollment_source
=
u'manually enrolled by user_id {user_id}, enrollment state transition: {transition}'
.
format
(
user_id
=
self
.
instructor
.
id
,
transition
=
ALLOWEDTOENROLL_TO_ENROLLED
)
# pylint: disable=no-member
self
.
assertDictContainsSubset
({
'attempted'
:
1
,
'succeeded'
:
1
,
'failed'
:
0
},
result
)
self
.
_verify_cell_data_in_csv
(
student
.
username
,
'Enrollment Source'
,
enrollment_source
)
self
.
_verify_cell_data_in_csv
(
student
.
username
,
'Payment Status'
,
'TBD'
)
def
test_student_used_enrollment_code_for_course_enrollment
(
self
):
"""
test to check the user enrollment source and payment status in the
...
...
lms/static/coffee/src/instructor_dashboard/membership.coffee
View file @
65c4f1df
...
...
@@ -367,6 +367,8 @@ class BatchEnrollment
# gather elements
@
$identifier_input
=
@
$container
.
find
(
"textarea[name='student-ids']"
)
@
$enrollment_button
=
@
$container
.
find
(
".enrollment-button"
)
@
$is_course_white_label
=
@
$container
.
find
(
"#is_course_white_label"
).
val
()
@
$reason_field
=
@
$container
.
find
(
"textarea[name='reason-field']"
)
@
$checkbox_autoenroll
=
@
$container
.
find
(
"input[name='auto-enroll']"
)
@
$checkbox_emailstudents
=
@
$container
.
find
(
"input[name='email-students']"
)
@
$task_response
=
@
$container
.
find
(
".request-response"
)
...
...
@@ -374,12 +376,18 @@ class BatchEnrollment
# attach click handler for enrollment buttons
@
$enrollment_button
.
click
(
event
)
=>
if
@
$is_course_white_label
==
'True'
if
not
@
$reason_field
.
val
()
@
fail_with_error
gettext
"Reason field should not be left blank."
return
false
emailStudents
=
@
$checkbox_emailstudents
.
is
(
':checked'
)
send_data
=
action
:
$
(
event
.
target
).
data
(
'action'
)
# 'enroll' or 'unenroll'
identifiers
:
@
$identifier_input
.
val
()
auto_enroll
:
@
$checkbox_autoenroll
.
is
(
':checked'
)
email_students
:
emailStudents
reason
:
@
$reason_field
.
val
()
$
.
ajax
dataType
:
'json'
...
...
@@ -393,6 +401,7 @@ class BatchEnrollment
# clear the input text field
clear_input
:
->
@
$identifier_input
.
val
''
@
$reason_field
.
val
''
# default for the checkboxes should be checked
@
$checkbox_emailstudents
.
attr
(
'checked'
,
true
)
@
$checkbox_autoenroll
.
attr
(
'checked'
,
true
)
...
...
lms/templates/instructor/instructor_dashboard_2/membership.html
View file @
65c4f1df
...
...
@@ -37,7 +37,16 @@
${_("You will not get notification for emails that bounce, so please double-check spelling.")}
</label>
<textarea
rows=
"6"
name=
"student-ids"
placeholder=
"${_("
Email
Addresses
/
Usernames
")}"
spellcheck=
"false"
></textarea>
</p>
<input
type=
"hidden"
id=
"is_course_white_label"
value=
"${section_data['is_white_label']}"
>
% if section_data['is_white_label']:
<p>
<label
for=
"reason-field-id"
>
${_("Enter the reason why the students are to be manually enrolled or unenrolled.")}
${_("This cannot be left blank and will be recorded and presented in Enrollment Reports.")}
${_("Therefore, please given enough detail to account for this action.")}
</label>
<textarea
rows=
"2"
id=
"reason-field-id"
name=
"reason-field"
placeholder=
"${_('Reason')}"
spellcheck=
"false"
></textarea>
</p>
%endif
<div
class=
"enroll-option"
>
<input
type=
"checkbox"
name=
"auto-enroll"
value=
"Auto-Enroll"
checked=
"yes"
>
<label
for=
"auto-enroll"
>
${_("Auto Enroll")}
</label>
...
...
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