Skip to content

Commit

Permalink
Merge pull request openedx#23975 from edx/depr/shoppingcart-instructo…
Browse files Browse the repository at this point in the history
…r-report

instructor_task: remove shoppingcart (DEPR-43)
  • Loading branch information
feanil committed Jun 29, 2020
2 parents d6c1ec5 + 80ea0a6 commit cf5f948
Show file tree
Hide file tree
Showing 12 changed files with 1 addition and 1,171 deletions.
57 changes: 0 additions & 57 deletions lms/djangoapps/instructor/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,17 +144,6 @@
}
)

# ddt data for test cases involving executive summary report
EXECUTIVE_SUMMARY_DATA = (
{
'report_type': 'executive summary',
'task_type': 'exec_summary_report',
'instructor_api_endpoint': 'get_exec_summary_report',
'task_api_endpoint': 'lms.djangoapps.instructor_task.api.submit_executive_summary_report',
'extra_instructor_api_kwargs': {}
},
)


INSTRUCTOR_GET_ENDPOINTS = set([
'get_anon_ids',
Expand All @@ -167,7 +156,6 @@
'change_due_date',
'export_ora2_data',
'get_enrollment_report',
'get_exec_summary_report',
'get_grading_config',
'get_problem_responses',
'get_proctored_exam_results',
Expand Down Expand Up @@ -445,7 +433,6 @@ def setUp(self):
('get_students_features', {}),
('get_enrollment_report', {}),
('get_students_who_may_enroll', {}),
('get_exec_summary_report', {}),
('get_proctored_exam_results', {}),
('get_problem_responses', {}),
('export_ora2_data', {}),
Expand Down Expand Up @@ -2875,50 +2862,6 @@ def test_calculate_report_csv_success(self, report_type, instructor_api_endpoint
response = self.client.post(url, {})
self.assertContains(response, success_status)

@ddt.data(*EXECUTIVE_SUMMARY_DATA)
@ddt.unpack
def test_executive_summary_report_success(
self,
report_type,
task_type,
instructor_api_endpoint,
task_api_endpoint,
extra_instructor_api_kwargs
): # pylint: disable=unused-argument
kwargs = {'course_id': text_type(self.course.id)}
kwargs.update(extra_instructor_api_kwargs)
url = reverse(instructor_api_endpoint, kwargs=kwargs)

CourseFinanceAdminRole(self.course.id).add_users(self.instructor)
with patch(task_api_endpoint):
response = self.client.post(url, {})
success_status = u"The {report_type} report is being created." \
" To view the status of the report, see Pending" \
" Tasks below".format(report_type=report_type)
self.assertContains(response, success_status)

@ddt.data(*EXECUTIVE_SUMMARY_DATA)
@ddt.unpack
def test_executive_summary_report_already_running(
self,
report_type,
task_type,
instructor_api_endpoint,
task_api_endpoint,
extra_instructor_api_kwargs
):
kwargs = {'course_id': text_type(self.course.id)}
kwargs.update(extra_instructor_api_kwargs)
url = reverse(instructor_api_endpoint, kwargs=kwargs)

CourseFinanceAdminRole(self.course.id).add_users(self.instructor)
already_running_status = generate_already_running_error_message(task_type)
with patch(task_api_endpoint) as mock:
mock.side_effect = AlreadyRunningError(already_running_status)
response = self.client.post(url, {})

self.assertContains(response, already_running_status, status_code=400)

def test_get_ora2_responses_success(self):
url = reverse('export_ora2_data', kwargs={'course_id': text_type(self.course.id)})

Expand Down
21 changes: 1 addition & 20 deletions lms/djangoapps/instructor/views/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.response import Response
from rest_framework.views import APIView
from six import StringIO, text_type
from six import text_type
from six.moves import map, range
from submissions import api as sub_api # installed from the edx-submissions repository

Expand Down Expand Up @@ -1362,25 +1362,6 @@ def get_enrollment_report(request, course_id):
return JsonResponse({"status": success_status})


@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_exec_summary_report(request, course_id):
"""
get the executive summary report for the particular course.
"""
course_key = CourseKey.from_string(course_id)
report_type = _('executive summary')
task_api.submit_executive_summary_report(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
Expand Down
1 change: 0 additions & 1 deletion lms/djangoapps/instructor/views/api_urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@

# Reports..
url(r'^get_enrollment_report$', api.get_enrollment_report, name='get_enrollment_report'),
url(r'^get_exec_summary_report$', api.get_exec_summary_report, name='get_exec_summary_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'),

Expand Down
1 change: 0 additions & 1 deletion lms/djangoapps/instructor/views/instructor_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,6 @@ def _section_e_commerce(course, access, paid_mode, coupons_enabled, reports_enab
'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)}),
'exec_summary_report_url': reverse('get_exec_summary_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)}
Expand Down
15 changes: 0 additions & 15 deletions lms/djangoapps/instructor_task/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
course_survey_report_csv,
delete_problem_state,
enrollment_report_features_csv,
exec_summary_report_csv,
export_ora2_data,
generate_certificates,
override_problem_score,
Expand Down Expand Up @@ -406,20 +405,6 @@ def submit_calculate_may_enroll_csv(request, course_key, features):
return submit_task(request, task_type, task_class, course_key, task_input, task_key)


def submit_executive_summary_report(request, course_key):
"""
Submits a task to generate a HTML File containing the executive summary report.
Raises AlreadyRunningError if HTML File is already being updated.
"""
task_type = 'exec_summary_report'
task_class = exec_summary_report_csv
task_input = {}
task_key = ""

return submit_task(request, task_type, task_class, course_key, task_input, task_key)


def submit_course_survey_report(request, course_key):
"""
Submits a task to generate a HTML File containing the executive summary report.
Expand Down
1 change: 0 additions & 1 deletion lms/djangoapps/instructor_task/api_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ def generate_already_running_error_message(task_type):
'profile_info_csv': _('enrolled learner profile'),
'may_enroll_info_csv': _('enrollment'),
'detailed_enrollment_report': _('detailed enrollment'),
'exec_summary_report': _('executive summary'),
'course_survey_report': _('survey'),
'proctored_exam_results_report': _('proctored exam results'),
'export_ora2_data': _('ORA data'),
Expand Down
13 changes: 0 additions & 13 deletions lms/djangoapps/instructor_task/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
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_exec_summary_report,
upload_may_enroll_csv,
upload_students_csv
)
Expand Down Expand Up @@ -230,18 +229,6 @@ def enrollment_report_features_csv(entry_id, xmodule_instance_args):
return run_main_task(entry_id, task_fn, action_name)


@task(base=BaseInstructorTask)
def exec_summary_report_csv(entry_id, xmodule_instance_args):
"""
Compute executive summary report for a course and upload the
Html generated report to an S3 bucket for download.
"""
# Translators: This is a past-tense verb that is inserted into task progress messages as {action}.
action_name = 'generating_exec_summary_report'
task_fn = partial(upload_exec_summary_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):
"""
Expand Down
181 changes: 0 additions & 181 deletions lms/djangoapps/instructor_task/tasks_helper/enrollments.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,6 @@
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
from shoppingcart.models import (
CouponRedemption,
CourseRegCodeItem,
CourseRegistrationCode,
Invoice,
InvoiceTransaction,
PaidCourseRegistration,
RegistrationCodeRedemption
)
from student.models import CourseAccessRole, CourseEnrollment
from util.file import course_filename_prefix_generator

Expand Down Expand Up @@ -221,175 +212,3 @@ def upload_students_csv(_xmodule_instance_args, _entry_id, course_id, task_input
upload_csv_to_report_store(rows, 'student_profile_info', course_id, start_date)

return task_progress.update_task_state(extra_meta=current_step)


def get_executive_report(course_id):
"""
Returns dict containing information about the course executive summary.
"""
single_purchase_total = PaidCourseRegistration.get_total_amount_of_purchased_item(course_id)
bulk_purchase_total = CourseRegCodeItem.get_total_amount_of_purchased_item(course_id)
paid_invoices_total = InvoiceTransaction.get_total_amount_of_paid_course_invoices(course_id)
gross_paid_revenue = single_purchase_total + bulk_purchase_total + paid_invoices_total

all_invoices_total = Invoice.get_invoice_total_amount_for_course(course_id)
gross_pending_revenue = all_invoices_total - float(paid_invoices_total)

gross_revenue = float(gross_paid_revenue) + float(gross_pending_revenue)

refunded_self_purchased_seats = PaidCourseRegistration.get_self_purchased_seat_count(
course_id, status='refunded'
)
refunded_bulk_purchased_seats = CourseRegCodeItem.get_bulk_purchased_seat_count(
course_id, status='refunded'
)
total_seats_refunded = refunded_self_purchased_seats + refunded_bulk_purchased_seats

self_purchased_refunds = PaidCourseRegistration.get_total_amount_of_purchased_item(
course_id,
status='refunded'
)
bulk_purchase_refunds = CourseRegCodeItem.get_total_amount_of_purchased_item(course_id, status='refunded')
total_amount_refunded = self_purchased_refunds + bulk_purchase_refunds

top_discounted_codes = CouponRedemption.get_top_discount_codes_used(course_id)
total_coupon_codes_purchases = CouponRedemption.get_total_coupon_code_purchases(course_id)

bulk_purchased_codes = CourseRegistrationCode.order_generated_registration_codes(course_id)

unused_registration_codes = 0
for registration_code in bulk_purchased_codes:
if not RegistrationCodeRedemption.is_registration_code_redeemed(registration_code.code):
unused_registration_codes += 1

self_purchased_seat_count = PaidCourseRegistration.get_self_purchased_seat_count(course_id)
bulk_purchased_seat_count = CourseRegCodeItem.get_bulk_purchased_seat_count(course_id)
total_invoiced_seats = CourseRegistrationCode.invoice_generated_registration_codes(course_id).count()

total_seats = self_purchased_seat_count + bulk_purchased_seat_count + total_invoiced_seats

self_purchases_percentage = 0.0
bulk_purchases_percentage = 0.0
invoice_purchases_percentage = 0.0
avg_price_paid = 0.0

if total_seats != 0:
self_purchases_percentage = (float(self_purchased_seat_count) / float(total_seats)) * 100
bulk_purchases_percentage = (float(bulk_purchased_seat_count) / float(total_seats)) * 100
invoice_purchases_percentage = (float(total_invoiced_seats) / float(total_seats)) * 100
avg_price_paid = gross_revenue / total_seats

course = get_course_by_id(course_id, depth=0)
currency = settings.PAID_COURSE_REGISTRATION_CURRENCY[1]

return {
'display_name': course.display_name,
'start_date': course.start.strftime("%Y-%m-%d") if course.start is not None else 'N/A',
'end_date': course.end.strftime("%Y-%m-%d") if course.end is not None else 'N/A',
'total_seats': total_seats,
'currency': currency,
'gross_revenue': float(gross_revenue),
'gross_paid_revenue': float(gross_paid_revenue),
'gross_pending_revenue': gross_pending_revenue,
'total_seats_refunded': total_seats_refunded,
'total_amount_refunded': float(total_amount_refunded),
'average_paid_price': float(avg_price_paid),
'discount_codes_data': top_discounted_codes,
'total_seats_using_discount_codes': total_coupon_codes_purchases,
'total_self_purchase_seats': self_purchased_seat_count,
'total_bulk_purchase_seats': bulk_purchased_seat_count,
'total_invoiced_seats': total_invoiced_seats,
'unused_bulk_purchase_code_count': unused_registration_codes,
'self_purchases_percentage': self_purchases_percentage,
'bulk_purchases_percentage': bulk_purchases_percentage,
'invoice_purchases_percentage': invoice_purchases_percentage,
}


def upload_exec_summary_report(_xmodule_instance_args, _entry_id, course_id, _task_input, action_name):
"""
For a given `course_id`, generate a html report containing information,
which provides a snapshot of how the course is doing.
"""
start_time = time()
report_generation_date = datetime.now(UTC)
status_interval = 100

enrolled_users = CourseEnrollment.objects.users_enrolled_in(course_id)
true_enrollment_count = 0
for user in enrolled_users:
if not user.is_staff and not CourseAccessRole.objects.filter(
user=user, course_id=course_id, role__in=FILTERED_OUT_ROLES
).exists():
true_enrollment_count += 1

task_progress = TaskProgress(action_name, true_enrollment_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)
current_step = {'step': 'Gathering executive summary report information'}

TASK_LOG.info(
u'%s, Task type: %s, Current step: %s, generating executive summary report',
task_info_string,
action_name,
current_step
)

if task_progress.attempted % status_interval == 0:
task_progress.update_task_state(extra_meta=current_step)
task_progress.attempted += 1

# get the course executive summary report information.
data_dict = get_executive_report(course_id)
data_dict.update(
{
'total_enrollments': true_enrollment_count,
'report_generation_date': report_generation_date.strftime("%Y-%m-%d"),
}
)

# By this point, we've got the data that we need to generate html report.
current_step = {'step': 'Uploading executive summary report HTML file'}
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_exec_summary_to_store(data_dict, 'executive_report', course_id, report_generation_date)
task_progress.succeeded += 1
# One last update before we close out...
TASK_LOG.info(u'%s, Task type: %s, Finalizing executive summary report task', task_info_string, action_name)
return task_progress.update_task_state(extra_meta=current_step)


def _upload_exec_summary_to_store(data_dict, report_name, course_id, generated_at, config_name='FINANCIAL_REPORTS'):
"""
Upload Executive Summary Html file using ReportStore.
Arguments:
data_dict: containing executive report data.
report_name: Name of the resulting Html File.
course_id: ID of the course
"""
report_store = ReportStore.from_config(config_name)

# Use the data dict and html template to generate the output buffer
output_buffer = StringIO(render_to_string("instructor/instructor_dashboard_2/executive_summary.html", data_dict))

report_store.store(
course_id,
u"{course_prefix}_{report_name}_{timestamp_str}.html".format(
course_prefix=course_filename_prefix_generator(course_id),
report_name=report_name,
timestamp_str=generated_at.strftime("%Y-%m-%d-%H%M")
),
output_buffer,
)
tracker_emit(report_name)
Loading

0 comments on commit cf5f948

Please sign in to comment.