diff --git a/contentcuration/contentcuration/constants/completion_criteria.py b/contentcuration/contentcuration/constants/completion_criteria.py index deee7fd62a..ffed5d7821 100644 --- a/contentcuration/contentcuration/constants/completion_criteria.py +++ b/contentcuration/contentcuration/constants/completion_criteria.py @@ -20,6 +20,53 @@ def _build_validator(): validator = _build_validator() +ALLOWED_MODELS_PER_KIND = { + content_kinds.DOCUMENT: { + completion_criteria.PAGES, + completion_criteria.TIME, + completion_criteria.APPROX_TIME, + completion_criteria.REFERENCE, + }, + content_kinds.EXERCISE: {completion_criteria.MASTERY}, + content_kinds.HTML5: { + completion_criteria.DETERMINED_BY_RESOURCE, + completion_criteria.TIME, + completion_criteria.APPROX_TIME, + completion_criteria.REFERENCE, + }, + content_kinds.H5P: { + completion_criteria.DETERMINED_BY_RESOURCE, + completion_criteria.TIME, + completion_criteria.APPROX_TIME, + completion_criteria.REFERENCE, + }, + content_kinds.AUDIO: { + completion_criteria.TIME, + completion_criteria.APPROX_TIME, + completion_criteria.REFERENCE, + }, + content_kinds.VIDEO: { + completion_criteria.TIME, + completion_criteria.APPROX_TIME, + completion_criteria.REFERENCE, + }, +} + + +def check_model_for_kind(data, kind): + model = data.get("model") + if kind is None or model is None or kind not in ALLOWED_MODELS_PER_KIND: + return + + # validate that content kind is allowed for the completion criteria model + if model not in ALLOWED_MODELS_PER_KIND[kind]: + raise ValidationError( + "Completion criteria model '{}' is invalid for content kind '{}'".format( + model, kind + ) + ) + + def validate(data, kind=None): """ :param data: Dictionary of data to validate @@ -49,16 +96,4 @@ def validate(data, kind=None): e.error_list.extend(error_descriptions) raise e - model = data.get("model") - if kind is None or model is None: - return - - # validate that content kind is allowed for the completion criteria model - if (model == completion_criteria.PAGES and kind != content_kinds.DOCUMENT) or ( - model == completion_criteria.MASTERY and kind != content_kinds.EXERCISE - ): - raise ValidationError( - "Completion criteria model '{}' is invalid for content kind '{}'".format( - model, kind - ) - ) + check_model_for_kind(data, kind) diff --git a/contentcuration/contentcuration/context_processors.py b/contentcuration/contentcuration/context_processors.py index 86d372895b..83736d7972 100644 --- a/contentcuration/contentcuration/context_processors.py +++ b/contentcuration/contentcuration/context_processors.py @@ -22,6 +22,10 @@ def site_variables(request): "DEBUG": settings.DEBUG, "LANG_INFO": json_for_parse_from_data(language_globals()), "LOGGED_IN": not request.user.is_anonymous, + "SENTRY_DSN": settings.SENTRY_DSN, + "SENTRY_ENVIRONMENT": settings.SENTRY_ENVIRONMENT, + "SENTRY_RELEASE": settings.SENTRY_RELEASE, + "SENTRY_ACTIVE": settings.SENTRY_ACTIVE, } diff --git a/contentcuration/contentcuration/frontend/channelEdit/__tests__/CurrentTopicView.spec.js b/contentcuration/contentcuration/frontend/channelEdit/__tests__/CurrentTopicView.spec.js index 7745554d28..3f7fd9b0e2 100644 --- a/contentcuration/contentcuration/frontend/channelEdit/__tests__/CurrentTopicView.spec.js +++ b/contentcuration/contentcuration/frontend/channelEdit/__tests__/CurrentTopicView.spec.js @@ -81,15 +81,21 @@ function selectNode(wrapper) { } describe('CurrentTopicView', () => { + let wrapper; + + afterEach(() => { + wrapper && wrapper.destroy(); + }); + it('smoke test', () => { const store = storeFactory(STORE_CONFIG); - const wrapper = makeWrapper({ store }); + wrapper = makeWrapper({ store }); expect(wrapper.isVueInstance()).toBe(true); }); describe('for a topic with nodes', () => { - let store, wrapper; + let store; beforeEach(() => { global.CHANNEL_EDIT_GLOBAL.channel_id = CHANNEL.id; diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/edit/ActivityDuration.vue b/contentcuration/contentcuration/frontend/channelEdit/components/edit/ActivityDuration.vue index 2209dda4ac..2afa529798 100644 --- a/contentcuration/contentcuration/frontend/channelEdit/components/edit/ActivityDuration.vue +++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/ActivityDuration.vue @@ -23,6 +23,7 @@ :items="availableNumbers" :menu-props="menuProps" :attach="attach" + :rules="minutesRules" /> @@ -102,10 +103,7 @@ if (this.audioVideoUpload) { return false; } - return ( - this.selectedDuration !== DurationDropdownMap.EXACT_TIME && - this.selectedCompletion === CompletionDropdownMap.completeDuration - ); + return this.selectedCompletion === CompletionDropdownMap.completeDuration; }, showOptionalLabel() { return this.selectedDuration !== DurationDropdownMap.EXACT_TIME; @@ -153,8 +151,13 @@ return getShortActivityDurationValidators().map(translateValidator); } else if (this.selectedDuration === DurationDropdownMap.LONG_ACTIVITY) { return getLongActivityDurationValidators().map(translateValidator); + } else if ( + this.selectedDuration === DurationDropdownMap.EXACT_TIME && + this.selectedCompletion === CompletionDropdownMap.completeDuration + ) { + return getActivityDurationValidators().map(translateValidator); } - return getActivityDurationValidators().map(translateValidator); + return []; }, }, created() { diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/edit/CompletionOptions.vue b/contentcuration/contentcuration/frontend/channelEdit/components/edit/CompletionOptions.vue index 5202afb40c..788651098e 100644 --- a/contentcuration/contentcuration/frontend/channelEdit/components/edit/CompletionOptions.vue +++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/CompletionOptions.vue @@ -1,6 +1,15 @@