Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calypso ginkgo stage #558

Merged
merged 12 commits into from
Sep 6, 2018
1 change: 1 addition & 0 deletions lms/djangoapps/commerce/api/v0/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def post(self, request, *args, **kwargs):
"""
Attempt to create the basket and enroll the user.
"""
return DetailResponse('Method not allowed', status=HTTP_406_NOT_ACCEPTABLE)
user = request.user
valid, course_key, error = self._is_data_valid(request)
if not valid:
Expand Down
8 changes: 5 additions & 3 deletions lms/djangoapps/courseware/views/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.generic import View
from django.utils.translation import ugettext as _
from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.keys import CourseKey, UsageKey
from web_fragments.fragment import Fragment

from edxmako.shortcuts import render_to_response, render_to_string
Expand Down Expand Up @@ -558,7 +558,7 @@ def check_prerequisite(request, course_id):
if not prereq:
return JsonResponse({'next': True, 'msg': _('Go to next subsection')})

for i in range(10):
for i in range(30):
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
course_key,
request.user,
Expand Down Expand Up @@ -597,7 +597,9 @@ def check_prerequisite(request, course_id):
'location': prereq[0],
}
)
msg = _('Subsection blocked by <a href="{}">this</a> prerequisite.').format(url)
descriptor = modulestore().get_item(UsageKey.from_string(prereq[0]))
name = descriptor.display_name_with_default
msg = _('In order to proceed, you must successfully complete section "<a href="{}">{}</a>" first.').format(url, name)
return JsonResponse({'next': False, 'msg': msg, 'url': url})

next_url = reverse(
Expand Down
17 changes: 16 additions & 1 deletion lms/djangoapps/courseware/views/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
from xmodule.modulestore.exceptions import ItemNotFoundError, NoPathToItem
from xmodule.tabs import CourseTabList
from xmodule.x_module import STUDENT_VIEW
from learner_dashboard.views import get_course_ids

from ..entrance_exams import user_can_skip_entrance_exam
from ..module_render import get_module, get_module_by_usage_id, get_module_for_descriptor
Expand Down Expand Up @@ -836,12 +837,26 @@ def program_marketing(request, program_uuid):
if not program_data:
raise Http404

course_ids = get_course_ids(program_data['courses'])
total_courses_count = len(course_ids)

started_courses_count = CourseEnrollment.enrollments_for_user(request.user).filter(course_id__in=course_ids).count()

program = ProgramMarketingDataExtender(program_data, request.user).extend()
program['type_slug'] = slugify(program['type'])
skus = program.get('skus')
ecommerce_service = EcommerceService()

context = {'program': program}
price = program.get('price', '0.00')
if price != '0.00' and started_courses_count:
program['full_program_price'] = (float(price) / total_courses_count) * (total_courses_count - started_courses_count)
elif price != '0.00':
program['full_program_price'] = float(price)

context = {
'program': program,
'add_to_cart_url': reverse('add_programm_to_cart', kwargs={'uuid': program_data['uuid']})
}

if program.get('is_learner_eligible_for_one_click_purchase') and skus:
context['buy_button_href'] = ecommerce_service.get_checkout_page_url(*skus)
Expand Down
35 changes: 17 additions & 18 deletions lms/djangoapps/learner_dashboard/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,22 @@
get_certificates,
get_program_marketing_url
)
from openedx.core.djangoapps.programs.views import program_listing as base_program_listing
from openedx.core.djangoapps.user_api.preferences.api import get_user_preferences
from opaque_keys.edx.keys import CourseKey
from student.models import CourseEnrollment


@login_required
@require_GET
def program_listing(request):
"""View a list of programs in which the user is engaged."""
programs_config = ProgramsApiConfig.current()
if not programs_config.enabled:
raise Http404
return base_program_listing(request, request.user)

meter = ProgramProgressMeter(request.user)

context = {
'disable_courseware_js': True,
'marketing_url': get_program_marketing_url(programs_config),
'nav_hidden': True,
'programs': meter.engaged_programs,
'progress': meter.progress(),
'show_program_listing': programs_config.enabled,
'uses_pattern_library': True,
}

return render_to_response('learner_dashboard/programs.html', context)
def get_course_ids(courses):
course_ids = []
for course in courses:
course_ids.extend([CourseKey.from_string(c['key']) for c in course['course_runs']])
return course_ids


@login_required
Expand All @@ -58,7 +50,13 @@ def program_details(request, program_uuid):
course_data = meter.progress(programs=[program_data], count_only=False)[0]
certificate_data = get_certificates(request.user, program_data)

program_data.pop('courses')
course_ids = get_course_ids(program_data.pop('courses'))
total_courses_count = len(course_ids)

started_courses_count = CourseEnrollment.enrollments_for_user(request.user).filter(course_id__in=course_ids).count()

