Fix n-click behaviour on poster. Fix unit tests. Fix handler for non_en lang for bumper. Add more tests. Fix docstrings. Fix pep8. Fix static redirection with bumper. Fix button in IE11. Add video_bumper field in bok_choy. Fix pylink violations. Update docstrings and some clean up. Rename edx_video_id in bumper tests. Fix too long lines in help text. Address ui comments. Fix bumper events. Refactor bumper-transcripts code, fix bugs, address comments. Squashed commits: Fix download transcript button. [74e0c8c] Fix quality [a759f33] Fix error, when sub contains extension. [b30755c] Revert "Add video files to host for transcripts." This reverts commit cf8a96bf84346e17b6ad57ad4cc6a27d7a9118cd. [36f038a] Add video files to host for transcripts. [23f1655] Fix pep8 and pyling issues. [0f1f9d2] Update acceptance test. [765a27d] Wait for ajax in captions. [8ae72a3] Fix logic. [063450f] Fix unit tests. [d1075fc] Fix handlers tests. [25d31ad] Update bumper_utils. [cb5f9df] Remove maxDiff. [8738b1a] Code cleanup. [87dbcb7] Fix issues with transcripts. [ec899de] Fix transcripts in serializers. [444b1fc] Fix transcripts typo. [d524cb5] Fix bumper. [f62cf22] Fix video mongo tests. [8f1b55a] Fix dispatches. [53bc308] Add more fixes. [d5e3723] Fix test_video_handlers and rename the method. [93efc23] Fix mobile tests. [740e2ae] Fix pep8 and pylint. [47cfb66] Address comments, add fixes. [4e499d9] Add fixes. [8353553] Add improvements. Updated dispatch values) . Use ddt in bumper handler tests. Move common metadata to single place. Fix style. Update docstring. Fix poster button. Improve bumper events. Fix test after rebase. Address comments. Download transcript: use def video lang, not bump. Renamed date_last_view_bumper to bumper_last_view_date. Rename do_not_show_again_bumper to bumper_... Address comments. Fix tests for download for en lang. Fix bumper logic. Update strings. Update resizer. Remove resizer. Fix unit tests. Add tests. Fix bumper events. Clean up tests. Fix pylint violations. Fix pep8 and pylint violations. Update docs and method names. Update events. Make /static/ prefix a must. Fix wrong code.
196 lines
7.2 KiB
Python
196 lines
7.2 KiB
Python
"""
|
|
Django module for Course Metadata class -- manages advanced settings and related parameters
|
|
"""
|
|
from xblock.fields import Scope
|
|
from xmodule.modulestore.django import modulestore
|
|
from django.utils.translation import ugettext as _
|
|
from django.conf import settings
|
|
|
|
|
|
class CourseMetadata(object):
|
|
'''
|
|
For CRUD operations on metadata fields which do not have specific editors
|
|
on the other pages including any user generated ones.
|
|
The objects have no predefined attrs but instead are obj encodings of the
|
|
editable metadata.
|
|
'''
|
|
# The list of fields that wouldn't be shown in Advanced Settings.
|
|
# Should not be used directly. Instead the filtered_list method should
|
|
# be used if the field needs to be filtered depending on the feature flag.
|
|
FILTERED_LIST = [
|
|
'cohort_config',
|
|
'xml_attributes',
|
|
'start',
|
|
'end',
|
|
'enrollment_start',
|
|
'enrollment_end',
|
|
'tabs',
|
|
'graceperiod',
|
|
'checklists',
|
|
'show_timezone',
|
|
'format',
|
|
'graded',
|
|
'hide_from_toc',
|
|
'pdf_textbooks',
|
|
'user_partitions',
|
|
'name', # from xblock
|
|
'tags', # from xblock
|
|
'visible_to_staff_only',
|
|
'group_access',
|
|
'pre_requisite_courses',
|
|
'entrance_exam_enabled',
|
|
'entrance_exam_minimum_score_pct',
|
|
'entrance_exam_id',
|
|
'is_entrance_exam',
|
|
'in_entrance_exam',
|
|
]
|
|
|
|
@classmethod
|
|
def filtered_list(cls):
|
|
"""
|
|
Filter fields based on feature flag, i.e. enabled, disabled.
|
|
"""
|
|
# Copy the filtered list to avoid permanently changing the class attribute.
|
|
filtered_list = list(cls.FILTERED_LIST)
|
|
|
|
# Do not show giturl if feature is not enabled.
|
|
if not settings.FEATURES.get('ENABLE_EXPORT_GIT'):
|
|
filtered_list.append('giturl')
|
|
|
|
# Do not show edxnotes if the feature is disabled.
|
|
if not settings.FEATURES.get('ENABLE_EDXNOTES'):
|
|
filtered_list.append('edxnotes')
|
|
|
|
# Do not show video_upload_pipeline if the feature is disabled.
|
|
if not settings.FEATURES.get('ENABLE_VIDEO_UPLOAD_PIPELINE'):
|
|
filtered_list.append('video_upload_pipeline')
|
|
|
|
# Do not show facebook_url if the feature is disabled.
|
|
if not settings.FEATURES.get('ENABLE_MOBILE_SOCIAL_FACEBOOK_FEATURES'):
|
|
filtered_list.append('facebook_url')
|
|
|
|
# Do not show social sharing url field if the feature is disabled.
|
|
if (not settings.FEATURES.get('DASHBOARD_SHARE_SETTINGS') or
|
|
not settings.FEATURES.get("DASHBOARD_SHARE_SETTINGS").get("CUSTOM_COURSE_URLS")):
|
|
filtered_list.append('social_sharing_url')
|
|
|
|
# Do not show teams configuration if feature is disabled.
|
|
if not settings.FEATURES.get('ENABLE_TEAMS'):
|
|
filtered_list.append('teams_configuration')
|
|
|
|
if not settings.FEATURES.get('ENABLE_VIDEO_BUMPER'):
|
|
filtered_list.append('video_bumper')
|
|
|
|
return filtered_list
|
|
|
|
@classmethod
|
|
def fetch(cls, descriptor):
|
|
"""
|
|
Fetch the key:value editable course details for the given course from
|
|
persistence and return a CourseMetadata model.
|
|
"""
|
|
result = {}
|
|
metadata = cls.fetch_all(descriptor)
|
|
for key, value in metadata.iteritems():
|
|
if key in cls.filtered_list():
|
|
continue
|
|
result[key] = value
|
|
return result
|
|
|
|
@classmethod
|
|
def fetch_all(cls, descriptor):
|
|
"""
|
|
Fetches all key:value pairs from persistence and returns a CourseMetadata model.
|
|
"""
|
|
result = {}
|
|
for field in descriptor.fields.values():
|
|
if field.scope != Scope.settings:
|
|
continue
|
|
result[field.name] = {
|
|
'value': field.read_json(descriptor),
|
|
'display_name': _(field.display_name), # pylint: disable=translation-of-non-string
|
|
'help': _(field.help), # pylint: disable=translation-of-non-string
|
|
'deprecated': field.runtime_options.get('deprecated', False)
|
|
}
|
|
return result
|
|
|
|
@classmethod
|
|
def update_from_json(cls, descriptor, jsondict, user, filter_tabs=True):
|
|
"""
|
|
Decode the json into CourseMetadata and save any changed attrs to the db.
|
|
|
|
Ensures none of the fields are in the blacklist.
|
|
"""
|
|
filtered_list = cls.filtered_list()
|
|
# Don't filter on the tab attribute if filter_tabs is False.
|
|
if not filter_tabs:
|
|
filtered_list.remove("tabs")
|
|
|
|
# Validate the values before actually setting them.
|
|
key_values = {}
|
|
|
|
for key, model in jsondict.iteritems():
|
|
# should it be an error if one of the filtered list items is in the payload?
|
|
if key in filtered_list:
|
|
continue
|
|
try:
|
|
val = model['value']
|
|
if hasattr(descriptor, key) and getattr(descriptor, key) != val:
|
|
key_values[key] = descriptor.fields[key].from_json(val)
|
|
except (TypeError, ValueError) as err:
|
|
raise ValueError(_("Incorrect format for field '{name}'. {detailed_message}").format(
|
|
name=model['display_name'], detailed_message=err.message))
|
|
|
|
return cls.update_from_dict(key_values, descriptor, user)
|
|
|
|
@classmethod
|
|
def validate_and_update_from_json(cls, descriptor, jsondict, user, filter_tabs=True):
|
|
"""
|
|
Validate the values in the json dict (validated by xblock fields from_json method)
|
|
|
|
If all fields validate, go ahead and update those values in the database.
|
|
If not, return the error objects list.
|
|
|
|
Returns:
|
|
did_validate: whether values pass validation or not
|
|
errors: list of error objects
|
|
result: the updated course metadata or None if error
|
|
"""
|
|
filtered_list = cls.filtered_list()
|
|
if not filter_tabs:
|
|
filtered_list.remove("tabs")
|
|
|
|
filtered_dict = dict((k, v) for k, v in jsondict.iteritems() if k not in filtered_list)
|
|
did_validate = True
|
|
errors = []
|
|
key_values = {}
|
|
updated_data = None
|
|
|
|
for key, model in filtered_dict.iteritems():
|
|
try:
|
|
val = model['value']
|
|
if hasattr(descriptor, key) and getattr(descriptor, key) != val:
|
|
key_values[key] = descriptor.fields[key].from_json(val)
|
|
except (TypeError, ValueError) as err:
|
|
did_validate = False
|
|
errors.append({'message': err.message, 'model': model})
|
|
|
|
# If did validate, go ahead and update the metadata
|
|
if did_validate:
|
|
updated_data = cls.update_from_dict(key_values, descriptor, user)
|
|
|
|
return did_validate, errors, updated_data
|
|
|
|
@classmethod
|
|
def update_from_dict(cls, key_values, descriptor, user):
|
|
"""
|
|
Update metadata descriptor in modulestore from key_values.
|
|
"""
|
|
for key, value in key_values.iteritems():
|
|
setattr(descriptor, key, value)
|
|
|
|
if len(key_values):
|
|
modulestore().update_item(descriptor, user.id)
|
|
|
|
return cls.fetch(descriptor)
|