Commit 4333e539 by Muhammad Shoaib Committed by Chris Dodge

added registration-codes generation functionality

rebased and resolve conficts with cdoge/registration_codes

feature enhancement request: added transaction group name text field to the download buttons as an extra optional query paramerter
parent 08ff0305
...@@ -11,7 +11,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase ...@@ -11,7 +11,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.tests.factories import CourseFactory
from course_modes.models import CourseMode from course_modes.models import CourseMode
from shoppingcart.models import Coupon, PaidCourseRegistration from shoppingcart.models import Coupon, PaidCourseRegistration, CourseRegistrationCode
from mock import patch from mock import patch
from student.roles import CourseFinanceAdminRole from student.roles import CourseFinanceAdminRole
...@@ -107,6 +107,17 @@ class TestECommerceDashboardViews(ModuleStoreTestCase): ...@@ -107,6 +107,17 @@ class TestECommerceDashboardViews(ModuleStoreTestCase):
response = self.client.post(add_coupon_url, data=data) response = self.client.post(add_coupon_url, data=data)
self.assertTrue('Please Enter the Integer Value for Coupon Discount' in response.content) self.assertTrue('Please Enter the Integer Value for Coupon Discount' in response.content)
course_registration = CourseRegistrationCode(
code='Vs23Ws4j', course_id=self.course.id.to_deprecated_string(),
transaction_group_name='Test Group', created_by=self.instructor
)
course_registration.save()
data['code'] = 'Vs23Ws4j'
response = self.client.post(add_coupon_url, data)
self.assertTrue("The code ({code}) that you have tried to define is already in use as a registration code"
.format(code=data['code']) in response.content)
def test_delete_coupon(self): def test_delete_coupon(self):
""" """
Test Delete Coupon Scenarios. Handle all the HttpResponses return by remove_coupon view Test Delete Coupon Scenarios. Handle all the HttpResponses return by remove_coupon view
...@@ -213,3 +224,15 @@ class TestECommerceDashboardViews(ModuleStoreTestCase): ...@@ -213,3 +224,15 @@ class TestECommerceDashboardViews(ModuleStoreTestCase):
data = {'coupon_id': coupon.id, 'code': '11111', 'discount': '12'} data = {'coupon_id': coupon.id, 'code': '11111', 'discount': '12'}
response = self.client.post(update_coupon_url, data=data) response = self.client.post(update_coupon_url, data=data)
self.assertTrue('coupon with the coupon id ({coupon_id}) already exist'.format(coupon_id=coupon.id) in response.content) self.assertTrue('coupon with the coupon id ({coupon_id}) already exist'.format(coupon_id=coupon.id) in response.content)
course_registration = CourseRegistrationCode(
code='Vs23Ws4j', course_id=self.course.id.to_deprecated_string(),
transaction_group_name='Test Group', created_by=self.instructor
)
course_registration.save()
data = {'coupon_id': coupon.id, 'code': 'Vs23Ws4j',
'discount': '6', 'course_id': coupon.course_id.to_deprecated_string()}
response = self.client.post(update_coupon_url, data=data)
self.assertTrue("The code ({code}) that you have tried to define is already in use as a registration code".
format(code=data['code']) in response.content)
...@@ -5,6 +5,7 @@ JSON views which the instructor dashboard requests. ...@@ -5,6 +5,7 @@ JSON views which the instructor dashboard requests.
Many of these GETs may become PUTs in the future. Many of these GETs may become PUTs in the future.
""" """
from django.views.decorators.http import require_POST
import json import json
import logging import logging
...@@ -14,11 +15,14 @@ from django.conf import settings ...@@ -14,11 +15,14 @@ from django.conf import settings
from django_future.csrf import ensure_csrf_cookie from django_future.csrf import ensure_csrf_cookie
from django.views.decorators.cache import cache_control from django.views.decorators.cache import cache_control
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import IntegrityError
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.core.validators import validate_email from django.core.validators import validate_email
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden
from django.utils.html import strip_tags from django.utils.html import strip_tags
import string # pylint: disable=W0402
import random
from util.json_request import JsonResponse from util.json_request import JsonResponse
from instructor.views.instructor_task_helpers import extract_email_features, extract_task_features from instructor.views.instructor_task_helpers import extract_email_features, extract_task_features
...@@ -34,6 +38,7 @@ from django_comment_common.models import ( ...@@ -34,6 +38,7 @@ from django_comment_common.models import (
) )
from edxmako.shortcuts import render_to_response from edxmako.shortcuts import render_to_response
from courseware.models import StudentModule from courseware.models import StudentModule
from shoppingcart.models import Coupon, CourseRegistrationCode, RegistrationCodeRedemption
from student.models import CourseEnrollment, unique_id_for_user, anonymous_id_for_user from student.models import CourseEnrollment, unique_id_for_user, anonymous_id_for_user
import instructor_task.api import instructor_task.api
from instructor_task.api_helper import AlreadyRunningError from instructor_task.api_helper import AlreadyRunningError
...@@ -561,7 +566,7 @@ def get_purchase_transaction(request, course_id, csv=False): # pylint: disable= ...@@ -561,7 +566,7 @@ def get_purchase_transaction(request, course_id, csv=False): # pylint: disable=
'order_id', 'order_id',
] ]
student_data = analytics.basic.purchase_transactions(course_id, query_features) student_data = instructor_analytics.basic.purchase_transactions(course_id, query_features)
if not csv: if not csv:
response_payload = { response_payload = {
...@@ -630,6 +635,155 @@ def get_students_features(request, course_id, csv=False): # pylint: disable=W06 ...@@ -630,6 +635,155 @@ def get_students_features(request, course_id, csv=False): # pylint: disable=W06
return instructor_analytics.csvs.create_csv_response("enrolled_profiles.csv", header, datarows) return instructor_analytics.csvs.create_csv_response("enrolled_profiles.csv", header, datarows)
def save_registration_codes(request, course_id, generated_codes_list, group_name):
"""
recursive function that generate a new code every time and saves in the Course Registration Table
if validation check passes
"""
code = random_code_generator()
# check if the generated code is in the Coupon Table
matching_coupons = Coupon.objects.filter(code=code, is_active=True)
if matching_coupons:
return save_registration_codes(request, course_id, generated_codes_list, group_name)
course_registration = CourseRegistrationCode(
code=code, course_id=course_id.to_deprecated_string(),
transaction_group_name=group_name, created_by=request.user
)
try:
course_registration.save()
generated_codes_list.append(course_registration)
except IntegrityError:
return save_registration_codes(request, course_id, generated_codes_list, group_name)
def registration_codes_csv(file_name, codes_list, csv_type=None):
"""
Respond with the csv headers and data rows
given a dict of codes list
:param file_name:
:param codes_list:
:param csv_type:
"""
# csv headers
query_features = ['code', 'course_id', 'transaction_group_name', 'created_by', 'redeemed_by']
registration_codes = instructor_analytics.basic.course_registration_features(query_features, codes_list, csv_type)
header, data_rows = instructor_analytics.csvs.format_dictlist(registration_codes, query_features)
return analytics.csvs.create_csv_response(file_name, header, data_rows)
def random_code_generator():
"""
generate a random alphanumeric code of length defined in
REGISTRATION_CODE_LENGTH settings
"""
chars = string.ascii_uppercase + string.digits + string.ascii_lowercase
code_length = getattr(settings, 'REGISTRATION_CODE_LENGTH', 8)
return string.join((random.choice(chars) for _ in range(code_length)), '')
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
@require_POST
def get_registration_codes(request, course_id): # pylint: disable=W0613
"""
Respond with csv which contains a summary of all Registration Codes.
"""
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
#filter all the course registration codes
registration_codes = CourseRegistrationCode.objects.filter(course_id=course_id).order_by('transaction_group_name')
group_name = request.POST['download_transaction_group_name']
if group_name:
registration_codes = registration_codes.filter(transaction_group_name=group_name)
csv_type = 'download'
return registration_codes_csv("Registration_Codes.csv", registration_codes, csv_type)
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
@require_POST
def generate_registration_codes(request, course_id):
"""
Respond with csv which contains a summary of all Generated Codes.
"""
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
course_registration_codes = []
# covert the course registration code number into integer
try:
course_code_number = int(request.POST['course_registration_code_number'])
except ValueError:
course_code_number = int(float(request.POST['course_registration_code_number']))
group_name = request.POST['transaction_group_name']
for _ in range(course_code_number): # pylint: disable=W0621
save_registration_codes(request, course_id, course_registration_codes, group_name)
return registration_codes_csv("Registration_Codes.csv", course_registration_codes)
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
@require_POST
def active_registration_codes(request, course_id): # pylint: disable=W0613
"""
Respond with csv which contains a summary of all Active Registration Codes.
"""
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
# find all the registration codes in this course
registration_codes_list = CourseRegistrationCode.objects.filter(course_id=course_id).order_by('transaction_group_name')
group_name = request.POST['active_transaction_group_name']
if group_name:
registration_codes_list = registration_codes_list.filter(transaction_group_name=group_name)
# find the redeemed registration codes if any exist in the db
code_redemption_set = RegistrationCodeRedemption.objects.select_related('registration_code').filter(registration_code__course_id=course_id)
if code_redemption_set.exists():
redeemed_registration_codes = [code.registration_code.code for code in code_redemption_set]
# exclude the redeemed registration codes from the registration codes list and you will get
# all the registration codes that are active
registration_codes_list = registration_codes_list.exclude(code__in=redeemed_registration_codes)
return registration_codes_csv("Active_Registration_Codes.csv", registration_codes_list)
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
@require_POST
def spent_registration_codes(request, course_id): # pylint: disable=W0613
"""
Respond with csv which contains a summary of all Spent(used) Registration Codes.
"""
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
# find the redeemed registration codes if any exist in the db
code_redemption_set = RegistrationCodeRedemption.objects.select_related('registration_code').filter(registration_code__course_id=course_id)
spent_codes_list = []
if code_redemption_set.exists():
redeemed_registration_codes = [code.registration_code.code for code in code_redemption_set]
# filter the Registration Codes by course id and the redeemed codes and
# you will get a list of all the spent(Redeemed) Registration Codes
spent_codes_list = CourseRegistrationCode.objects.filter(course_id=course_id, code__in=redeemed_registration_codes).order_by('transaction_group_name')
group_name = request.POST['spent_transaction_group_name']
if group_name:
spent_codes_list = spent_codes_list.filter(transaction_group_name=group_name) # pylint: disable=E1103
csv_type = 'spent'
return registration_codes_csv("Spent_Registration_Codes.csv", spent_codes_list, csv_type)
@ensure_csrf_cookie @ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff') @require_level('staff')
......
...@@ -58,6 +58,16 @@ urlpatterns = patterns('', # nopep8 ...@@ -58,6 +58,16 @@ urlpatterns = patterns('', # nopep8
url(r'calculate_grades_csv$', url(r'calculate_grades_csv$',
'instructor.views.api.calculate_grades_csv', name="calculate_grades_csv"), 'instructor.views.api.calculate_grades_csv', name="calculate_grades_csv"),
# Registration Codes..
url(r'get_registration_codes$',
'instructor.views.api.get_registration_codes', name="get_registration_codes"),
url(r'generate_registration_codes$',
'instructor.views.api.generate_registration_codes', name="generate_registration_codes"),
url(r'active_registration_codes$',
'instructor.views.api.active_registration_codes', name="active_registration_codes"),
url(r'spent_registration_codes$',
'instructor.views.api.spent_registration_codes', name="spent_registration_codes"),
# spoc gradebook # spoc gradebook
url(r'^gradebook$', url(r'^gradebook$',
'instructor.views.api.spoc_gradebook', name='spoc_gradebook'), 'instructor.views.api.spoc_gradebook', name='spoc_gradebook'),
......
...@@ -8,7 +8,7 @@ from django.views.decorators.http import require_POST ...@@ -8,7 +8,7 @@ from django.views.decorators.http import require_POST
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from util.json_request import JsonResponse from util.json_request import JsonResponse
from django.http import HttpResponse, HttpResponseNotFound from django.http import HttpResponse, HttpResponseNotFound
from shoppingcart.models import Coupon from shoppingcart.models import Coupon, CourseRegistrationCode
import logging import logging
...@@ -59,6 +59,13 @@ def add_coupon(request, course_id): # pylint: disable=W0613 ...@@ -59,6 +59,13 @@ def add_coupon(request, course_id): # pylint: disable=W0613
if coupon: if coupon:
return HttpResponseNotFound(_("coupon with the coupon code ({code}) already exist").format(code=code)) return HttpResponseNotFound(_("coupon with the coupon code ({code}) already exist").format(code=code))
# check if the coupon code is in the CourseRegistrationCode Table
course_registration_code = CourseRegistrationCode.objects.filter(code=code)
if course_registration_code:
return HttpResponseNotFound(_(
"The code ({code}) that you have tried to define is already in use as a registration code").format(code=code)
)
description = request.POST.get('description') description = request.POST.get('description')
course_id = request.POST.get('course_id') course_id = request.POST.get('course_id')
try: try:
...@@ -96,6 +103,13 @@ def update_coupon(request, course_id): # pylint: disable=W0613 ...@@ -96,6 +103,13 @@ def update_coupon(request, course_id): # pylint: disable=W0613
if filtered_coupons: if filtered_coupons:
return HttpResponseNotFound(_("coupon with the coupon id ({coupon_id}) already exists").format(coupon_id=coupon_id)) return HttpResponseNotFound(_("coupon with the coupon id ({coupon_id}) already exists").format(coupon_id=coupon_id))
# check if the coupon code is in the CourseRegistrationCode Table
course_registration_code = CourseRegistrationCode.objects.filter(code=code)
if course_registration_code:
return HttpResponseNotFound(_(
"The code ({code}) that you have tried to define is already in use as a registration code").format(code=code)
)
description = request.POST.get('description') description = request.POST.get('description')
course_id = request.POST.get('course_id') course_id = request.POST.get('course_id')
try: try:
......
...@@ -144,6 +144,10 @@ def _section_e_commerce(course_key, access): ...@@ -144,6 +144,10 @@ def _section_e_commerce(course_key, access):
'ajax_add_coupon': reverse('add_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()}), '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()}), 'instructor_url': reverse('instructor_dashboard', kwargs={'course_id': course_key.to_deprecated_string()}),
'get_registration_code_csv_url': reverse('get_registration_codes', kwargs={'course_id': course_key.to_deprecated_string()}),
'generate_registration_code_csv_url': reverse('generate_registration_codes', kwargs={'course_id': course_key.to_deprecated_string()}),
'active_registration_code_csv_url': reverse('active_registration_codes', kwargs={'course_id': course_key.to_deprecated_string()}),
'spent_registration_code_csv_url': reverse('spent_registration_codes', kwargs={'course_id': course_key.to_deprecated_string()}),
'coupons': coupons, 'coupons': coupons,
'total_amount': total_amount, 'total_amount': total_amount,
} }
......
...@@ -6,6 +6,7 @@ Serve miscellaneous course and student data ...@@ -6,6 +6,7 @@ Serve miscellaneous course and student data
from shoppingcart.models import PaidCourseRegistration, CouponRedemption from shoppingcart.models import PaidCourseRegistration, CouponRedemption
from django.contrib.auth.models import User from django.contrib.auth.models import User
import xmodule.graders as xmgraders import xmodule.graders as xmgraders
from django.core.exceptions import ObjectDoesNotExist
STUDENT_FEATURES = ('id', 'username', 'first_name', 'last_name', 'is_staff', 'email') STUDENT_FEATURES = ('id', 'username', 'first_name', 'last_name', 'is_staff', 'email')
...@@ -15,6 +16,7 @@ ORDER_ITEM_FEATURES = ('list_price', 'unit_cost', 'order_id') ...@@ -15,6 +16,7 @@ ORDER_ITEM_FEATURES = ('list_price', 'unit_cost', 'order_id')
ORDER_FEATURES = ('purchase_time',) ORDER_FEATURES = ('purchase_time',)
AVAILABLE_FEATURES = STUDENT_FEATURES + PROFILE_FEATURES AVAILABLE_FEATURES = STUDENT_FEATURES + PROFILE_FEATURES
COURSE_REGISTRATION_FEATURES = ('code', 'course_id', 'transaction_group_name', 'created_by')
def purchase_transactions(course_id, features): def purchase_transactions(course_id, features):
...@@ -98,6 +100,42 @@ def enrolled_students_features(course_id, features): ...@@ -98,6 +100,42 @@ def enrolled_students_features(course_id, features):
return [extract_student(student, features) for student in students] return [extract_student(student, features) for student in students]
def course_registration_features(features, registration_codes, csv_type):
"""
Return list of Course Registration Codes as dictionaries.
course_registration_features
would return [
{'code': 'code1', 'course_id': 'edX/Open_DemoX/edx_demo_course, ..... }
{'code': 'code2', 'course_id': 'edX/Open_DemoX/edx_demo_course, ..... }
]
"""
def extract_course_registration(registration_code, features, csv_type):
""" convert registration_code to dictionary
:param registration_code:
:param features:
:param csv_type:
"""
registration_features = [x for x in COURSE_REGISTRATION_FEATURES if x in features]
course_registration_dict = dict((feature, getattr(registration_code, feature)) for feature in registration_features)
course_registration_dict['redeemed_by'] = None
# we have to capture the redeemed_by value in the case of the downloading and spent registration
# codes csv. In the case of active and generated registration codes the redeemed_by value will be None.
# They have not been redeemed yet
if csv_type is not None:
try:
course_registration_dict['redeemed_by'] = getattr(registration_code.registrationcoderedemption_set.get(registration_code=registration_code), 'redeemed_by')
except ObjectDoesNotExist:
pass
course_registration_dict['course_id'] = course_registration_dict['course_id'].to_deprecated_string()
return course_registration_dict
return [extract_course_registration(code, features, csv_type) for code in registration_codes]
def dump_grading_context(course): def dump_grading_context(course):
""" """
Render information about course grading context Render information about course grading context
......
...@@ -6,8 +6,9 @@ from django.test import TestCase ...@@ -6,8 +6,9 @@ from django.test import TestCase
from student.models import CourseEnrollment from student.models import CourseEnrollment
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
from opaque_keys.edx.locations import SlashSeparatedCourseKey from opaque_keys.edx.locations import SlashSeparatedCourseKey
from shoppingcart.models import CourseRegistrationCode, RegistrationCodeRedemption, Order
from instructor_analytics.basic import enrolled_students_features, AVAILABLE_FEATURES, STUDENT_FEATURES, PROFILE_FEATURES from instructor_analytics.basic import enrolled_students_features, course_registration_features, AVAILABLE_FEATURES, STUDENT_FEATURES, PROFILE_FEATURES
class TestAnalyticsBasic(TestCase): class TestAnalyticsBasic(TestCase):
...@@ -42,3 +43,34 @@ class TestAnalyticsBasic(TestCase): ...@@ -42,3 +43,34 @@ class TestAnalyticsBasic(TestCase):
def test_available_features(self): def test_available_features(self):
self.assertEqual(len(AVAILABLE_FEATURES), len(STUDENT_FEATURES + PROFILE_FEATURES)) self.assertEqual(len(AVAILABLE_FEATURES), len(STUDENT_FEATURES + PROFILE_FEATURES))
self.assertEqual(set(AVAILABLE_FEATURES), set(STUDENT_FEATURES + PROFILE_FEATURES)) self.assertEqual(set(AVAILABLE_FEATURES), set(STUDENT_FEATURES + PROFILE_FEATURES))
def test_course_registration_features(self):
query_features = ['code', 'course_id', 'transaction_group_name', 'created_by', 'redeemed_by']
for i in range(5):
course_code = CourseRegistrationCode(
code="test_code{}".format(i), course_id=self.course_key.to_deprecated_string(),
transaction_group_name='TestName', created_by=self.users[0]
)
course_code.save()
order = Order(user=self.users[0], status='purchased')
order.save()
registration_code_redemption = RegistrationCodeRedemption(
order=order, registration_code_id=1, redeemed_by=self.users[0]
)
registration_code_redemption.save()
registration_codes = CourseRegistrationCode.objects.all()
course_registration_list = course_registration_features(query_features, registration_codes, csv_type='download')
self.assertEqual(len(course_registration_list), len(registration_codes))
for course_registration in course_registration_list:
self.assertEqual(set(course_registration.keys()), set(query_features))
self.assertIn(course_registration['code'], [registration_code.code for registration_code in registration_codes])
self.assertIn(
course_registration['course_id'],
[registration_code.course_id.to_deprecated_string() for registration_code in registration_codes]
)
self.assertIn(
course_registration['transaction_group_name'],
[registration_code.transaction_group_name for registration_code in registration_codes]
)
...@@ -429,3 +429,6 @@ ADVANCED_SECURITY_CONFIG = ENV_TOKENS.get('ADVANCED_SECURITY_CONFIG', {}) ...@@ -429,3 +429,6 @@ ADVANCED_SECURITY_CONFIG = ENV_TOKENS.get('ADVANCED_SECURITY_CONFIG', {})
##### GOOGLE ANALYTICS IDS ##### ##### GOOGLE ANALYTICS IDS #####
GOOGLE_ANALYTICS_ACCOUNT = AUTH_TOKENS.get('GOOGLE_ANALYTICS_ACCOUNT') GOOGLE_ANALYTICS_ACCOUNT = AUTH_TOKENS.get('GOOGLE_ANALYTICS_ACCOUNT')
GOOGLE_ANALYTICS_LINKEDIN = AUTH_TOKENS.get('GOOGLE_ANALYTICS_LINKEDIN') GOOGLE_ANALYTICS_LINKEDIN = AUTH_TOKENS.get('GOOGLE_ANALYTICS_LINKEDIN')
#### Course Registration Code length ####
REGISTRATION_CODE_LENGTH = ENV_TOKENS.get('REGISTRATION_CODE_LENGTH', 8)
...@@ -291,6 +291,9 @@ FEATURES['ENABLE_SHOPPING_CART'] = True ...@@ -291,6 +291,9 @@ FEATURES['ENABLE_SHOPPING_CART'] = True
### This enables the Metrics tab for the Instructor dashboard ########### ### This enables the Metrics tab for the Instructor dashboard ###########
FEATURES['CLASS_DASHBOARD'] = True FEATURES['CLASS_DASHBOARD'] = True
### This settings is for the course registration code length ############
REGISTRATION_CODE_LENGTH = 8
##################################################################### #####################################################################
# Lastly, see if the developer has any local overrides. # Lastly, see if the developer has any local overrides.
try: try:
......
### ###
E-Commerce Download Section E-Commerce Section
### ###
# Ecommerce Purchase Download Section
class ECommerce class ECommerce
# E-Commerce Section
constructor: (@$section) -> constructor: (@$section) ->
# attach self to html so that instructor_dashboard.coffee can find # attach self to html so that instructor_dashboard.coffee can find
# this object to call event handlers like 'onClickTitle' # this object to call event handlers like 'onClickTitle'
@$section.data 'wrapper', @ @$section.data 'wrapper', @
# gather elements # gather elements
@$list_purchase_csv_btn = @$section.find("input[name='list-purchase-transaction-csv']'") @$list_purchase_csv_btn = @$section.find("input[name='list-purchase-transaction-csv']'")
@$transaction_group_name = @$section.find("input[name='transaction_group_name']'")
@$transaction_group_name = @$section.find("input[name='transaction_group_name']'")
@$download_transaction_group_name = @$section.find("input[name='transaction_group_name']'")
@$active_transaction_group_name = @$section.find("input[name='transaction_group_name']'")
@$spent_transaction_group_name = @$section.find('input[name="course_registration_code_number"]')
@$generate_registration_code_form = @$section.find("form#course_codes_number")
@$download_registration_codes_form = @$section.find("form#download_registration_codes")
@$active_registration_codes_form = @$section.find("form#active_registration_codes")
@$spent_registration_codes_form = @$section.find("form#spent_registration_codes")
@$coupoon_error = @$section.find('#coupon-error')
@$course_code_error = @$section.find('#code-error')
# attach click handlers # attach click handlers
# this handler binds to both the download # this handler binds to both the download
# and the csv button # and the csv button
...@@ -20,17 +32,72 @@ class ECommerce ...@@ -20,17 +32,72 @@ class ECommerce
url += '/csv' url += '/csv'
location.href = url location.href = url
@$download_registration_codes_form.submit (e) =>
@$course_code_error.attr('style', 'display: none')
@$coupoon_error.attr('style', 'display: none')
return true
@$active_registration_codes_form.submit (e) =>
@$course_code_error.attr('style', 'display: none')
@$coupoon_error.attr('style', 'display: none')
return true
@$spent_registration_codes_form.submit (e) =>
@$course_code_error.attr('style', 'display: none')
@$coupoon_error.attr('style', 'display: none')
return true
@$generate_registration_code_form.submit (e) =>
@$course_code_error.attr('style', 'display: none')
@$coupoon_error.attr('style', 'display: none')
group_name = @$transaction_group_name.val()
if group_name == ''
@$course_code_error.html('Please Enter the Transaction Group Name').show()
return false
if ($.isNumeric(group_name))
@$course_code_error.html('Please Enter the non-numeric value for Transaction Group Name').show()
return false;
registration_codes = @$course_registration_number.val();
if (isInt(registration_codes) && $.isNumeric(registration_codes))
if (parseInt(registration_codes) > 1000 )
@$course_code_error.html('You can only generate 1000 Registration Codes at a time').show()
return false;
if (parseInt(registration_codes) == 0 )
@$course_code_error.html('Please Enter the Value greater than 0 for Registration Codes').show()
return false;
return true;
else
@$course_code_error.html('Please Enter the Integer Value for Registration Codes').show()
return false;
# handler for when the section title is clicked. # handler for when the section title is clicked.
onClickTitle: -> onClickTitle: ->
@clear_display() @clear_display()
# handler for when the section title is clicked.
onClickTitle: -> @clear_display()
# handler for when the section is closed
onExit: -> @clear_display()
clear_display: -> clear_display: ->
@$course_code_error.attr('style', 'display: none')
@$coupoon_error.attr('style', 'display: none')
@$course_registration_number.val('')
@$transaction_group_name.val('')
@$download_transaction_group_name.val('')
@$active_transaction_group_name.val('')
@$spent_transaction_group_name.val('')
isInt = (n) -> return n % 1 == 0;
# Clear any generated tables, warning messages, etc.
# export for use # export for use
# create parent namespaces if they do not already exist. # create parent namespaces if they do not already exist.
_.defaults window, InstructorDashboard: {} _.defaults window, InstructorDashboard: {}
_.defaults window.InstructorDashboard, sections: {} _.defaults window.InstructorDashboard, sections: {}
_.defaults window.InstructorDashboard.sections, _.defaults window.InstructorDashboard.sections,
ECommerce: ECommerce ECommerce: ECommerce
\ No newline at end of file
...@@ -829,6 +829,19 @@ input[name="subject"] { ...@@ -829,6 +829,19 @@ input[name="subject"] {
.content{ .content{
padding: 0 !important; padding: 0 !important;
} }
input[name="course_registration_code_number"] {
margin-right: 10px;
height: 34px;
width: 258px;
border-radius: 3px;
}
input[name="transaction_group_name"], input[name="download_transaction_group_name"],
input[name="active_transaction_group_name"], input[name="spent_transaction_group_name"] {
margin-right: 8px;
height: 36px;
width: 300px;
border-radius: 3px;
}
.coupons-table { .coupons-table {
width: 100%; width: 100%;
tr:nth-child(even){ tr:nth-child(even){
......
...@@ -4,8 +4,46 @@ ...@@ -4,8 +4,46 @@
<%include file="edit_coupon_modal.html" args="section_data=section_data" /> <%include file="edit_coupon_modal.html" args="section_data=section_data" />
<div class="ecommerce-wrapper"> <div class="ecommerce-wrapper">
<h3 class="coupon-errors" id="code-error"></h3>
<h2>Registration Codes</h2>
<p>Enter the transaction group name and number of registration codes that you want to generate. Click to generate a CSV :</p>
<p>
<form action="${ section_data['generate_registration_code_csv_url'] }" id="course_codes_number" method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="${ csrf_token }">
<input type="text" name="transaction_group_name" placeholder="Transaction Group Name"/>
<input type="text" name="course_registration_code_number" placeholder="Number of Registration Codes" maxlength="4"/>
<input type="submit" name="generate-registration-codes-csv" value="${_("Generate Registration Codes")}" data-csv="true">
</form>
</p>
%if section_data['access']['finance_admin'] is True: <p>Click to generate a CSV file of all Course Registrations Codes:</p>
<p>
<form action="${ section_data['get_registration_code_csv_url'] }" id="download_registration_codes" method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="${ csrf_token }">
<input type="text" name="download_transaction_group_name" placeholder="Transaction Group Name (Optional)"/>
<input type="submit" name="list-registration-codes-csv" value="${_("Download Registration Codes")}" data-csv="true">
</form>
</p>
<p>Click to generate a CSV file of all Active Course Registrations Codes:</p>
<p>
<form action="${ section_data['active_registration_code_csv_url'] }" id="active_registration_codes" method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="${ csrf_token }">
<input type="text" name="active_transaction_group_name" placeholder="Transaction Group Name (Optional)"/>
<input type="submit" name="active-registration-codes-csv" value="${_("Active Registration Codes")}" data-csv="true">
</form>
</p>
<p>Click to generate a CSV file of all Spent Course Registrations Codes:</p>
<p>
<form action="${ section_data['spent_registration_code_csv_url'] }" id="spent_registration_codes" method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="${ csrf_token }">
<input type="text" name="spent_transaction_group_name" placeholder="Transaction Group Name (Optional)"/>
<input type="submit" name="spent-registration-codes-csv" value="${_("Spent Registration Codes")}" data-csv="true">
</form>
</p>
<hr>
%if section_data['access']['finance_admin'] is True:
<h2>${_("Transactions")}</h2> <h2>${_("Transactions")}</h2>
%if section_data['total_amount'] is not None: %if section_data['total_amount'] is not None:
......
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