if started_courses_count:
program_data['price'] = '%.2f' % ((float(program_data['price']) / total_courses_count) * (total_courses_count - started_courses_count))

urls = {
'program_listing_url': reverse('program_listing_view'),
Expand All @@ -78,6 +76,7 @@ def program_details(request, program_uuid):
'program_data': program_data,
'course_data': course_data,
'certificate_data': certificate_data,
'add_to_cart_url': reverse('add_programm_to_cart', kwargs={'uuid': program_data['uuid']}),
}

return render_to_response('learner_dashboard/program_details.html', context)
3 changes: 2 additions & 1 deletion lms/djangoapps/shoppingcart/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
url(r'^donation/$', 'donate', name='donation'),
url(r'^csv_report/$', 'csv_report', name='payment_csv_report'),
# These following URLs are only valid if the ENABLE_SHOPPING_CART feature flag is set
url(r'^$', 'show_cart'),
url(r'^$', 'show_cart', name='show_cart'),
url(r'^clear/$', 'clear_cart'),
url(r'^remove_item/$', 'remove_item'),
url(r'^add/course/{}/$'.format(settings.COURSE_ID_PATTERN), 'add_course_to_cart', name='add_course_to_cart'),
Expand All @@ -19,6 +19,7 @@
url(r'^reset_code_redemption/$', 'reset_code_redemption'),
url(r'^billing_details/$', 'billing_details', name='billing_details'),
url(r'^verify_cart/$', 'verify_cart'),
url(r'^add/program/(?P<uuid>[0-9a-f-]+)/', 'add_program_to_cart', name='add_programm_to_cart'),
)

if settings.FEATURES.get('ENABLE_PAYMENT_FAKE'):
Expand Down
46 changes: 46 additions & 0 deletions lms/djangoapps/shoppingcart/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@
render_purchase_form_html
)

from openedx.core.djangoapps.programs.models import ProgramsApiConfig
from openedx.core.djangoapps.programs.utils import ProgramProgressMeter

log = logging.getLogger("shoppingcart")
AUDIT_LOG = logging.getLogger("audit")

Expand Down Expand Up @@ -135,6 +138,49 @@ def add_course_to_cart(request, course_id):
return HttpResponse(_("Course added to cart."))


@require_POST
def add_program_to_cart(request, uuid):
if not request.user.is_authenticated():
return JsonResponse({'success': False, 'msg': _('You must be logged-in to add to a shopping cart')})

cart = Order.get_cart_for_user(request.user)

programs_config = ProgramsApiConfig.current()
if not programs_config.enabled:
raise JsonResponse({'success': False, 'msg': _('The program config you requested does not exist.')})

meter = ProgramProgressMeter(request.user, uuid=uuid)
program_data = meter.programs[0]

if not program_data:
return JsonResponse({'success': False, 'msg': _('The program you requested does not exist.')})

course_keys = []
for course in program_data['courses']:
for c in course['course_runs']:
course_key = SlashSeparatedCourseKey.from_deprecated_string(c['key'])
if course_key and course_key not in course_keys:
course_keys.append(course_key)

course_price = float(program_data['price']) / len(course_keys)

for course_key in course_keys:
try:
paid_course_item = PaidCourseRegistration.add_to_order(cart, course_key)
except CourseDoesNotExistException:
log.warning(u'A non-existent course "%s" is skipped.', course_key.to_deprecated_string())
except AlreadyEnrolledInCourseException:
log.info(u'Already enrolled course "%s" is skipped.', course_key.to_deprecated_string())
except ItemAlreadyInCartException:
paid_course_item = cart.orderitem_set.get(paidcourseregistration__course_id=course_key)

paid_course_item.unit_cost = course_price
paid_course_item.currency = program_data['currency']
paid_course_item.save()

return JsonResponse({'success': True, 'msg': 'Program added to cart.', 'redirect_url': reverse('show_cart')})


@login_required
@enforce_shopping_cart_enabled
def update_user_cart(request):
Expand Down
1 change: 1 addition & 0 deletions lms/static/js/learner_dashboard/models/program_model.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
subtitle: data.subtitle,
authoring_organizations: data.authoring_organizations,
detailUrl: data.detail_url,
marketingUrl: data.marketing_page_url,
xsmallBannerUrl: data.banner_image['x-small'].url,
smallBannerUrl: data.banner_image.small.url,
mediumBannerUrl: data.banner_image.medium.url,
Expand Down
2 changes: 2 additions & 0 deletions lms/static/js/learner_dashboard/views/program_card_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
uuid: this.model.get('uuid')
});
}
this.isMarketing = data.context.isMarketing;
this.render();
},

