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
...@@ -23,6 +23,7 @@ from xmodule.course_module import CourseDescriptor ...@@ -23,6 +23,7 @@ from xmodule.course_module import CourseDescriptor
from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.modulestore.exceptions import ItemNotFoundError
from course_modes.models import CourseMode from course_modes.models import CourseMode
from courseware.courses import get_course_by_id
from edxmako.shortcuts import render_to_string from edxmako.shortcuts import render_to_string
from student.views import course_from_id from student.views import course_from_id
from student.models import CourseEnrollment, unenroll_done from student.models import CourseEnrollment, unenroll_done
...@@ -259,66 +260,6 @@ class OrderItem(models.Model): ...@@ -259,66 +260,6 @@ class OrderItem(models.Model):
""" """
return self.pk_with_subclass, set([]) return self.pk_with_subclass, set([])
@classmethod
def purchased_items_btw_dates(cls, start_date, end_date):
"""
Returns a QuerySet of the purchased items between start_date and end_date inclusive.
"""
return cls.objects.filter(
status="purchased",
fulfilled_time__gte=start_date,
fulfilled_time__lt=end_date,
)
@classmethod
def csv_purchase_report_btw_dates(cls, filelike, start_date, end_date):
"""
Outputs a CSV report into "filelike" (a file-like python object, such as an actual file, an HttpRequest,
or sys.stdout) of purchased items between start_date and end_date inclusive.
Opening and closing filelike (if applicable) should be taken care of by the caller
"""
items = cls.purchased_items_btw_dates(start_date, end_date).order_by("fulfilled_time")
writer = unicodecsv.writer(filelike, encoding="utf-8")
writer.writerow(OrderItem.csv_report_header_row())
for item in items:
writer.writerow(item.csv_report_row)
@classmethod
def csv_report_header_row(cls):
"""
Returns the "header" row for a csv report of purchases
"""
return [
"Purchase Time",
"Order ID",
"Status",
"Quantity",
"Unit Cost",
"Total Cost",
"Currency",
"Description",
"Comments"
]
@property
def csv_report_row(self):
"""
Returns an array which can be fed into csv.writer to write out one csv row
"""
return [
self.fulfilled_time,
self.order_id, # pylint: disable=no-member
self.status,
self.qty,
self.unit_cost,
self.line_cost,
self.currency,
self.line_desc,
self.report_comments,
]
@property @property
def pk_with_subclass(self): def pk_with_subclass(self):
""" """
...@@ -625,3 +566,204 @@ class CertificateItem(OrderItem): ...@@ -625,3 +566,204 @@ class CertificateItem(OrderItem):
"Please include your order number in your e-mail. " "Please include your order number in your e-mail. "
"Please do NOT include your credit card information.").format( "Please do NOT include your credit card information.").format(
billing_email=settings.PAYMENT_SUPPORT_EMAIL) billing_email=settings.PAYMENT_SUPPORT_EMAIL)
class Report(models.Model):
"""
Base class for making CSV reports related to revenue, enrollments, etc
To make a different type of report, write a new subclass that implements
the methods get_query, csv_report_header_row, and csv_report_row.
"""
@classmethod
def initialize_report(cls, report_type):
if report_type == "refund_report":
return RefundReport()
elif report_type == "itemized_purchase_report":
return ItemizedPurchaseReport()
elif report_type == "university_revenue_share":
return UniversityRevenueShareReport()
elif report_type == "certificate_status":
return CertificateStatusReport()
else:
return # TODO return an error
def get_query(self, start_date, end_date):
raise NotImplementedError
def csv_report_header_row(self, start_date, end_date):
raise NotImplementedError
def csv_report_row(self):
raise NotImplementedError
@classmethod
def make_report(cls, report_type, filelike, start_date, end_date):
report = cls.initialize_report(report_type)
items = report.get_query(start_date, end_date)
writer = unicodecsv.writer(filelike, encoding="utf-8")
writer.writerow(report.csv_report_header_row())
for item in items:
writer.writerow(report.csv_report_row(item))
class RefundReport(Report):
def get_query(self, start_date, end_date):
return CertificateItem.objects.filter(
status="refunded",
)
def csv_report_header_row(self):
return [
"Order Number",
"Customer Name",
"Date of Original Transaction",
"Date of Refund",
"Amount of Refund",
"Service Fees (if any)",
]
def csv_report_row(self, item):
return [
item.order_id,
item.user.get_full_name(),
item.fulfilled_time,
item.refund_requested_time, # actually may need to use refund_fulfilled here
item.line_cost,
0, # TODO: determine if service_fees field is necessary; if so, add
]
class ItemizedPurchaseReport(Report):
def get_query(self, start_date, end_date):
return OrderItem.objects.filter(
status="purchased",
fulfilled_time__gte=start_date,
fulfilled_time__lt=end_date,
).order_by("fulfilled_time")
def csv_report_header_row(self):
return [
"Purchase Time",
"Order ID",
"Status",
"Quantity",
"Unit Cost",
"Total Cost",
"Currency",
"Description",
"Comments"
]
def csv_report_row(self, item):
return [
item.fulfilled_time,
item.order_id, # pylint: disable=no-member
item.status,
item.qty,
item.unit_cost,
item.line_cost,
item.currency,
item.line_desc,
item.report_comments,
]
class CertificateStatusReport(Report):
def get_query(self, start_date, send_date):
results = []
for course_id in settings.COURSE_LISTINGS:
cur_course = get_course_by_id(course)
university = cur_course.org
course = cur_course.number + " " + course.display_name #TODO add term (i.e. Fall 2013)?
enrollments = CourseEnrollment.objects.filter(course_id=course_id)
total_enrolled = enrollments.objects.count()
audit_enrolled = enrollments.objects.filter(mode="audit").count()
honor_enrolled = enrollments.objects.filter(mode="honor").count()
verified_enrollments = enrollments.objects.filter(mode="verified")
verified_enrolled = verified_enrollments.objects.count()
gross_rev = CertificateItem.objects.filter(course_id=course_id, mode="verified").aggregate(Sum('unit_cost'))
gross_rev_over_min = gross_rev - (CourseMode.objects.get('course_id').min_price * verified_enrollments)
num_verified_over_min = 0 # TODO clarify with billing what exactly this means
refunded_enrollments = CertificateItem.objects.filter(course_id='course_id', mode="refunded")
number_of_refunds = refunded_enrollments.objects.count()
dollars_refunded = refunded_enrollments.objects.aggregate(Sum('unit_cost'))
result = [
university,
course,
total_enrolled,
audit_enrolled,
honor_enrolled,
verified_enrolled,
gross_rev,
gross_rev_over_min,
num_verified_over_min,
number_of_refunds,
dollars_refunded
]
results.append(result)
return results
def csv_report_header_row(self):
return [
"University",
"Course",
"Total Enrolled",
"Audit Enrollment",
"Honor Code Enrollment",
"Verified Enrollment",
"Gross Revenue",
"Gross Revenue over the Minimum",
"Number of Verified over the Minimum",
"Number of Refunds",
"Dollars Refunded",
]
def csv_report_row(self, item):
return item
class UniversityRevenueShareReport(Report):
def get_query(self, start_date, end_date):
results = []
for course_id in settings.COURSE_LISTINGS:
cur_course = get_course_by_id(course)
university = cur_course.org
course = cur_course.number + " " + course.display_name
num_transactions = 0 # TODO clarify with building what transactions are included in this (purchases? refunds? etc)
total_payments_collected = CertificateItem.objects.filter(course_id=course_id, mode="verified").aggregate(Sum('unit_cost'))
#note: we're assuming certitems are the only way to make money right now
service_fees = 0 # TODO add an actual service fees field, in case needed in future
refunded_enrollments = CertificateItem.objects.filter(course_id='course_id', mode="refunded")
num_refunds = refunded_enrollments.objects.count()
amount_refunds = refunded_enrollments.objects.aggregate(Sum('unit_cost'))
result = [
university,
course,
num_transactions,
total_payments_collected,
service_fees,
refunded_enrollments,
num_refunds,
amount_refunds
]
results.append(result)
return results
def csv_report_header_row(self):
return [
"University",
"Course",
"Number of Transactions",
"Total Payments Collected",
"Service Fees (if any)",
"Number of Successful Refunds",
"Total Amount of Refunds",
# note this is restricted by a date range
]
def csv_report_row(self, item):
return item
...@@ -17,7 +17,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase ...@@ -17,7 +17,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.models import (Order, OrderItem, CertificateItem, InvalidCartItem, PaidCourseRegistration, from shoppingcart.models import (Order, OrderItem, CertificateItem, InvalidCartItem, PaidCourseRegistration,
OrderItemSubclassPK, PaidCourseRegistrationAnnotation) OrderItemSubclassPK, PaidCourseRegistrationAnnotation, 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
...@@ -324,7 +324,82 @@ class PaidCourseRegistrationTest(ModuleStoreTestCase): ...@@ -324,7 +324,82 @@ class PaidCourseRegistrationTest(ModuleStoreTestCase):
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE) @override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
class PurchaseReportTest(ModuleStoreTestCase): class RefundReportTest(ModuleStoreTestCase):
FIVE_MINS = datetime.timedelta(minutes=5)
def setUp(self):
self.user = UserFactory.create()
self.user.first_name = "John"
self.user.last_name = "Doe"
self.user.save()
self.course_id = "MITx/999/Robot_Super_Course"
self.cost = 40
self.course = CourseFactory.create(org='MITx', number='999', display_name=u'Robot Super Course')
course_mode = CourseMode(course_id=self.course_id,
mode_slug="honor",
mode_display_name="honor cert",
min_price=self.cost)
course_mode.save()
course_mode2 = CourseMode(course_id=self.course_id,
mode_slug="verified",
mode_display_name="verified cert",
min_price=self.cost)
course_mode2.save()
self.cart = Order.get_cart_for_user(self.user)
CertificateItem.add_to_order(self.cart, self.course_id, self.cost, 'verified')
self.cart.purchase()
# should auto-refund the relevant cert
CourseEnrollment.unenroll(self.user, self.course_id)
self.cert_item = CertificateItem.objects.get(user=self.user, course_id=self.course_id)
self.now = datetime.datetime.now(pytz.UTC)
def test_get_query(self):
report_type = "refund_report"
report = Report.initialize_report(report_type)
refunded_certs = report.get_query(self.now - self.FIVE_MINS, self.now + self.FIVE_MINS)
self.assertEqual(len(refunded_certs), 1)
self.assertIn(self.cert_item, refunded_certs)
# TODO no time restrictions yet
test_time = datetime.datetime.now(pytz.UTC)
CORRECT_CSV = dedent("""
Order Number,Customer Name,Date of Original Transaction,Date of Refund,Amount of Refund,Service Fees (if any)
1,John Doe,{time_str},{time_str},40,0
""".format(time_str=str(test_time)))
def test_purchased_csv(self):
"""
Tests that a generated purchase report CSV is as we expect
"""
# coerce the purchase times to self.test_time so that the test can match.
# It's pretty hard to patch datetime.datetime b/c it's a python built-in, which is immutable, so we
# make the times match this way
# TODO test multiple report types
report_type = "refund_report"
report = Report.initialize_report(report_type)
for item in report.get_query(self.now - self.FIVE_MINS, self.now + self.FIVE_MINS):
item.fulfilled_time = self.test_time
item.refund_requested_time = self.test_time #hm do we want to make these different
item.save()
# add annotation to the
csv_file = StringIO.StringIO()
Report.make_report(report_type, csv_file, self.now - self.FIVE_MINS, self.now + self.FIVE_MINS)
csv = csv_file.getvalue()
csv_file.close()
# Using excel mode csv, which automatically ends lines with \r\n, so need to convert to \n
self.assertEqual(csv.replace('\r\n', '\n').strip(), self.CORRECT_CSV.strip())
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
class ItemizedPurchaseReportTest(ModuleStoreTestCase):
FIVE_MINS = datetime.timedelta(minutes=5) FIVE_MINS = datetime.timedelta(minutes=5)
TEST_ANNOTATION = u'Ba\xfc\u5305' TEST_ANNOTATION = u'Ba\xfc\u5305'
...@@ -353,11 +428,14 @@ class PurchaseReportTest(ModuleStoreTestCase): ...@@ -353,11 +428,14 @@ class PurchaseReportTest(ModuleStoreTestCase):
self.now = datetime.datetime.now(pytz.UTC) self.now = datetime.datetime.now(pytz.UTC)
def test_purchased_items_btw_dates(self): def test_purchased_items_btw_dates(self):
purchases = OrderItem.purchased_items_btw_dates(self.now - self.FIVE_MINS, self.now + self.FIVE_MINS) # TODO test multiple report types
report_type = "itemized_purchase_report"
report = Report.initialize_report(report_type)
purchases = report.get_query(self.now - self.FIVE_MINS, self.now + self.FIVE_MINS)
self.assertEqual(len(purchases), 2) self.assertEqual(len(purchases), 2)
self.assertIn(self.reg.orderitem_ptr, purchases) self.assertIn(self.reg.orderitem_ptr, purchases)
self.assertIn(self.cert_item.orderitem_ptr, purchases) self.assertIn(self.cert_item.orderitem_ptr, purchases)
no_purchases = OrderItem.purchased_items_btw_dates(self.now + self.FIVE_MINS, no_purchases = report.get_query(self.now + self.FIVE_MINS,
self.now + self.FIVE_MINS + self.FIVE_MINS) self.now + self.FIVE_MINS + self.FIVE_MINS)
self.assertFalse(no_purchases) self.assertFalse(no_purchases)
...@@ -376,13 +454,16 @@ class PurchaseReportTest(ModuleStoreTestCase): ...@@ -376,13 +454,16 @@ class PurchaseReportTest(ModuleStoreTestCase):
# coerce the purchase times to self.test_time so that the test can match. # coerce the purchase times to self.test_time so that the test can match.
# It's pretty hard to patch datetime.datetime b/c it's a python built-in, which is immutable, so we # It's pretty hard to patch datetime.datetime b/c it's a python built-in, which is immutable, so we
# make the times match this way # make the times match this way
for item in OrderItem.purchased_items_btw_dates(self.now - self.FIVE_MINS, self.now + self.FIVE_MINS): # TODO test multiple report types
report_type = "itemized_purchase_report"
report = Report.initialize_report(report_type)
for item in report.get_query(self.now - self.FIVE_MINS, self.now + self.FIVE_MINS):
item.fulfilled_time = self.test_time item.fulfilled_time = self.test_time
item.save() item.save()
# add annotation to the # add annotation to the
csv_file = StringIO.StringIO() csv_file = StringIO.StringIO()
OrderItem.csv_purchase_report_btw_dates(csv_file, self.now - self.FIVE_MINS, self.now + self.FIVE_MINS) Report.make_report(report_type, csv_file, self.now - self.FIVE_MINS, self.now + self.FIVE_MINS)
csv = csv_file.getvalue() csv = csv_file.getvalue()
csv_file.close() csv_file.close()
# Using excel mode csv, which automatically ends lines with \r\n, so need to convert to \n # Using excel mode csv, which automatically ends lines with \r\n, so need to convert to \n
...@@ -403,6 +484,88 @@ class PurchaseReportTest(ModuleStoreTestCase): ...@@ -403,6 +484,88 @@ class PurchaseReportTest(ModuleStoreTestCase):
""" """
self.assertEqual(unicode(self.annotation), u'{} : {}'.format(self.course_id, self.TEST_ANNOTATION)) self.assertEqual(unicode(self.annotation), u'{} : {}'.format(self.course_id, self.TEST_ANNOTATION))
# TODO: finish this test class
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
class CertificateStatusReportTest(ModuleStoreTestCase):
FIVE_MINS = datetime.timedelta(minutes=5)
def setUp(self):
self.user = UserFactory.create()
self.user.first_name = "John"
self.user.last_name = "Doe"
self.user.save()
self.course_id = "MITx/999/Robot_Super_Course"
self.cost = 40
self.course = CourseFactory.create(org='MITx', number='999', display_name=u'Robot Super Course')
course_mode = CourseMode(course_id=self.course_id,
mode_slug="honor",
mode_display_name="honor cert",
min_price=self.cost)
course_mode.save()
course_mode2 = CourseMode(course_id=self.course_id,
mode_slug="verified",
mode_display_name="verified cert",
min_price=self.cost)
course_mode2.save()
self.cart = Order.get_cart_for_user(self.user)
CertificateItem.add_to_order(self.cart, self.course_id, self.cost, 'verified')
self.cart.purchase()
self.now = datetime.datetime.now(pytz.UTC)
# TODO finish these tests. This is just a basic test to start with, making sure the regular
# flow doesn't throw any strange errors while running
def test_basic(self):
report_type = "certificate_status"
report = Report.initialize_report(report_type)
refunded_certs = report.get_query(self.now - self.FIVE_MINS, self.now + self.FIVE_MINS)
csv_file = StringIO.StringIO()
report.make_report(report_type, csv_file, self.now - self.FIVE_MINS, self.now + self.FIVE_MINS)
# TODO no time restrictions yet
# TODO: finish this test class
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
class UniversityRevenueShareReportTest(ModuleStoreTestCase):
FIVE_MINS = datetime.timedelta(minutes=5)
def setUp(self):
self.user = UserFactory.create()
self.user.first_name = "John"
self.user.last_name = "Doe"
self.user.save()
self.course_id = "MITx/999/Robot_Super_Course"
self.cost = 40
self.course = CourseFactory.create(org='MITx', number='999', display_name=u'Robot Super Course')
course_mode = CourseMode(course_id=self.course_id,
mode_slug="honor",
mode_display_name="honor cert",
min_price=self.cost)
course_mode.save()
course_mode2 = CourseMode(course_id=self.course_id,
mode_slug="verified",
mode_display_name="verified cert",
min_price=self.cost)
course_mode2.save()
self.cart = Order.get_cart_for_user(self.user)
CertificateItem.add_to_order(self.cart, self.course_id, self.cost, 'verified')
self.cart.purchase()
self.now = datetime.datetime.now(pytz.UTC)
# TODO finish these tests. This is just a basic test to start with, making sure the regular
# flow doesn't throw any strange errors while running
def test_basic(self):
report_type = "university_revenue_share"
report = Report.initialize_report(report_type)
refunded_certs = report.get_query(self.now - self.FIVE_MINS, self.now + self.FIVE_MINS)
csv_file = StringIO.StringIO()
report.make_report(report_type, csv_file, self.now - self.FIVE_MINS, self.now + self.FIVE_MINS)
# TODO no time restrictions yet
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE) @override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
class CertificateItemTest(ModuleStoreTestCase): class CertificateItemTest(ModuleStoreTestCase):
......
...@@ -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