Commit 1ffd2355 by Clinton Blackburn Committed by Clinton Blackburn

Added support for the display and editing of audit modes

Users can interact with existing audit seats, but will not be able to create new ones.

XCOM-541
parent e8c8c0be
...@@ -174,9 +174,7 @@ class AtomicPublicationSerializer(serializers.Serializer): # pylint: disable=ab ...@@ -174,9 +174,7 @@ class AtomicPublicationSerializer(serializers.Serializer): # pylint: disable=ab
# Verify that attributes required to create a Seat are present. # Verify that attributes required to create a Seat are present.
attrs = self._flatten(product['attribute_values']) attrs = self._flatten(product['attribute_values'])
if attrs.get('certificate_type') is None: if attrs.get('id_verification_required') is None:
raise serializers.ValidationError(_(u"Products must have a certificate type."))
elif attrs.get('id_verification_required') is None:
raise serializers.ValidationError(_(u"Products must indicate whether ID verification is required.")) raise serializers.ValidationError(_(u"Products must indicate whether ID verification is required."))
# Verify that a price is present. # Verify that a price is present.
......
...@@ -89,10 +89,10 @@ define([ ...@@ -89,10 +89,10 @@ define([
* Mapping of course type to an array of course seat types. * Mapping of course type to an array of course seat types.
*/ */
courseTypeSeatMapping: { courseTypeSeatMapping: {
honor: ['honor'], honor: ['audit', 'honor'],
verified: ['honor', 'verified'], verified: ['audit', 'honor', 'verified'],
professional: ['professional'], professional: ['professional'],
credit: ['honor', 'verified', 'credit'] credit: ['audit', 'honor', 'verified', 'credit']
}, },
initialize: function () { initialize: function () {
...@@ -146,6 +146,7 @@ define([ ...@@ -146,6 +146,7 @@ define([
return (product instanceof CourseSeat) && (product.seatType === seatType); return (product instanceof CourseSeat) && (product.seatType === seatType);
}); });
// Do NOT create new audit seats
if (!seat) { if (!seat) {
seatClass = CourseUtils.getCourseSeatModel(seatType); seatClass = CourseUtils.getCourseSeatModel(seatType);
seat = new seatClass(); seat = new seatClass();
...@@ -206,6 +207,16 @@ define([ ...@@ -206,6 +207,16 @@ define([
}, this); }, this);
}, },
courseSeatTypes: function(){
var seatTypes = this.seats().map(function(seat){
return seat.getSeatType();
});
// Note (CCB): Audit is intentionally left out of this list, to avoid
// creating new audit seats (which we do not yet support).
return seatTypes.length > 0 ? seatTypes : ['honor', 'verified', 'professional'];
},
/** /**
* Save the Course using the publication endpoint. * Save the Course using the publication endpoint.
* *
......
...@@ -53,8 +53,10 @@ define([ ...@@ -53,8 +53,10 @@ define([
case 'professional': case 'professional':
case 'no-id-professional': case 'no-id-professional':
return gettext('Professional'); return gettext('Professional');
default: case 'honor':
return gettext('Honor'); return gettext('Honor');
default:
return gettext('Audit');
} }
}, },
...@@ -68,8 +70,11 @@ define([ ...@@ -68,8 +70,11 @@ define([
case 'no-id-professional': case 'no-id-professional':
return gettext('Professional Certificate'); return gettext('Professional Certificate');
default: case 'honor':
return gettext('Honor Certificate'); return gettext('Honor Certificate');
default:
return '(' + gettext('No Certificate') + ')';
} }
} }
}); });
......
...@@ -15,6 +15,10 @@ define([ ...@@ -15,6 +15,10 @@ define([
'use strict'; 'use strict';
return { return {
seatSortObj: _.invert(_.object(_.pairs([
'audit', 'honor', 'verified', 'no-id-professional', 'professional', 'credit'
]))),
/** /**
* Returns a mapping of seat types to CourseSeat classes. * Returns a mapping of seat types to CourseSeat classes.
* *
...@@ -68,6 +72,24 @@ define([ ...@@ -68,6 +72,24 @@ define([
} }
return seatType; return seatType;
},
/**
* Returns an array of CourseSeats, ordered as they should be displayed.
* @param {CourseSeat[]} seats
* @returns {CourseSeat[]}
*/
orderSeatsForDisplay: function (seats) {
return _.sortBy(seats, function (seat) {
return this.seatSortObj[seat.getSeatType()];
}, this);
},
orderSeatTypesForDisplay: function(seatTypes){
return _.sortBy(seatTypes, function (seatType) {
return this.seatSortObj[seatType];
}, this);
} }
} }
} }
......
...@@ -5,7 +5,8 @@ define([ ...@@ -5,7 +5,8 @@ define([
'underscore.string', 'underscore.string',
'moment', 'moment',
'text!templates/course_detail.html', 'text!templates/course_detail.html',
'text!templates/_course_seat.html' 'text!templates/_course_seat.html',
'utils/course_utils'
], ],
function ($, function ($,
Backbone, Backbone,
...@@ -13,7 +14,8 @@ define([ ...@@ -13,7 +14,8 @@ define([
_s, _s,
moment, moment,
CourseDetailTemplate, CourseDetailTemplate,
CourseSeatTemplate) { CourseSeatTemplate,
CourseUtils) {
'use strict'; 'use strict';
return Backbone.View.extend({ return Backbone.View.extend({
...@@ -23,24 +25,6 @@ define([ ...@@ -23,24 +25,6 @@ define([
this.listenTo(this.model, 'change', this.render); this.listenTo(this.model, 'change', this.render);
}, },
/**
* Returns an array of CourseSeat models, sorted in the order expected for display.
* @returns {CourseSeat[]}
*/
getSeats: function () {
// Returns an array of seats sorted for display
var seats,
sortObj = _.invert(_.object(_.pairs([
'audit', 'honor', 'verified', 'no-id-professional', 'professional', 'credit'
])));
seats = _.sortBy(this.model.seats(), function (seat) {
return sortObj[seat.getSeatType()];
});
return seats;
},
render: function () { render: function () {
var html, var html,
verificationDeadline = this.model.get('verification_deadline'), verificationDeadline = this.model.get('verification_deadline'),
...@@ -62,9 +46,10 @@ define([ ...@@ -62,9 +46,10 @@ define([
renderSeats: function () { renderSeats: function () {
var html = '', var html = '',
seats = CourseUtils.orderSeatsForDisplay(this.model.seats()),
$seatHolder = $('.course-seats', this.$el); $seatHolder = $('.course-seats', this.$el);
_.each(this.getSeats(), function (seat) { _.each(seats, function (seat) {
html += _.template(CourseSeatTemplate)({seat: seat, moment: moment}); html += _.template(CourseSeatTemplate)({seat: seat, moment: moment});
}); });
......
...@@ -9,11 +9,12 @@ define([ ...@@ -9,11 +9,12 @@ define([
'underscore.string', 'underscore.string',
'text!templates/course_form.html', 'text!templates/course_form.html',
'text!templates/_course_type_radio_field.html', 'text!templates/_course_type_radio_field.html',
'views/course_seat_form_fields/audit_course_seat_form_field_view',
'views/course_seat_form_fields/honor_course_seat_form_field_view', 'views/course_seat_form_fields/honor_course_seat_form_field_view',
'views/course_seat_form_fields/verified_course_seat_form_field_view', 'views/course_seat_form_fields/verified_course_seat_form_field_view',
'views/course_seat_form_fields/professional_course_seat_form_field_view', 'views/course_seat_form_fields/professional_course_seat_form_field_view',
'views/alert_view', 'views/alert_view',
'jquery-cookie' 'utils/course_utils'
], ],
function ($, function ($,
Backbone, Backbone,
...@@ -25,11 +26,12 @@ define([ ...@@ -25,11 +26,12 @@ define([
_s, _s,
CourseFormTemplate, CourseFormTemplate,
CourseTypeRadioTemplate, CourseTypeRadioTemplate,
AuditCourseSeatFormFieldView,
HonorCourseSeatFormFieldView, HonorCourseSeatFormFieldView,
VerifiedCourseSeatFormFieldView, VerifiedCourseSeatFormFieldView,
ProfessionalCourseSeatFormFieldView, ProfessionalCourseSeatFormFieldView,
AlertView, AlertView,
cookie) { CourseUtils) {
'use strict'; 'use strict';
// Extend the callbacks to work with Bootstrap. // Extend the callbacks to work with Bootstrap.
...@@ -83,19 +85,9 @@ define([ ...@@ -83,19 +85,9 @@ define([
} }
}, },
// Map course types to the available seats
courseTypeSeatMapping: {
honor: ['honor'],
verified: ['honor', 'verified'],
professional: ['professional'],
credit: ['honor', 'verified', 'credit']
},
// TODO Activate credit
courseSeatTypes: ['honor', 'verified', 'professional'],
// Map course seats to view classes // Map course seats to view classes
courseSeatViewMappings: { courseSeatViewMappings: {
audit: AuditCourseSeatFormFieldView,
honor: HonorCourseSeatFormFieldView, honor: HonorCourseSeatFormFieldView,
verified: VerifiedCourseSeatFormFieldView, verified: VerifiedCourseSeatFormFieldView,
professional: ProfessionalCourseSeatFormFieldView professional: ProfessionalCourseSeatFormFieldView
...@@ -261,6 +253,7 @@ define([ ...@@ -261,6 +253,7 @@ define([
*/ */
renderCourseSeats: function () { renderCourseSeats: function () {
var $courseSeats, var $courseSeats,
seatTypes,
$courseSeatsContainer = this.$el.find('.course-seats'), $courseSeatsContainer = this.$el.find('.course-seats'),
activeSeats = this.model.validSeatTypes(); activeSeats = this.model.validSeatTypes();
...@@ -270,7 +263,9 @@ define([ ...@@ -270,7 +263,9 @@ define([
} }
if (_.isEmpty(this.courseSeatViews)) { if (_.isEmpty(this.courseSeatViews)) {
_.each(this.courseSeatTypes, function (seatType) { seatTypes = CourseUtils.orderSeatTypesForDisplay(this.model.courseSeatTypes());
_.each(seatTypes, function (seatType) {
var view, var view,
model = this.model.getOrCreateSeat(seatType), model = this.model.getOrCreateSeat(seatType),
viewClass = this.courseSeatViewMappings[seatType]; viewClass = this.courseSeatViewMappings[seatType];
......
define([
'views/course_seat_form_fields/course_seat_form_field_view',
'text!templates/audit_course_seat_form_field.html'
],
function (CourseSeatFormFieldView,
FieldTemplate) {
'use strict';
return CourseSeatFormFieldView.extend({
certificateType: null,
idVerificationRequired: false,
seatType: 'audit',
template: _.template(FieldTemplate)
});
}
);
...@@ -57,5 +57,9 @@ ...@@ -57,5 +57,9 @@
.form-actions { .form-actions {
margin-top: spacing-vertical(large); margin-top: spacing-vertical(large);
} }
.price-label {
@extend label;
}
} }
} }
<div class="col-sm-12">
<div class="seat-type"><%= gettext('Audit') %></div>
</div>
<div class="col-sm-4">
<span class="price-label"><%= gettext('Price (in USD)') %>:</span> <span class="seat-price">$0.00</span>
<input type="hidden" name="price" value="0">
<input type="hidden" name="certificate_type" value="">
<input type="hidden" name="id_verification_required" value="false">
</div>
<div class="col-sm-4 seat-certificate-type">
(<%= gettext('No Certificate') %>)
</div>
<div class="col-sm-4 seat-additional-info"></div>
<div class="col-sm-12"> <div class="col-sm-12">
<div class="seat-type"><%= gettext('Free (Honor)') %></div> <div class="seat-type"><%= gettext('Honor') %></div>
</div> </div>
<div class="col-sm-4"> <div class="col-sm-4">
<label class="price-label"><%= gettext('Price (in USD)') %>:</label> <span class="seat-price">$0.00</span> <span class="price-label"><%= gettext('Price (in USD)') %>:</span> <span class="seat-price">$0.00</span>
<input type="hidden" name="price" value="0"> <input type="hidden" name="price" value="0">
<input type="hidden" name="certificate_type" value="honor"> <input type="hidden" name="certificate_type" value="honor">
<input type="hidden" name="id_verification_required" value="false"> <input type="hidden" name="id_verification_required" value="false">
......
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