Commit d3277c99 by Julia Hansbrough

Initial PR for verified cert reporting

Refactored from code in Stanford's itemized revenue reports into a new Report class used for all revenue-related reporting.

Models for all cert types complete; test coverage for half of cert types complete.

If this architecture is deemed solid, the next steps are to add a reasonable UI folks will use to select reports to download, allow them to restrict based on dates/universities, and of course complete test coverage.
parent 1c979090
...@@ -14,7 +14,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase ...@@ -14,7 +14,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.tests.factories import CourseFactory
from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE
from shoppingcart.views import _can_download_report, _get_date_from_str from shoppingcart.views import _can_download_report, _get_date_from_str
from shoppingcart.models import Order, CertificateItem, PaidCourseRegistration, OrderItem from shoppingcart.models import Order, CertificateItem, PaidCourseRegistration, OrderItem, Report
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
from student.models import CourseEnrollment from student.models import CourseEnrollment
from course_modes.models import CourseMode from course_modes.models import CourseMode
...@@ -379,6 +379,9 @@ class CSVReportViewsTest(ModuleStoreTestCase): ...@@ -379,6 +379,9 @@ class CSVReportViewsTest(ModuleStoreTestCase):
CORRECT_CSV_NO_DATE = ",1,purchased,1,40,40,usd,Registration for Course: Robot Super Course," CORRECT_CSV_NO_DATE = ",1,purchased,1,40,40,usd,Registration for Course: Robot Super Course,"
def test_report_csv(self): def test_report_csv(self):
# TODO test multiple types
report_type = "itemized_purchase_report"
PaidCourseRegistration.add_to_order(self.cart, self.course_id) PaidCourseRegistration.add_to_order(self.cart, self.course_id)
self.cart.purchase() self.cart.purchase()
self.login_user() self.login_user()
...@@ -386,7 +389,8 @@ class CSVReportViewsTest(ModuleStoreTestCase): ...@@ -386,7 +389,8 @@ class CSVReportViewsTest(ModuleStoreTestCase):
response = self.client.post(reverse('payment_csv_report'), {'start_date': '1970-01-01', response = self.client.post(reverse('payment_csv_report'), {'start_date': '1970-01-01',
'end_date': '2100-01-01'}) 'end_date': '2100-01-01'})
self.assertEqual(response['Content-Type'], 'text/csv') self.assertEqual(response['Content-Type'], 'text/csv')
self.assertIn(",".join(OrderItem.csv_report_header_row()), response.content) report = Report.initialize_report(report_type)
self.assertIn(",".join(report.csv_report_header_row()), response.content)
self.assertIn(self.CORRECT_CSV_NO_DATE, response.content) self.assertIn(self.CORRECT_CSV_NO_DATE, response.content)
......
...@@ -11,8 +11,8 @@ from django.core.urlresolvers import reverse ...@@ -11,8 +11,8 @@ from django.core.urlresolvers import reverse
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from edxmako.shortcuts import render_to_response from edxmako.shortcuts import render_to_response
from .models import Order, PaidCourseRegistration, OrderItem
from student.models import CourseEnrollment from student.models import CourseEnrollment
from .models import Order, PaidCourseRegistration, OrderItem, Report
from .processors import process_postpay_callback, render_purchase_form_html from .processors import process_postpay_callback, render_purchase_form_html
from .exceptions import ItemAlreadyInCartException, AlreadyEnrolledInCourseException, CourseDoesNotExistException from .exceptions import ItemAlreadyInCartException, AlreadyEnrolledInCourseException, CourseDoesNotExistException
...@@ -174,6 +174,9 @@ def csv_report(request): ...@@ -174,6 +174,9 @@ def csv_report(request):
""" """
Downloads csv reporting of orderitems Downloads csv reporting of orderitems
""" """
# TODO: change this to something modular later
report_type = "itemized_purchase_report"
if not _can_download_report(request.user): if not _can_download_report(request.user):
return HttpResponseForbidden(_('You do not have permission to view this page.')) return HttpResponseForbidden(_('You do not have permission to view this page.'))
...@@ -187,7 +190,8 @@ def csv_report(request): ...@@ -187,7 +190,8 @@ def csv_report(request):
# Error case: there was a badly formatted user-input date string # Error case: there was a badly formatted user-input date string
return _render_report_form(start_str, end_str, date_fmt_error=True) return _render_report_form(start_str, end_str, date_fmt_error=True)
items = OrderItem.purchased_items_btw_dates(start_date, end_date) report = Report.initialize_report(report_type)
items = report.get_query(start_date, end_date)
if items.count() > settings.PAYMENT_REPORT_MAX_ITEMS: if items.count() > settings.PAYMENT_REPORT_MAX_ITEMS:
# Error case: too many items would be generated in the report and we're at risk of timeout # Error case: too many items would be generated in the report and we're at risk of timeout
return _render_report_form(start_str, end_str, total_count_error=True) return _render_report_form(start_str, end_str, total_count_error=True)
...@@ -195,7 +199,8 @@ def csv_report(request): ...@@ -195,7 +199,8 @@ def csv_report(request):
response = HttpResponse(mimetype='text/csv') response = HttpResponse(mimetype='text/csv')
filename = "purchases_report_{}.csv".format(datetime.datetime.now(pytz.UTC).strftime("%Y-%m-%d-%H-%M-%S")) filename = "purchases_report_{}.csv".format(datetime.datetime.now(pytz.UTC).strftime("%Y-%m-%d-%H-%M-%S"))
response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename) response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
OrderItem.csv_purchase_report_btw_dates(response, start_date, end_date) # this flos is a little odd; what's up with report_type being called twice? check later
report.make_report(report_type, response, start_date, end_date)
return response return response
elif request.method == 'GET': elif request.method == 'GET':
......
...@@ -477,7 +477,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification): ...@@ -477,7 +477,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification):
# developing and aren't interested in working on student identity # developing and aren't interested in working on student identity
# verification functionality. If you do want to work on it, you have to # verification functionality. If you do want to work on it, you have to
# explicitly enable these in your private settings. # explicitly enable these in your private settings.
if settings.FEATURES.get('AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'): if settings.MITX_FEATURES.get('AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'):
return return
aes_key_str = settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["FACE_IMAGE_AES_KEY"] aes_key_str = settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["FACE_IMAGE_AES_KEY"]
......
...@@ -132,6 +132,7 @@ def create_order(request): ...@@ -132,6 +132,7 @@ def create_order(request):
""" """
Submit PhotoVerification and create a new Order for this verified cert Submit PhotoVerification and create a new Order for this verified cert
""" """
from nose.tools import set_trace; set_trace()
if not SoftwareSecurePhotoVerification.user_has_valid_or_pending(request.user): if not SoftwareSecurePhotoVerification.user_has_valid_or_pending(request.user):
attempt = SoftwareSecurePhotoVerification(user=request.user) attempt = SoftwareSecurePhotoVerification(user=request.user)
b64_face_image = request.POST['face_image'].split(",")[1] b64_face_image = request.POST['face_image'].split(",")[1]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment