/csv)?$',
+ 'instructor.views.api.get_purchase_transaction', name="get_purchase_transaction"),
url(r'^get_anon_ids$',
'instructor.views.api.get_anon_ids', name="get_anon_ids"),
url(r'^get_distribution$',
diff --git a/lms/djangoapps/instructor/views/instructor_dashboard.py b/lms/djangoapps/instructor/views/instructor_dashboard.py
index d9118d3016..2590fc373a 100644
--- a/lms/djangoapps/instructor/views/instructor_dashboard.py
+++ b/lms/djangoapps/instructor/views/instructor_dashboard.py
@@ -142,6 +142,7 @@ def _section_e_commerce(course_key, access):
'ajax_get_coupon_info': reverse('get_coupon_info', kwargs={'course_id': course_key.to_deprecated_string()}),
'ajax_update_coupon': reverse('update_coupon', kwargs={'course_id': course_key.to_deprecated_string()}),
'ajax_add_coupon': reverse('add_coupon', kwargs={'course_id': course_key.to_deprecated_string()}),
+ 'get_purchase_transaction_url': reverse('get_purchase_transaction', kwargs={'course_id': course_key.to_deprecated_string()}),
'instructor_url': reverse('instructor_dashboard', kwargs={'course_id': course_key.to_deprecated_string()}),
'coupons': coupons,
'total_amount': total_amount,
diff --git a/lms/djangoapps/instructor_analytics/basic.py b/lms/djangoapps/instructor_analytics/basic.py
index 0ec5876014..2254cdfc3a 100644
--- a/lms/djangoapps/instructor_analytics/basic.py
+++ b/lms/djangoapps/instructor_analytics/basic.py
@@ -3,7 +3,7 @@ Student and course analytics.
Serve miscellaneous course and student data
"""
-
+from shoppingcart.models import PaidCourseRegistration, CouponRedemption
from django.contrib.auth.models import User
import xmodule.graders as xmgraders
@@ -11,9 +11,60 @@ import xmodule.graders as xmgraders
STUDENT_FEATURES = ('id', 'username', 'first_name', 'last_name', 'is_staff', 'email')
PROFILE_FEATURES = ('name', 'language', 'location', 'year_of_birth', 'gender',
'level_of_education', 'mailing_address', 'goals')
+ORDER_ITEM_FEATURES = ('list_price', 'unit_cost', 'order_id')
+ORDER_FEATURES = ('purchase_time',)
+
AVAILABLE_FEATURES = STUDENT_FEATURES + PROFILE_FEATURES
+def purchase_transactions(course_id, features):
+ """
+ Return list of purchased transactions features as dictionaries.
+
+ purchase_transactions(course_id, ['username, email', unit_cost])
+ would return [
+ {'username': 'username1', 'email': 'email1', unit_cost:'cost1 in decimal'.}
+ {'username': 'username2', 'email': 'email2', unit_cost:'cost2 in decimal'.}
+ {'username': 'username3', 'email': 'email3', unit_cost:'cost3 in decimal'.}
+ ]
+ """
+
+ purchased_courses = PaidCourseRegistration.objects.filter(course_id=course_id, status='purchased')
+
+ def purchase_transactions_info(purchased_course, features):
+ """ convert purchase transactions to dictionary """
+ coupon_code_dict = dict()
+ student_features = [x for x in STUDENT_FEATURES if x in features]
+ order_features = [x for x in ORDER_FEATURES if x in features]
+ order_item_features = [x for x in ORDER_ITEM_FEATURES if x in features]
+
+ # Extracting user information
+ student_dict = dict((feature, getattr(purchased_course.user, feature))
+ for feature in student_features)
+
+ # Extracting Order information
+ order_dict = dict((feature, getattr(purchased_course.order, feature))
+ for feature in order_features)
+
+ # Extracting OrderItem information
+ order_item_dict = dict((feature, getattr(purchased_course, feature))
+ for feature in order_item_features)
+ order_item_dict.update({"orderitem_id": getattr(purchased_course, 'id')})
+
+ try:
+ coupon_redemption = CouponRedemption.objects.select_related('coupon').get(order_id=purchased_course.order_id)
+ except CouponRedemption.DoesNotExist:
+ coupon_code_dict = {'coupon_code': 'None'}
+ else:
+ coupon_code_dict = {'coupon_code': coupon_redemption.coupon.code}
+
+ student_dict.update(dict(order_dict.items() + order_item_dict.items() + coupon_code_dict.items()))
+ student_dict.update({'course_id': course_id.to_deprecated_string()})
+ return student_dict
+
+ return [purchase_transactions_info(purchased_course, features) for purchased_course in purchased_courses]
+
+
def enrolled_students_features(course_id, features):
"""
Return list of student features as dictionaries.
diff --git a/lms/static/coffee/src/instructor_dashboard/e-commerce.coffee b/lms/static/coffee/src/instructor_dashboard/e-commerce.coffee
new file mode 100644
index 0000000000..24449f771a
--- /dev/null
+++ b/lms/static/coffee/src/instructor_dashboard/e-commerce.coffee
@@ -0,0 +1,36 @@
+###
+E-Commerce Download Section
+###
+
+# Ecommerce Purchase Download Section
+class ECommerce
+ constructor: (@$section) ->
+ # attach self to html so that instructor_dashboard.coffee can find
+ # this object to call event handlers like 'onClickTitle'
+ @$section.data 'wrapper', @
+
+ # gather elements
+ @$list_purchase_csv_btn = @$section.find("input[name='list-purchase-transaction-csv']'")
+
+ # attach click handlers
+ # this handler binds to both the download
+ # and the csv button
+ @$list_purchase_csv_btn.click (e) =>
+ url = @$list_purchase_csv_btn.data 'endpoint'
+ url += '/csv'
+ location.href = url
+
+
+ # handler for when the section title is clicked.
+ onClickTitle: ->
+ @clear_display()
+
+ clear_display: ->
+
+
+# export for use
+# create parent namespaces if they do not already exist.
+_.defaults window, InstructorDashboard: {}
+_.defaults window.InstructorDashboard, sections: {}
+_.defaults window.InstructorDashboard.sections,
+ ECommerce: ECommerce
diff --git a/lms/static/coffee/src/instructor_dashboard/instructor_dashboard.coffee b/lms/static/coffee/src/instructor_dashboard/instructor_dashboard.coffee
index dec365f6ae..fa7c0d353f 100644
--- a/lms/static/coffee/src/instructor_dashboard/instructor_dashboard.coffee
+++ b/lms/static/coffee/src/instructor_dashboard/instructor_dashboard.coffee
@@ -155,6 +155,9 @@ setup_instructor_dashboard_sections = (idash_content) ->
,
constructor: window.InstructorDashboard.sections.DataDownload
$element: idash_content.find ".#{CSS_IDASH_SECTION}#data_download"
+ ,
+ constructor: window.InstructorDashboard.sections.ECommerce
+ $element: idash_content.find ".#{CSS_IDASH_SECTION}#e-commerce"
,
constructor: window.InstructorDashboard.sections.Membership
$element: idash_content.find ".#{CSS_IDASH_SECTION}#membership"
diff --git a/lms/static/sass/course/instructor/_instructor_2.scss b/lms/static/sass/course/instructor/_instructor_2.scss
index d1b1a70d78..3a0efe76f0 100644
--- a/lms/static/sass/course/instructor/_instructor_2.scss
+++ b/lms/static/sass/course/instructor/_instructor_2.scss
@@ -931,7 +931,7 @@ input[name="subject"] {
}
}
- // coupon edit and add modals
+ // coupon edit and add modals
#add-coupon-modal, #edit-coupon-modal{
.inner-wrapper {
background: #fff;
@@ -1046,4 +1046,88 @@ input[name="subject"] {
}
}
}
+
+}
+
+.profile-distribution-widget {
+ margin-bottom: $baseline * 2;
+
+ .display-text {}
+
+ .display-graph .graph-placeholder {
+ width: 750px;
+ height: 250px;
+ }
+
+ .display-table {
+ .slickgrid {
+ height: 250px;
+ }
+ }
+}
+
+.grade-distributions-widget {
+ margin-bottom: $baseline * 2;
+
+ .last-updated {
+ line-height: 2.2em;
+ @include font-size(12);
+ }
+
+ .display-graph .graph-placeholder {
+ width: 750px;
+ height: 200px;
+ }
+
+ .display-text {
+ line-height: 2em;
+ }
+}
+
+input[name="subject"] {
+ width:600px;
+}
+
+.enrollment-wrapper {
+ margin-bottom: $baseline * 2;
+
+ .count {
+ color: green;
+ font-weight: bold;
+ }
+}
+
+.ecommerce-wrapper{
+ h2{
+ height: 26px;
+ line-height: 26px;
+ span{
+ float: right;
+ font-size: 16px;
+ font-weight: bold;
+ span{
+ background: #ddd;
+ padding: 2px 9px;
+ border-radius: 2px;
+ float: none;
+ font-weight: 400;
+ }
+ }
+ }
+ span.tip{
+ padding: 10px 15px;
+ display: block;
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+ background: #f8f4ec;
+ color: #3c3c3c;
+ line-height: 30px;
+ .add{
+ @include button(simple, $blue);
+ @extend .button-reset;
+ font-size: em(13);
+ float: right;
+ }
+ }
+
}
diff --git a/lms/templates/instructor/instructor_dashboard_2/e-commerce.html b/lms/templates/instructor/instructor_dashboard_2/e-commerce.html
index 061ca5dcac..5bc7d9cee9 100644
--- a/lms/templates/instructor/instructor_dashboard_2/e-commerce.html
+++ b/lms/templates/instructor/instructor_dashboard_2/e-commerce.html
@@ -3,6 +3,12 @@
<%include file="add_coupon_modal.html" args="section_data=section_data" />
<%include file="edit_coupon_modal.html" args="section_data=section_data" />
+%if section_data['access']['finance_admin'] is True:
+ ${_("Click to generate a CSV file for all purchase transactions in this course")}
+
+
+%endif
+
${_("Coupons List")}
%if section_data['total_amount'] is not None: