feat: create feature flag for PLS custom pacing
This commit is contained in:
@@ -67,3 +67,33 @@ REDIRECT_TO_LIBRARY_AUTHORING_MICROFRONTEND = LegacyWaffleFlag(
|
||||
flag_name='library_authoring_mfe',
|
||||
module_name=__name__,
|
||||
)
|
||||
|
||||
|
||||
# .. toggle_name: studio.pages_and_resources_mfe
|
||||
# .. toggle_implementation: CourseWaffleFlag
|
||||
# .. toggle_default: False
|
||||
# .. toggle_description: Waffle flag to link existing studio views to the new Pages and Resources experience.
|
||||
# .. toggle_use_cases: temporary, open_edx
|
||||
# .. toggle_creation_date: 2021-05-24
|
||||
# .. toggle_target_removal_date: 2021-12-31
|
||||
# .. toggle_warnings: Also set settings.COURSE_AUTHORING_MICROFRONTEND_URL.
|
||||
# .. toggle_tickets: None
|
||||
ENABLE_PAGES_AND_RESOURCES_MICROFRONTEND = CourseWaffleFlag(
|
||||
waffle_namespace=waffle_flags(),
|
||||
flag_name='pages_and_resources_mfe',
|
||||
module_name=__name__,
|
||||
)
|
||||
|
||||
# .. toggle_name: studio.custom_pls
|
||||
# .. toggle_implementation: CourseWaffleFlag
|
||||
# .. toggle_default: False (except for SuperUsers)
|
||||
# .. toggle_description: Waffle flag to enable custom pacing for PLS
|
||||
# .. toggle_use_cases: temporary
|
||||
# .. toggle_creation_date: 2021-06-15
|
||||
# .. toggle_target_removal_date: 2021-12-31
|
||||
# .. toggle_warnings: None
|
||||
# .. toggle_tickets: None
|
||||
CUSTOM_PLS = CourseWaffleFlag(WAFFLE_NAMESPACE, 'custom_pls', module_name=__name__,)
|
||||
|
||||
def custom_pls_is_active(course_key):
|
||||
return CUSTOM_PLS.is_enabled(course_key)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
from functools import partial
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
@@ -294,7 +294,7 @@ describe('CourseOutlinePage', function() {
|
||||
TemplateHelpers.installTemplates([
|
||||
'course-outline', 'xblock-string-field-editor', 'modal-button',
|
||||
'basic-modal', 'course-outline-modal', 'release-date-editor',
|
||||
'due-date-editor', 'grading-editor', 'publish-editor',
|
||||
'due-date-editor', 'self-paced-due-date-editor', 'grading-editor', 'publish-editor',
|
||||
'staff-lock-editor', 'unit-access-editor', 'content-visibility-editor',
|
||||
'settings-modal-tabs', 'timed-examination-preference-editor', 'access-editor',
|
||||
'show-correctness-editor', 'highlights-editor', 'highlights-enable-editor',
|
||||
|
||||
@@ -17,7 +17,8 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
AbstractEditor, BaseDateEditor,
|
||||
ReleaseDateEditor, DueDateEditor, GradingEditor, PublishEditor, AbstractVisibilityEditor,
|
||||
StaffLockEditor, UnitAccessEditor, ContentVisibilityEditor, TimedExaminationPreferenceEditor,
|
||||
AccessEditor, ShowCorrectnessEditor, HighlightsEditor, HighlightsEnableXBlockModal, HighlightsEnableEditor;
|
||||
AccessEditor, ShowCorrectnessEditor, HighlightsEditor, HighlightsEnableXBlockModal, HighlightsEnableEditor,
|
||||
SelfPacedDueDateEditor;
|
||||
|
||||
CourseOutlineXBlockModal = BaseModal.extend({
|
||||
events: _.extend({}, BaseModal.prototype.events, {
|
||||
@@ -74,6 +75,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
|
||||
event.preventDefault();
|
||||
requestData = this.getRequestData();
|
||||
console.log(requestData)
|
||||
if (!_.isEqual(requestData, {metadata: {}})) {
|
||||
XBlockViewUtils.updateXBlockFields(this.model, requestData, {
|
||||
success: this.options.onSave
|
||||
@@ -389,6 +391,35 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
}
|
||||
});
|
||||
|
||||
SelfPacedDueDateEditor = BaseDateEditor.extend({
|
||||
fieldName: 'due',
|
||||
templateName: 'self-paced-due-date-editor',
|
||||
className: 'modal-section-content has-actions due-date-input grading-due-date',
|
||||
|
||||
getValue: function() {
|
||||
return this.$('#due_date').val();
|
||||
},
|
||||
|
||||
clearValue: function(event) {
|
||||
event.preventDefault();
|
||||
this.$('#due_date').val('');
|
||||
},
|
||||
|
||||
getRequestData: function() {
|
||||
let currentDate = parseInt(this.getValue())
|
||||
if (parseInt(this.getValue())){
|
||||
currentDate = new Date()
|
||||
currentDate.setDate(currentDate.getDate() + parseInt(this.getValue())*7)
|
||||
};
|
||||
// due_num_weeks
|
||||
return {
|
||||
metadata: {
|
||||
due: currentDate
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
ReleaseDateEditor = BaseDateEditor.extend({
|
||||
fieldName: 'start',
|
||||
templateName: 'release-date-editor',
|
||||
@@ -1078,6 +1109,10 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
tabs[0].editors = [ReleaseDateEditor, GradingEditor, DueDateEditor];
|
||||
tabs[1].editors = [ContentVisibilityEditor, ShowCorrectnessEditor];
|
||||
|
||||
if (course.get('self_paced')) {
|
||||
tabs[0].editors.push(SelfPacedDueDateEditor)
|
||||
}
|
||||
|
||||
if (options.enable_proctored_exams || options.enable_timed_exams) {
|
||||
advancedTab.editors.push(TimedExaminationPreferenceEditor);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ from django.urls import reverse
|
||||
|
||||
<%block name="header_extras">
|
||||
<link rel="stylesheet" type="text/css" href="${static.url('js/vendor/timepicker/jquery.timepicker.css')}" />
|
||||
% for template_name in ['course-outline', 'xblock-string-field-editor', 'basic-modal', 'modal-button', 'course-outline-modal', 'due-date-editor', 'release-date-editor', 'grading-editor', 'publish-editor', 'staff-lock-editor', 'unit-access-editor', 'content-visibility-editor', 'verification-access-editor', 'timed-examination-preference-editor', 'access-editor', 'settings-modal-tabs', 'show-correctness-editor', 'highlights-editor', 'highlights-enable-editor', 'course-highlights-enable']:
|
||||
% for template_name in ['course-outline', 'xblock-string-field-editor', 'basic-modal', 'modal-button', 'course-outline-modal', 'due-date-editor', 'self-paced-due-date-editor', 'release-date-editor', 'grading-editor', 'publish-editor', 'staff-lock-editor', 'unit-access-editor', 'content-visibility-editor', 'verification-access-editor', 'timed-examination-preference-editor', 'access-editor', 'settings-modal-tabs', 'show-correctness-editor', 'highlights-editor', 'highlights-enable-editor', 'course-highlights-enable']:
|
||||
<script type="text/template" id="${template_name}-tpl">
|
||||
<%static:include path="js/${template_name}.underscore" />
|
||||
</script>
|
||||
|
||||
16
cms/templates/js/self-paced-due-date-editor.underscore
Normal file
16
cms/templates/js/self-paced-due-date-editor.underscore
Normal file
@@ -0,0 +1,16 @@
|
||||
<ul class="list-fields list-input datepair date-setter">
|
||||
<li class="field field-text field-due-date">
|
||||
<label for="due_date"><%- gettext('Due in:') %></label>
|
||||
<input type="number" id="due_date" name="due_date" value=""
|
||||
placeholder="" autocomplete="off"/> weeks
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="list-actions">
|
||||
<li class="action-item">
|
||||
<a href="#" data-tooltip="<%- gettext('Clear Grading Due Date') %>" class="clear-date action-button action-clear">
|
||||
<span class="icon fa fa-undo" aria-hidden="true"></span>
|
||||
<span class="sr"><%- gettext('Clear Grading Due Date') %></span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -1051,7 +1051,7 @@ def allowed_metadata_by_category(category):
|
||||
return {
|
||||
'vertical': [],
|
||||
'chapter': ['start'],
|
||||
'sequential': ['due', 'format', 'start', 'graded']
|
||||
'sequential': ['due', 'due_num_weeks', 'format', 'start', 'graded']
|
||||
}.get(category, ['*'])
|
||||
|
||||
|
||||
|
||||
@@ -79,6 +79,12 @@ class SequenceFields: # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
scope=Scope.settings,
|
||||
)
|
||||
|
||||
due_num_weeks = Integer(
|
||||
display_name = _("Number of Weeks Due By"),
|
||||
help=_("Enter the number of weeks the problems are due by"),
|
||||
scope = Scope.settings,
|
||||
)
|
||||
|
||||
hide_after_due = Boolean(
|
||||
display_name=_("Hide sequence content After Due Date"),
|
||||
help=_(
|
||||
@@ -195,6 +201,7 @@ class ProctoringFields:
|
||||
default=False,
|
||||
scope=Scope.settings,
|
||||
)
|
||||
|
||||
|
||||
def _get_course(self):
|
||||
"""
|
||||
|
||||
@@ -51,6 +51,7 @@ SUPPORTED_FIELDS = [
|
||||
SupportedFieldType('format'),
|
||||
SupportedFieldType('start'),
|
||||
SupportedFieldType('due'),
|
||||
SupportedFieldType('due_num_weeks'),
|
||||
SupportedFieldType('contains_gated_content'),
|
||||
SupportedFieldType('has_score'),
|
||||
SupportedFieldType('has_scheduled_content'),
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
"""Signal handlers for writing course dates into edx_when."""
|
||||
|
||||
|
||||
from datetime import timedelta, datetime
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
from cms.djangoapps.contentstore.config.waffle import custom_pls_is_active
|
||||
from django.dispatch import receiver
|
||||
from edx_when.api import FIELDS_TO_EXTRACT, set_dates_for_course
|
||||
|
||||
@@ -27,6 +30,11 @@ def _field_values(fields, xblock):
|
||||
if field_name not in xblock.fields:
|
||||
continue
|
||||
field = xblock.fields[field_name]
|
||||
if field_name == 'due':
|
||||
print("THIS IS THE FIELD ", field)
|
||||
print(xblock)
|
||||
result[field.name] = field.read_from(xblock)
|
||||
continue
|
||||
if field.scope == Scope.settings and field.is_set_on(xblock):
|
||||
try:
|
||||
result[field.name] = field.read_from(xblock)
|
||||
@@ -83,7 +91,34 @@ def extract_dates_from_course(course):
|
||||
Extract all dates from the supplied course.
|
||||
"""
|
||||
log.info('Extracting course dates for %s', course.id)
|
||||
if course.self_paced:
|
||||
|
||||
if course.self_paced and custom_pls_is_active(course.id):
|
||||
print("This is self paced ")
|
||||
date_items = []
|
||||
store = modulestore()
|
||||
with store.branch_setting(ModuleStoreEnum.Branch.published_only, course.id):
|
||||
items = store.get_items(course.id)
|
||||
log.info('Extracting dates from %d items in %s', len(items), course.id)
|
||||
print("B4 the items sections")
|
||||
# new_fields_to_extract = FIELDS_TO_EXTRACT + ('due_num_weeks',)
|
||||
# print("THe new fields to extract ", new_fields_to_extract)
|
||||
for item in items:
|
||||
metadata = _field_values(FIELDS_TO_EXTRACT, item)
|
||||
print("THIS IS THE METADATA ", metadata)
|
||||
metadata['due'] = datetime.datetime.now() - metadata['due']
|
||||
|
||||
# print("TYPE OF DATES: ", metadata)
|
||||
# print("RIGHT NOW, ", datetime.datetime.now())
|
||||
# print(metadata['due'])
|
||||
# metadata['due'] = datetime.datetime.now() - metadata['due']
|
||||
# print('metadata due: ', metadata['due'])
|
||||
metadata.pop('due_num_weeks',None)
|
||||
# print("THIS IS THE DUE DATE: ", metadata['due'])
|
||||
date_items.append((item.location, metadata))
|
||||
# date_items.append((item.location, _field_values(FIELDS_TO_EXTRACT, item)))
|
||||
print("Here are the date items: ", date_items)
|
||||
|
||||
elif course.self_paced and not custom_pls_is_active(course.id):
|
||||
metadata = _field_values(FIELDS_TO_EXTRACT, course)
|
||||
# self-paced courses may accidentally have a course due date
|
||||
metadata.pop('due', None)
|
||||
@@ -94,6 +129,7 @@ def extract_dates_from_course(course):
|
||||
# unless that item already has a relative date set
|
||||
for _, section, weeks_to_complete in spaced_out_sections(course):
|
||||
section_date_items = []
|
||||
print("THESE IS THE WEEKS TO COMPLETE ,", weeks_to_complete)
|
||||
for subsection in section.get_children():
|
||||
section_date_items.extend(_gather_graded_items(subsection, weeks_to_complete))
|
||||
|
||||
@@ -108,6 +144,7 @@ def extract_dates_from_course(course):
|
||||
log.info('Extracting dates from %d items in %s', len(items), course.id)
|
||||
for item in items:
|
||||
date_items.append((item.location, _field_values(FIELDS_TO_EXTRACT, item)))
|
||||
|
||||
return date_items
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user