Expand All @@ -44,6 +45,7 @@
return gettext(org.key);
}),
data = $.extend(
{isMarketing: this.isMarketing},
this.model.toJSON(),
this.getProgramProgress(),
{orgList: orgList.join(' ')}
Expand Down
22 changes: 21 additions & 1 deletion lms/static/js/learner_dashboard/views/program_details_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
'js/learner_dashboard/views/collection_list_view',
'js/learner_dashboard/views/course_card_view',
'js/learner_dashboard/views/program_details_sidebar_view',
'text!../../../templates/learner_dashboard/program_details_view.underscore'
'text!../../../templates/learner_dashboard/program_details_view.underscore',
'jquery.ui'
],
function(
Backbone,
Expand All @@ -30,6 +31,10 @@

tpl: HtmlUtils.template(pageTpl),

events: {
'click button.buy-program': 'addProgramToCart'
},

initialize: function(options) {
this.options = options;
this.programModel = new Backbone.Model(this.options.programData);
Expand Down Expand Up @@ -106,6 +111,21 @@
courseModel: this.courseData,
certificateCollection: this.certificateCollection
});
},
addProgramToCart: function() {
var addToCartUrl = this.options.addToCartUrl;
$.ajax({
url: addToCartUrl,
method: 'POST',
success: function(data) {
if (data.success) {
window.location = data.redirect_url;
} else {
$("#shoppingcart-popup #msg").html(data.msg);
$("#shoppingcart-popup").dialog({width: 400});
}
}
});
}
});
}
Expand Down
2 changes: 1 addition & 1 deletion lms/static/sass/elements/_program-card.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
.program-card {
@include span(12);
border: 1px solid $border-color-l3;
border-bottom: none;
/* border-bottom: none; */
margin-bottom: $baseline;
position: relative;

Expand Down
10 changes: 5 additions & 5 deletions lms/templates/courseware/courseware.html
Original file line number Diff line number Diff line change
Expand Up @@ -277,10 +277,10 @@
}
});

$(window).focus(function() {
$("body").show();
}).blur(function() {
$("body").hide();
});
// $(window).focus(function() {
// $("body").show();
// }).blur(function() {
// $("body").hide();
// });
});
</script>
58 changes: 46 additions & 12 deletions lms/templates/courseware/program_marketing.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,14 @@
description_max_length = 250
%>

<%static:css group='style-main-v2'/>
<%static:css group='style-main-v1'/>
<%static:css group='style-learner-dashboard'/>

<%block name="headextra">
<%static:css group='style-course-vendor'/>
<%static:css group='style-course'/>
</%block>

<%block name="js_extra">
<script src="${static.url('js/slick.min.js')}"></script>
<script src="${static.url('js/leanModal.js')}"></script>
Expand Down Expand Up @@ -75,18 +80,20 @@ <h1 class="banner-title">${title}</h1>
</a>
% endif
</div>
<div class="col col-12 md-col-4 lg-col-3" id="course-trailer">
<a href="#video-modal" class="media trailer-link visible-sm" rel="leanModal">
<i class="fa fa-play-circle-o" aria-hidden="true"></i>
<span>${_('View Program Trailer')}</span>
</a>
<div class="hidden-sm">
<button href="#video-modal" class="media btn-play" rel="leanModal">
<span class="sr">Play the ${title} program video</span>
<i class="icon fa fa-2x fa-play" alt="${_('Play')}"></i>
</button>
% if video_url:
<div class="col col-12 md-col-4 lg-col-3" id="course-trailer">
<a href="#video-modal" class="media trailer-link visible-sm" rel="leanModal">
<i class="fa fa-play-circle-o" aria-hidden="true"></i>
<span>${_('View Program Trailer')}</span>
</a>
<div class="hidden-sm">
<button href="#video-modal" class="media btn-play" rel="leanModal">
<span class="sr">Play the ${title} program video</span>
<i class="icon fa fa-2x fa-play" alt="${_('Play')}"></i>
</button>
</div>
</div>
</div>
% endif
</div>
</div>
</div>
Expand Down Expand Up @@ -391,6 +398,11 @@ <h3 class="hd">
program_type=program_type
)}
</h3>
% if (program['full_program_price'] and program['full_program_price'] != '0.00'):
<button class="btn buy-program" data-url="${add_to_cart_url}">
${Text(_('Buy all courses for ${full_program_price}')).format(full_program_price=full_program_price)}
</button>
% endif
<div class="course-cards">
% for course in courses:
% if course.get('course_runs'):
Expand Down Expand Up @@ -444,3 +456,25 @@ <h4 class="hd title">
</div>
</section>
</div>
<div id="shoppingcart-popup">
<p id='msg'></p>
</div>
<script type="text/javascript">
$(document).ready(function() {
$('button.buy-program').click(function(e) {
var addToCartUrl = $(e.target).data('url');
$.ajax({
url: addToCartUrl,
method: 'POST',
success: function(data) {
if (data.success) {
window.location = data.redirect_url;
} else {
$("#shoppingcart-popup #msg").html(data.msg);
$("#shoppingcart-popup").dialog({width: 400});
}
}
});
});
});
</script>
Loading