Remove final shoppingcart references from instructor.
This commit is contained in:
@@ -1,102 +0,0 @@
|
||||
"""
|
||||
Defines abstract class for the Enrollment Reports.
|
||||
"""
|
||||
|
||||
|
||||
import abc
|
||||
import collections
|
||||
import json
|
||||
|
||||
import six
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from student.models import UserProfile
|
||||
|
||||
|
||||
class AbstractEnrollmentReportProvider(six.with_metaclass(abc.ABCMeta, object)):
|
||||
"""
|
||||
Abstract interface for Detailed Enrollment Report Provider
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_enrollment_info(self, user, course_id):
|
||||
"""
|
||||
Returns the User Enrollment information.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_user_profile(self, user_id):
|
||||
"""
|
||||
Returns the UserProfile information.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_payment_info(self, user, course_id):
|
||||
"""
|
||||
Returns the User Payment information.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class BaseAbstractEnrollmentReportProvider(AbstractEnrollmentReportProvider):
|
||||
"""
|
||||
The base abstract class for all Enrollment Reports that can support multiple
|
||||
backend such as MySQL/Django-ORM.
|
||||
|
||||
# don't allow instantiation of this class, it must be subclassed
|
||||
"""
|
||||
def get_user_profile(self, user_id):
|
||||
"""
|
||||
Returns the UserProfile information.
|
||||
"""
|
||||
user_info = User.objects.select_related('profile').get(id=user_id)
|
||||
# extended user profile fields are stored in the user_profile meta column
|
||||
meta = {}
|
||||
if user_info.profile.meta:
|
||||
meta = json.loads(user_info.profile.meta)
|
||||
|
||||
user_data = collections.OrderedDict()
|
||||
user_data['User ID'] = user_info.id
|
||||
user_data['Username'] = user_info.username
|
||||
user_data['Email'] = user_info.email
|
||||
user_data['Full Name'] = user_info.profile.name
|
||||
user_data['First Name'] = meta.get('first-name', '')
|
||||
user_data['Last Name'] = meta.get('last-name', '')
|
||||
user_data['Company Name'] = meta.get('company', '')
|
||||
user_data['Title'] = meta.get('title', '')
|
||||
user_data['Language'] = user_info.profile.language
|
||||
user_data['Country'] = user_info.profile.country
|
||||
user_data['Year of Birth'] = user_info.profile.year_of_birth
|
||||
|
||||
user_data['Gender'] = None
|
||||
gender = user_info.profile.gender
|
||||
for _gender in UserProfile.GENDER_CHOICES:
|
||||
if gender == _gender[0]:
|
||||
user_data['Gender'] = _gender[1]
|
||||
break
|
||||
|
||||
user_data['Level of Education'] = None
|
||||
level_of_education = user_info.profile.level_of_education
|
||||
for _loe in UserProfile.LEVEL_OF_EDUCATION_CHOICES:
|
||||
if level_of_education == _loe[0]:
|
||||
user_data['Level of Education'] = _loe[1]
|
||||
|
||||
user_data['Mailing Address'] = user_info.profile.mailing_address
|
||||
user_data['Goals'] = user_info.profile.goals
|
||||
user_data['City'] = user_info.profile.city
|
||||
user_data['Country'] = user_info.profile.country
|
||||
return user_data
|
||||
|
||||
def get_enrollment_info(self, user, course_id):
|
||||
"""
|
||||
Returns the User Enrollment information.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_payment_info(self, user, course_id):
|
||||
"""
|
||||
Returns the User Payment information.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
@@ -1,196 +0,0 @@
|
||||
"""
|
||||
Defines concrete class for cybersource Enrollment Report.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
import collections
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from lms.djangoapps.courseware.access import has_access
|
||||
from lms.djangoapps.courseware.courses import get_course_by_id
|
||||
from lms.djangoapps.instructor.enrollment_report import BaseAbstractEnrollmentReportProvider
|
||||
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
|
||||
from shoppingcart.models import (
|
||||
CouponRedemption,
|
||||
InvoiceTransaction,
|
||||
OrderItem,
|
||||
PaidCourseRegistration,
|
||||
RegistrationCodeRedemption
|
||||
)
|
||||
from student.models import CourseEnrollment, ManualEnrollmentAudit
|
||||
|
||||
|
||||
class PaidCourseEnrollmentReportProvider(BaseAbstractEnrollmentReportProvider):
|
||||
"""
|
||||
The concrete class for all CyberSource Enrollment Reports.
|
||||
"""
|
||||
|
||||
def get_enrollment_info(self, user, course_id):
|
||||
"""
|
||||
Returns the User Enrollment information.
|
||||
"""
|
||||
course = get_course_by_id(course_id, depth=0)
|
||||
is_course_staff = bool(has_access(user, 'staff', course))
|
||||
manual_enrollment_reason = 'N/A'
|
||||
|
||||
# check the user enrollment role
|
||||
if user.is_staff:
|
||||
platform_name = configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME)
|
||||
enrollment_role = _(u'{platform_name} Staff').format(platform_name=platform_name)
|
||||
elif is_course_staff:
|
||||
enrollment_role = _('Course Staff')
|
||||
else:
|
||||
enrollment_role = _('Student')
|
||||
|
||||
course_enrollment = CourseEnrollment.get_enrollment(user=user, course_key=course_id)
|
||||
|
||||
if is_course_staff:
|
||||
enrollment_source = _('Staff')
|
||||
else:
|
||||
# get the registration_code_redemption object if exists
|
||||
registration_code_redemption = RegistrationCodeRedemption.registration_code_used_for_enrollment(
|
||||
course_enrollment)
|
||||
# get the paid_course registration item if exists
|
||||
paid_course_reg_item = PaidCourseRegistration.get_course_item_for_user_enrollment(
|
||||
user=user,
|
||||
course_id=course_id,
|
||||
course_enrollment=course_enrollment
|
||||
)
|
||||
|
||||
# from where the user get here
|
||||
if registration_code_redemption is not None:
|
||||
enrollment_source = _('Used Registration Code')
|
||||
elif paid_course_reg_item is not None:
|
||||
enrollment_source = _('Credit Card - Individual')
|
||||
else:
|
||||
manual_enrollment = ManualEnrollmentAudit.get_manual_enrollment(course_enrollment)
|
||||
if manual_enrollment is not None:
|
||||
enrollment_source = _(
|
||||
u'manually enrolled by username: {username}'
|
||||
).format(username=manual_enrollment.enrolled_by.username)
|
||||
|
||||
manual_enrollment_reason = manual_enrollment.reason
|
||||
else:
|
||||
enrollment_source = _('Manually Enrolled')
|
||||
|
||||
enrollment_date = course_enrollment.created.strftime(u"%B %d, %Y")
|
||||
currently_enrolled = course_enrollment.is_active
|
||||
|
||||
course_enrollment_data = collections.OrderedDict()
|
||||
course_enrollment_data['Enrollment Date'] = enrollment_date
|
||||
course_enrollment_data['Currently Enrolled'] = currently_enrolled
|
||||
course_enrollment_data['Enrollment Source'] = enrollment_source
|
||||
course_enrollment_data['Manual (Un)Enrollment Reason'] = manual_enrollment_reason
|
||||
course_enrollment_data['Enrollment Role'] = enrollment_role
|
||||
return course_enrollment_data
|
||||
|
||||
def get_payment_info(self, user, course_id):
|
||||
"""
|
||||
Returns the User Payment information.
|
||||
"""
|
||||
course_enrollment = CourseEnrollment.get_enrollment(user=user, course_key=course_id)
|
||||
paid_course_reg_item = PaidCourseRegistration.get_course_item_for_user_enrollment(
|
||||
user=user,
|
||||
course_id=course_id,
|
||||
course_enrollment=course_enrollment
|
||||
)
|
||||
payment_data = collections.OrderedDict()
|
||||
# check if the user made a single self purchase scenario
|
||||
# for enrollment in the course.
|
||||
if paid_course_reg_item is not None:
|
||||
coupon_redemption = CouponRedemption.objects.select_related('coupon').filter(
|
||||
order_id=paid_course_reg_item.order_id)
|
||||
coupon_codes = [redemption.coupon.code for redemption in coupon_redemption]
|
||||
coupon_codes = ", ".join(coupon_codes)
|
||||
registration_code_used = 'N/A'
|
||||
|
||||
list_price = paid_course_reg_item.get_list_price()
|
||||
payment_amount = paid_course_reg_item.unit_cost
|
||||
coupon_codes_used = coupon_codes
|
||||
payment_status = paid_course_reg_item.status
|
||||
transaction_reference_number = paid_course_reg_item.order_id
|
||||
|
||||
else:
|
||||
# check if the user used a registration code for the enrollment.
|
||||
registration_code_redemption = RegistrationCodeRedemption.registration_code_used_for_enrollment(
|
||||
course_enrollment)
|
||||
if registration_code_redemption is not None:
|
||||
registration_code = registration_code_redemption.registration_code
|
||||
registration_code_used = registration_code.code
|
||||
if registration_code.invoice_item_id:
|
||||
list_price, payment_amount, payment_status, transaction_reference_number =\
|
||||
self._get_invoice_data(registration_code_redemption)
|
||||
coupon_codes_used = 'N/A'
|
||||
|
||||
elif registration_code_redemption.registration_code.order_id:
|
||||
list_price, payment_amount, coupon_codes_used, payment_status, transaction_reference_number = \
|
||||
self._get_order_data(registration_code_redemption, course_id)
|
||||
|
||||
else:
|
||||
# this happens when the registration code is not created via invoice or bulk purchase
|
||||
# scenario.
|
||||
list_price = 'N/A'
|
||||
payment_amount = 'N/A'
|
||||
coupon_codes_used = 'N/A'
|
||||
registration_code_used = 'N/A'
|
||||
payment_status = _('Data Integrity Error')
|
||||
transaction_reference_number = 'N/A'
|
||||
else:
|
||||
list_price = 'N/A'
|
||||
payment_amount = 'N/A'
|
||||
coupon_codes_used = 'N/A'
|
||||
registration_code_used = 'N/A'
|
||||
payment_status = _('TBD')
|
||||
transaction_reference_number = 'N/A'
|
||||
|
||||
payment_data['List Price'] = list_price
|
||||
payment_data['Payment Amount'] = payment_amount
|
||||
payment_data['Coupon Codes Used'] = coupon_codes_used
|
||||
payment_data['Registration Code Used'] = registration_code_used
|
||||
payment_data['Payment Status'] = payment_status
|
||||
payment_data['Transaction Reference Number'] = transaction_reference_number
|
||||
return payment_data
|
||||
|
||||
def _get_order_data(self, registration_code_redemption, course_id):
|
||||
"""
|
||||
Returns the order data
|
||||
"""
|
||||
order_item = OrderItem.objects.get(order=registration_code_redemption.registration_code.order,
|
||||
courseregcodeitem__course_id=course_id)
|
||||
coupon_redemption = CouponRedemption.objects.select_related('coupon').filter(
|
||||
order_id=registration_code_redemption.registration_code.order)
|
||||
coupon_codes = [redemption.coupon.code for redemption in coupon_redemption]
|
||||
coupon_codes = ", ".join(coupon_codes)
|
||||
|
||||
list_price = order_item.get_list_price()
|
||||
payment_amount = order_item.unit_cost
|
||||
coupon_codes_used = coupon_codes
|
||||
payment_status = order_item.status
|
||||
transaction_reference_number = order_item.order_id
|
||||
return list_price, payment_amount, coupon_codes_used, payment_status, transaction_reference_number
|
||||
|
||||
def _get_invoice_data(self, registration_code_redemption):
|
||||
"""
|
||||
Returns the Invoice data
|
||||
"""
|
||||
registration_code = registration_code_redemption.registration_code
|
||||
list_price = registration_code.invoice_item.unit_price
|
||||
total_amount = registration_code_redemption.registration_code.invoice.total_amount
|
||||
qty = registration_code_redemption.registration_code.invoice_item.qty
|
||||
payment_amount = total_amount / qty
|
||||
invoice_transaction = InvoiceTransaction.get_invoice_transaction(
|
||||
invoice_id=registration_code_redemption.registration_code.invoice.id)
|
||||
if invoice_transaction is not None:
|
||||
# amount greater than 0 is invoice has bee paid
|
||||
if invoice_transaction.amount > 0:
|
||||
payment_status = 'Invoice Paid'
|
||||
else:
|
||||
# amount less than 0 is invoice has been refunded
|
||||
payment_status = 'Refunded'
|
||||
else:
|
||||
payment_status = 'Invoice Outstanding'
|
||||
transaction_reference_number = registration_code_redemption.registration_code.invoice_id
|
||||
return list_price, payment_amount, payment_status, transaction_reference_number
|
||||
@@ -118,12 +118,6 @@ REPORTS_DATA = (
|
||||
'task_api_endpoint': 'lms.djangoapps.instructor_task.api.submit_calculate_students_features_csv',
|
||||
'extra_instructor_api_kwargs': {'csv': '/csv'}
|
||||
},
|
||||
{
|
||||
'report_type': 'detailed enrollment',
|
||||
'instructor_api_endpoint': 'get_enrollment_report',
|
||||
'task_api_endpoint': 'lms.djangoapps.instructor_task.api.submit_detailed_enrollment_features_csv',
|
||||
'extra_instructor_api_kwargs': {}
|
||||
},
|
||||
{
|
||||
'report_type': 'enrollment',
|
||||
'instructor_api_endpoint': 'get_students_who_may_enroll',
|
||||
@@ -155,7 +149,6 @@ INSTRUCTOR_POST_ENDPOINTS = set([
|
||||
'calculate_grades_csv',
|
||||
'change_due_date',
|
||||
'export_ora2_data',
|
||||
'get_enrollment_report',
|
||||
'get_grading_config',
|
||||
'get_problem_responses',
|
||||
'get_proctored_exam_results',
|
||||
@@ -431,7 +424,6 @@ class TestInstructorAPIDenyLevels(SharedModuleStoreTestCase, LoginEnrollmentTest
|
||||
('list_report_downloads', {}),
|
||||
('calculate_grades_csv', {}),
|
||||
('get_students_features', {}),
|
||||
('get_enrollment_report', {}),
|
||||
('get_students_who_may_enroll', {}),
|
||||
('get_proctored_exam_results', {}),
|
||||
('get_problem_responses', {}),
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
"""
|
||||
Exercises tests on the base_store_provider file
|
||||
"""
|
||||
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from lms.djangoapps.instructor.enrollment_report import AbstractEnrollmentReportProvider
|
||||
from lms.djangoapps.instructor.paidcourse_enrollment_report import PaidCourseEnrollmentReportProvider
|
||||
|
||||
|
||||
class BadImplementationAbstractEnrollmentReportProvider(AbstractEnrollmentReportProvider):
|
||||
"""
|
||||
Test implementation of EnrollmentProvider to assert that non-implementations of methods
|
||||
raises the correct methods
|
||||
"""
|
||||
|
||||
def get_user_profile(self, user_id):
|
||||
"""
|
||||
Fake implementation of method which calls base class, which should throw NotImplementedError
|
||||
"""
|
||||
super(BadImplementationAbstractEnrollmentReportProvider, self).get_user_profile(user_id)
|
||||
|
||||
def get_enrollment_info(self, user, course_id):
|
||||
"""
|
||||
Fake implementation of method which calls base class, which should throw NotImplementedError
|
||||
"""
|
||||
super(BadImplementationAbstractEnrollmentReportProvider, self).get_enrollment_info(user, course_id)
|
||||
|
||||
def get_payment_info(self, user, course_id):
|
||||
"""
|
||||
Fake implementation of method which calls base class, which should throw NotImplementedError
|
||||
"""
|
||||
super(BadImplementationAbstractEnrollmentReportProvider, self).get_payment_info(user, course_id)
|
||||
|
||||
|
||||
class TestBaseNotificationDataProvider(TestCase):
|
||||
"""
|
||||
Cover the EnrollmentReportProvider class
|
||||
"""
|
||||
|
||||
def test_cannot_create_instance(self):
|
||||
"""
|
||||
EnrollmentReportProvider is an abstract class and we should not be able
|
||||
to create an instance of it
|
||||
"""
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
# parent of the BaseEnrollmentReportProvider is EnrollmentReportProvider
|
||||
super(BadImplementationAbstractEnrollmentReportProvider, self)
|
||||
|
||||
def test_get_provider(self):
|
||||
"""
|
||||
Makes sure we get an instance of the registered enrollment provider
|
||||
"""
|
||||
|
||||
provider = PaidCourseEnrollmentReportProvider()
|
||||
|
||||
self.assertIsNotNone(provider)
|
||||
self.assertTrue(isinstance(provider, PaidCourseEnrollmentReportProvider))
|
||||
|
||||
def test_base_methods_exceptions(self):
|
||||
"""
|
||||
Asserts that all base-methods on the EnrollmentProvider interface will throw
|
||||
an NotImplementedError
|
||||
"""
|
||||
|
||||
bad_provider = BadImplementationAbstractEnrollmentReportProvider()
|
||||
|
||||
with self.assertRaises(NotImplementedError):
|
||||
bad_provider.get_enrollment_info(None, None)
|
||||
|
||||
with self.assertRaises(NotImplementedError):
|
||||
bad_provider.get_payment_info(None, None)
|
||||
|
||||
with self.assertRaises(NotImplementedError):
|
||||
bad_provider.get_user_profile(None)
|
||||
@@ -1312,25 +1312,6 @@ class CohortCSV(DeveloperErrorViewMixin, APIView):
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
@transaction.non_atomic_requests
|
||||
@require_POST
|
||||
@ensure_csrf_cookie
|
||||
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
|
||||
@require_course_permission(permissions.ENROLLMENT_REPORT)
|
||||
@require_finance_admin
|
||||
@common_exceptions_400
|
||||
def get_enrollment_report(request, course_id):
|
||||
"""
|
||||
get the enrollment report for the particular course.
|
||||
"""
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
report_type = _('detailed enrollment')
|
||||
task_api.submit_detailed_enrollment_features_csv(request, course_key)
|
||||
success_status = SUCCESS_MESSAGE_TEMPLATE.format(report_type=report_type)
|
||||
|
||||
return JsonResponse({"status": success_status})
|
||||
|
||||
|
||||
@transaction.non_atomic_requests
|
||||
@require_POST
|
||||
@ensure_csrf_cookie
|
||||
|
||||
@@ -51,7 +51,6 @@ urlpatterns = [
|
||||
url(r'^problem_grade_report$', api.problem_grade_report, name='problem_grade_report'),
|
||||
|
||||
# Reports..
|
||||
url(r'^get_enrollment_report$', api.get_enrollment_report, name='get_enrollment_report'),
|
||||
url(r'^get_course_survey_results$', api.get_course_survey_results, name='get_course_survey_results'),
|
||||
url(r'^export_ora2_data', api.export_ora2_data, name='export_ora2_data'),
|
||||
|
||||
|
||||
@@ -55,7 +55,6 @@ from openedx.core.djangoapps.verified_track_content.models import VerifiedTrackC
|
||||
from openedx.core.djangolib.markup import HTML, Text
|
||||
from openedx.core.lib.url_utils import quote_slashes
|
||||
from openedx.core.lib.xblock_utils import wrap_xblock
|
||||
from shoppingcart.models import Coupon, CourseRegCodeItem, PaidCourseRegistration
|
||||
from student.models import CourseEnrollment
|
||||
from student.roles import (
|
||||
CourseFinanceAdminRole, CourseInstructorRole,
|
||||
@@ -182,10 +181,6 @@ def instructor_dashboard_2(request, course_id):
|
||||
if is_bulk_email_feature_enabled(course_key) and (access['staff'] or access['instructor']):
|
||||
sections.append(_section_send_email(course, access))
|
||||
|
||||
# Gate access to Ecommerce tab
|
||||
if course_mode_has_price and (access['finance_admin'] or access['sales_admin']):
|
||||
sections.append(_section_e_commerce(course, access, paid_modes[0], is_white_label, reports_enabled))
|
||||
|
||||
# Gate access to Special Exam tab depending if either timed exams or proctored exams
|
||||
# are enabled in the course
|
||||
|
||||
@@ -280,74 +275,6 @@ def instructor_dashboard_2(request, course_id):
|
||||
## section_key will be used as a css attribute, javascript tie-in, and template import filename.
|
||||
## section_display_name will be used to generate link titles in the nav bar.
|
||||
|
||||
|
||||
def _section_e_commerce(course, access, paid_mode, coupons_enabled, reports_enabled):
|
||||
""" Provide data for the corresponding dashboard section """
|
||||
course_key = course.id
|
||||
coupons = Coupon.objects.filter(course_id=course_key).order_by('-is_active')
|
||||
course_price = paid_mode.min_price
|
||||
|
||||
total_amount = None
|
||||
if access['finance_admin']:
|
||||
single_purchase_total = PaidCourseRegistration.get_total_amount_of_purchased_item(course_key)
|
||||
bulk_purchase_total = CourseRegCodeItem.get_total_amount_of_purchased_item(course_key)
|
||||
total_amount = single_purchase_total + bulk_purchase_total
|
||||
|
||||
section_data = {
|
||||
'section_key': 'e-commerce',
|
||||
'section_display_name': _('E-Commerce'),
|
||||
'access': access,
|
||||
'course_id': six.text_type(course_key),
|
||||
'currency_symbol': settings.PAID_COURSE_REGISTRATION_CURRENCY[1],
|
||||
'ajax_remove_coupon_url': reverse('remove_coupon', kwargs={'course_id': six.text_type(course_key)}),
|
||||
'ajax_get_coupon_info': reverse('get_coupon_info', kwargs={'course_id': six.text_type(course_key)}),
|
||||
'get_user_invoice_preference_url': reverse(
|
||||
'get_user_invoice_preference',
|
||||
kwargs={'course_id': six.text_type(course_key)}
|
||||
),
|
||||
'sale_validation_url': reverse('sale_validation', kwargs={'course_id': six.text_type(course_key)}),
|
||||
'ajax_update_coupon': reverse('update_coupon', kwargs={'course_id': six.text_type(course_key)}),
|
||||
'ajax_add_coupon': reverse('add_coupon', kwargs={'course_id': six.text_type(course_key)}),
|
||||
'instructor_url': reverse('instructor_dashboard', kwargs={'course_id': six.text_type(course_key)}),
|
||||
'get_registration_code_csv_url': reverse(
|
||||
'get_registration_codes',
|
||||
kwargs={'course_id': six.text_type(course_key)}
|
||||
),
|
||||
'generate_registration_code_csv_url': reverse(
|
||||
'generate_registration_codes',
|
||||
kwargs={'course_id': six.text_type(course_key)}
|
||||
),
|
||||
'active_registration_code_csv_url': reverse(
|
||||
'active_registration_codes',
|
||||
kwargs={'course_id': six.text_type(course_key)}
|
||||
),
|
||||
'spent_registration_code_csv_url': reverse(
|
||||
'spent_registration_codes',
|
||||
kwargs={'course_id': six.text_type(course_key)}
|
||||
),
|
||||
'set_course_mode_url': reverse('set_course_mode_price', kwargs={'course_id': six.text_type(course_key)}),
|
||||
'download_coupon_codes_url': reverse('get_coupon_codes', kwargs={'course_id': six.text_type(course_key)}),
|
||||
'enrollment_report_url': reverse('get_enrollment_report', kwargs={'course_id': six.text_type(course_key)}),
|
||||
'list_financial_report_downloads_url': reverse(
|
||||
'list_financial_report_downloads',
|
||||
kwargs={'course_id': six.text_type(course_key)}
|
||||
),
|
||||
'list_instructor_tasks_url': reverse('list_instructor_tasks', kwargs={'course_id': six.text_type(course_key)}),
|
||||
'look_up_registration_code': reverse(
|
||||
'look_up_registration_code',
|
||||
kwargs={'course_id': six.text_type(course_key)}
|
||||
),
|
||||
'coupons': coupons,
|
||||
'sales_admin': access['sales_admin'],
|
||||
'coupons_enabled': coupons_enabled,
|
||||
'reports_enabled': reports_enabled,
|
||||
'course_price': course_price,
|
||||
'total_amount': total_amount,
|
||||
'is_ecommerce_course': is_ecommerce_course(course_key)
|
||||
}
|
||||
return section_data
|
||||
|
||||
|
||||
def _section_special_exams(course, access):
|
||||
""" Provide data for the corresponding dashboard section """
|
||||
course_key = six.text_type(course.id)
|
||||
|
||||
@@ -34,7 +34,6 @@ from lms.djangoapps.instructor_task.tasks import (
|
||||
cohort_students,
|
||||
course_survey_report_csv,
|
||||
delete_problem_state,
|
||||
enrollment_report_features_csv,
|
||||
export_ora2_data,
|
||||
generate_certificates,
|
||||
override_problem_score,
|
||||
@@ -376,20 +375,6 @@ def submit_calculate_students_features_csv(request, course_key, features):
|
||||
return submit_task(request, task_type, task_class, course_key, task_input, task_key)
|
||||
|
||||
|
||||
def submit_detailed_enrollment_features_csv(request, course_key):
|
||||
"""
|
||||
Submits a task to generate a CSV containing detailed enrollment info.
|
||||
|
||||
Raises AlreadyRunningError if said CSV is already being updated.
|
||||
"""
|
||||
task_type = 'detailed_enrollment_report'
|
||||
task_class = enrollment_report_features_csv
|
||||
task_input = {}
|
||||
task_key = ""
|
||||
|
||||
return submit_task(request, task_type, task_class, course_key, task_input, task_key)
|
||||
|
||||
|
||||
def submit_calculate_may_enroll_csv(request, course_key, features):
|
||||
"""
|
||||
Submits a task to generate a CSV file containing information about
|
||||
|
||||
@@ -32,7 +32,6 @@ from bulk_email.tasks import perform_delegate_email_batches
|
||||
from lms.djangoapps.instructor_task.tasks_base import BaseInstructorTask
|
||||
from lms.djangoapps.instructor_task.tasks_helper.certs import generate_students_certificates
|
||||
from lms.djangoapps.instructor_task.tasks_helper.enrollments import (
|
||||
upload_enrollment_report,
|
||||
upload_may_enroll_csv,
|
||||
upload_students_csv
|
||||
)
|
||||
@@ -217,18 +216,6 @@ def calculate_students_features_csv(entry_id, xmodule_instance_args):
|
||||
return run_main_task(entry_id, task_fn, action_name)
|
||||
|
||||
|
||||
@task(base=BaseInstructorTask)
|
||||
def enrollment_report_features_csv(entry_id, xmodule_instance_args):
|
||||
"""
|
||||
Compute student profile information for a course and upload the
|
||||
CSV to an S3 bucket for download.
|
||||
"""
|
||||
# Translators: This is a past-tense verb that is inserted into task progress messages as {action}.
|
||||
action_name = ugettext_noop('generating_enrollment_report')
|
||||
task_fn = partial(upload_enrollment_report, xmodule_instance_args)
|
||||
return run_main_task(entry_id, task_fn, action_name)
|
||||
|
||||
|
||||
@task(base=BaseInstructorTask)
|
||||
def course_survey_report_csv(entry_id, xmodule_instance_args):
|
||||
"""
|
||||
|
||||
@@ -14,7 +14,6 @@ from six import StringIO
|
||||
|
||||
from edxmako.shortcuts import render_to_string
|
||||
from lms.djangoapps.courseware.courses import get_course_by_id
|
||||
from lms.djangoapps.instructor.paidcourse_enrollment_report import PaidCourseEnrollmentReportProvider
|
||||
from lms.djangoapps.instructor_analytics.basic import enrolled_students_features, list_may_enroll
|
||||
from lms.djangoapps.instructor_analytics.csvs import format_dictlist
|
||||
from lms.djangoapps.instructor_task.models import ReportStore
|
||||
@@ -28,127 +27,6 @@ TASK_LOG = logging.getLogger('edx.celery.task')
|
||||
FILTERED_OUT_ROLES = ['staff', 'instructor', 'finance_admin', 'sales_admin']
|
||||
|
||||
|
||||
def upload_enrollment_report(_xmodule_instance_args, _entry_id, course_id, _task_input, action_name):
|
||||
"""
|
||||
For a given `course_id`, generate a CSV file containing profile
|
||||
information for all students that are enrolled, and store using a
|
||||
`ReportStore`.
|
||||
"""
|
||||
start_time = time()
|
||||
start_date = datetime.now(UTC)
|
||||
status_interval = 100
|
||||
students_in_course = CourseEnrollment.objects.enrolled_and_dropped_out_users(course_id)
|
||||
task_progress = TaskProgress(action_name, students_in_course.count(), start_time)
|
||||
|
||||
fmt = u'Task: {task_id}, InstructorTask ID: {entry_id}, Course: {course_id}, Input: {task_input}'
|
||||
task_info_string = fmt.format(
|
||||
task_id=_xmodule_instance_args.get('task_id') if _xmodule_instance_args is not None else None,
|
||||
entry_id=_entry_id,
|
||||
course_id=course_id,
|
||||
task_input=_task_input
|
||||
)
|
||||
TASK_LOG.info(u'%s, Task type: %s, Starting task execution', task_info_string, action_name)
|
||||
|
||||
# Loop over all our students and build our CSV lists in memory
|
||||
rows = []
|
||||
header = None
|
||||
current_step = {'step': 'Gathering Profile Information'}
|
||||
enrollment_report_provider = PaidCourseEnrollmentReportProvider()
|
||||
total_students = students_in_course.count()
|
||||
student_counter = 0
|
||||
TASK_LOG.info(
|
||||
u'%s, Task type: %s, Current step: %s, generating detailed enrollment report for total students: %s',
|
||||
task_info_string,
|
||||
action_name,
|
||||
current_step,
|
||||
total_students
|
||||
)
|
||||
|
||||
for student in students_in_course:
|
||||
# Periodically update task status (this is a cache write)
|
||||
if task_progress.attempted % status_interval == 0:
|
||||
task_progress.update_task_state(extra_meta=current_step)
|
||||
task_progress.attempted += 1
|
||||
|
||||
# Now add a log entry after certain intervals to get a hint that task is in progress
|
||||
student_counter += 1
|
||||
if student_counter % 100 == 0:
|
||||
TASK_LOG.info(
|
||||
u'%s, Task type: %s, Current step: %s, gathering enrollment profile for students in progress: %s/%s',
|
||||
task_info_string,
|
||||
action_name,
|
||||
current_step,
|
||||
student_counter,
|
||||
total_students
|
||||
)
|
||||
|
||||
user_data = enrollment_report_provider.get_user_profile(student.id)
|
||||
course_enrollment_data = enrollment_report_provider.get_enrollment_info(student, course_id)
|
||||
payment_data = enrollment_report_provider.get_payment_info(student, course_id)
|
||||
|
||||
# display name map for the column headers
|
||||
enrollment_report_headers = {
|
||||
'User ID': _('User ID'),
|
||||
'Username': _('Username'),
|
||||
'Full Name': _('Full Name'),
|
||||
'First Name': _('First Name'),
|
||||
'Last Name': _('Last Name'),
|
||||
'Company Name': _('Company Name'),
|
||||
'Title': _('Title'),
|
||||
'Language': _('Language'),
|
||||
'Year of Birth': _('Year of Birth'),
|
||||
'Gender': _('Gender'),
|
||||
'Level of Education': _('Level of Education'),
|
||||
'Mailing Address': _('Mailing Address'),
|
||||
'Goals': _('Goals'),
|
||||
'City': _('City'),
|
||||
'Country': _('Country'),
|
||||
'Enrollment Date': _('Enrollment Date'),
|
||||
'Currently Enrolled': _('Currently Enrolled'),
|
||||
'Enrollment Source': _('Enrollment Source'),
|
||||
'Manual (Un)Enrollment Reason': _('Manual (Un)Enrollment Reason'),
|
||||
'Enrollment Role': _('Enrollment Role'),
|
||||
'List Price': _('List Price'),
|
||||
'Payment Amount': _('Payment Amount'),
|
||||
'Coupon Codes Used': _('Coupon Codes Used'),
|
||||
'Registration Code Used': _('Registration Code Used'),
|
||||
'Payment Status': _('Payment Status'),
|
||||
'Transaction Reference Number': _('Transaction Reference Number')
|
||||
}
|
||||
|
||||
if not header:
|
||||
header = list(user_data.keys()) + list(course_enrollment_data.keys()) + list(payment_data.keys())
|
||||
display_headers = []
|
||||
for header_element in header:
|
||||
# translate header into a localizable display string
|
||||
display_headers.append(enrollment_report_headers.get(header_element, header_element))
|
||||
rows.append(display_headers)
|
||||
|
||||
rows.append(list(user_data.values()) + list(course_enrollment_data.values()) + list(payment_data.values()))
|
||||
task_progress.succeeded += 1
|
||||
|
||||
TASK_LOG.info(
|
||||
u'%s, Task type: %s, Current step: %s, Detailed enrollment report generated for students: %s/%s',
|
||||
task_info_string,
|
||||
action_name,
|
||||
current_step,
|
||||
student_counter,
|
||||
total_students
|
||||
)
|
||||
|
||||
# By this point, we've got the rows we're going to stuff into our CSV files.
|
||||
current_step = {'step': 'Uploading CSVs'}
|
||||
task_progress.update_task_state(extra_meta=current_step)
|
||||
TASK_LOG.info(u'%s, Task type: %s, Current step: %s', task_info_string, action_name, current_step)
|
||||
|
||||
# Perform the actual upload
|
||||
upload_csv_to_report_store(rows, 'enrollment_report', course_id, start_date, config_name='FINANCIAL_REPORTS')
|
||||
|
||||
# One last update before we close out...
|
||||
TASK_LOG.info(u'%s, Task type: %s, Finalizing detailed enrollment task', task_info_string, action_name)
|
||||
return task_progress.update_task_state(extra_meta=current_step)
|
||||
|
||||
|
||||
def upload_may_enroll_csv(_xmodule_instance_args, _entry_id, course_id, task_input, action_name):
|
||||
"""
|
||||
For a given `course_id`, generate a CSV file containing
|
||||
|
||||
@@ -26,7 +26,6 @@ from lms.djangoapps.instructor_task.api import (
|
||||
submit_course_survey_report,
|
||||
submit_delete_entrance_exam_state_for_student,
|
||||
submit_delete_problem_state_for_all_students,
|
||||
submit_detailed_enrollment_features_csv,
|
||||
submit_export_ora2_data,
|
||||
submit_override_score,
|
||||
submit_rescore_entrance_exam_for_student,
|
||||
@@ -251,11 +250,6 @@ class InstructorTaskCourseSubmitTest(TestReportMixin, InstructorTaskCourseTestCa
|
||||
)
|
||||
self._test_resubmission(api_call)
|
||||
|
||||
def test_submit_enrollment_report_features_csv(self):
|
||||
api_call = lambda: submit_detailed_enrollment_features_csv(self.create_task_request(self.instructor),
|
||||
self.course.id)
|
||||
self._test_resubmission(api_call)
|
||||
|
||||
def test_submit_course_survey_report(self):
|
||||
api_call = lambda: submit_course_survey_report(
|
||||
self.create_task_request(self.instructor), self.course.id
|
||||
|
||||
@@ -43,7 +43,6 @@ from lms.djangoapps.grades.transformer import GradesTransformer
|
||||
from lms.djangoapps.instructor_analytics.basic import UNAVAILABLE, list_problem_responses
|
||||
from lms.djangoapps.instructor_task.tasks_helper.certs import generate_students_certificates
|
||||
from lms.djangoapps.instructor_task.tasks_helper.enrollments import (
|
||||
upload_enrollment_report,
|
||||
upload_may_enroll_csv,
|
||||
upload_students_csv
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user