From 0f767e42773f67464654cf031b76ddef8743e9a3 Mon Sep 17 00:00:00 2001 From: Vivek Agrawal Date: Sat, 12 Mar 2022 00:54:33 +0530 Subject: [PATCH 1/5] fix: override exported content nodes duration to support completion criteria --- .../tests/test_exportchannel.py | 21 +++++++++++++++++++ .../contentcuration/tests/testdata.py | 2 ++ .../contentcuration/utils/publish.py | 14 ++++++++----- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/contentcuration/contentcuration/tests/test_exportchannel.py b/contentcuration/contentcuration/tests/test_exportchannel.py index 25edd36f7f..675a5a86d1 100644 --- a/contentcuration/contentcuration/tests/test_exportchannel.py +++ b/contentcuration/contentcuration/tests/test_exportchannel.py @@ -77,6 +77,20 @@ def setUp(self): new_video.parent = self.content_channel.main_tree new_video.save() + # Add a node to test completion criteria. + extra_fields = { + "options": { + "completion_criteria": { + "model": "time", + "threshold": 20 + } + } + } + new_video = create_node({'kind_id': 'video', 'title': 'Completion criteria test', 'extra_fields': extra_fields, 'children': []}) + new_video.complete = True + new_video.parent = self.content_channel.main_tree + new_video.save() + set_channel_icon_encoding(self.content_channel) self.tempdb = create_content_database(self.content_channel, True, None, True) @@ -135,6 +149,13 @@ def test_tags_greater_than_30_excluded(self): for t in published_tags: assert len(t.tag_name) <= 30 + def test_duration_override_on_completion_criteria_time(self): + completion_criteria_node = kolibri_models.ContentNode.objects.filter(title='Completion criteria test').first() + non_completion_criteria_node = kolibri_models.ContentNode.objects.filter(title='kolibri tag test').first() + + assert completion_criteria_node.duration == 20 + assert non_completion_criteria_node.duration == 100 + def test_contentnode_channel_id_data(self): channel = kolibri_models.ChannelMetadata.objects.first() nodes = kolibri_models.ContentNode.objects.all() diff --git a/contentcuration/contentcuration/tests/testdata.py b/contentcuration/contentcuration/tests/testdata.py index c775168100..50895337b1 100644 --- a/contentcuration/contentcuration/tests/testdata.py +++ b/contentcuration/contentcuration/tests/testdata.py @@ -144,11 +144,13 @@ def node(data, parent=None): content_id=data.get('content_id') or data['node_id'], sort_order=data.get('sort_order', 1), complete=True, + extra_fields=data.get('extra_fields'), ) new_node.save() video_file = fileobj_video(contents=b"Video File") video_file.contentnode = new_node video_file.preset_id = format_presets.VIDEO_HIGH_RES + video_file.duration = 100 video_file.save() # Create exercises diff --git a/contentcuration/contentcuration/utils/publish.py b/contentcuration/contentcuration/utils/publish.py index 4c8a54ad73..632964cfa8 100644 --- a/contentcuration/contentcuration/utils/publish.py +++ b/contentcuration/contentcuration/utils/publish.py @@ -226,15 +226,19 @@ def create_bare_contentnode(ccnode, default_language, channel_id, channel_name): if ccnode.language or default_language: language, _new = get_or_create_language(ccnode.language or default_language) - duration = None - if ccnode.kind_id in [content_kinds.AUDIO, content_kinds.VIDEO]: - # aggregate duration from associated files, choosing maximum if there are multiple, like hi and lo res videos - duration = ccnode.files.aggregate(duration=Max("duration")).get("duration") - options = {} if ccnode.extra_fields and 'options' in ccnode.extra_fields: options = ccnode.extra_fields['options'] + duration = None + if ccnode.kind_id in [content_kinds.AUDIO, content_kinds.VIDEO]: + completion_criteria = options.get("completion_criteria") + if completion_criteria and (completion_criteria["model"] == "time" or completion_criteria["model"] == "approx_time"): + duration = completion_criteria["threshold"] + else: + # aggregate duration from associated files, choosing maximum if there are multiple, like hi and lo res videos. + duration = ccnode.files.aggregate(duration=Max("duration")).get("duration") + kolibrinode, is_new = kolibrimodels.ContentNode.objects.update_or_create( pk=ccnode.node_id, defaults={ From 1f26e00df80f6a96db75396f4923bb5eb2953289 Mon Sep 17 00:00:00 2001 From: Vivek Agrawal Date: Sat, 12 Mar 2022 01:37:42 +0530 Subject: [PATCH 2/5] fix: override irrespective of content kind --- contentcuration/contentcuration/utils/publish.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/contentcuration/contentcuration/utils/publish.py b/contentcuration/contentcuration/utils/publish.py index 632964cfa8..c91ef10106 100644 --- a/contentcuration/contentcuration/utils/publish.py +++ b/contentcuration/contentcuration/utils/publish.py @@ -30,6 +30,7 @@ from kolibri_content import models as kolibrimodels from kolibri_content.router import get_active_content_database from kolibri_content.router import using_content_database +from le_utils.constants import completion_criteria from le_utils.constants import content_kinds from le_utils.constants import exercises from le_utils.constants import file_formats @@ -232,12 +233,13 @@ def create_bare_contentnode(ccnode, default_language, channel_id, channel_name): duration = None if ccnode.kind_id in [content_kinds.AUDIO, content_kinds.VIDEO]: - completion_criteria = options.get("completion_criteria") - if completion_criteria and (completion_criteria["model"] == "time" or completion_criteria["model"] == "approx_time"): + # aggregate duration from associated files, choosing maximum if there are multiple, like hi and lo res videos. + duration = ccnode.files.aggregate(duration=Max("duration")).get("duration") + + ccnode_completion_criteria = options.get("completion_criteria") + if ccnode_completion_criteria: + if ccnode_completion_criteria["model"] == completion_criteria.TIME or ccnode_completion_criteria["model"] == completion_criteria.APPROX_TIME: duration = completion_criteria["threshold"] - else: - # aggregate duration from associated files, choosing maximum if there are multiple, like hi and lo res videos. - duration = ccnode.files.aggregate(duration=Max("duration")).get("duration") kolibrinode, is_new = kolibrimodels.ContentNode.objects.update_or_create( pk=ccnode.node_id, From 9a9cac5aedd30017741dc84b454c963b327074a8 Mon Sep 17 00:00:00 2001 From: Vivek Agrawal Date: Sat, 12 Mar 2022 02:01:39 +0530 Subject: [PATCH 3/5] fix: a small typo --- contentcuration/contentcuration/utils/publish.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contentcuration/contentcuration/utils/publish.py b/contentcuration/contentcuration/utils/publish.py index c91ef10106..a0707b1251 100644 --- a/contentcuration/contentcuration/utils/publish.py +++ b/contentcuration/contentcuration/utils/publish.py @@ -239,7 +239,7 @@ def create_bare_contentnode(ccnode, default_language, channel_id, channel_name): ccnode_completion_criteria = options.get("completion_criteria") if ccnode_completion_criteria: if ccnode_completion_criteria["model"] == completion_criteria.TIME or ccnode_completion_criteria["model"] == completion_criteria.APPROX_TIME: - duration = completion_criteria["threshold"] + duration = ccnode_completion_criteria["threshold"] kolibrinode, is_new = kolibrimodels.ContentNode.objects.update_or_create( pk=ccnode.node_id, From de63a1b13a9323d1b3240a0820dc1543a2cd3b18 Mon Sep 17 00:00:00 2001 From: Vivek Agrawal Date: Sat, 12 Mar 2022 02:24:45 +0530 Subject: [PATCH 4/5] perf: update order of ops to preserve query run --- contentcuration/contentcuration/utils/publish.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/contentcuration/contentcuration/utils/publish.py b/contentcuration/contentcuration/utils/publish.py index a0707b1251..ff1a9f879b 100644 --- a/contentcuration/contentcuration/utils/publish.py +++ b/contentcuration/contentcuration/utils/publish.py @@ -232,14 +232,13 @@ def create_bare_contentnode(ccnode, default_language, channel_id, channel_name): options = ccnode.extra_fields['options'] duration = None - if ccnode.kind_id in [content_kinds.AUDIO, content_kinds.VIDEO]: - # aggregate duration from associated files, choosing maximum if there are multiple, like hi and lo res videos. - duration = ccnode.files.aggregate(duration=Max("duration")).get("duration") - ccnode_completion_criteria = options.get("completion_criteria") if ccnode_completion_criteria: if ccnode_completion_criteria["model"] == completion_criteria.TIME or ccnode_completion_criteria["model"] == completion_criteria.APPROX_TIME: duration = ccnode_completion_criteria["threshold"] + elif ccnode.kind_id in [content_kinds.AUDIO, content_kinds.VIDEO]: + # aggregate duration from associated files, choosing maximum if there are multiple, like hi and lo res videos. + duration = ccnode.files.aggregate(duration=Max("duration")).get("duration") kolibrinode, is_new = kolibrimodels.ContentNode.objects.update_or_create( pk=ccnode.node_id, From ba1dfdbd6009e5abaa393bfe6c8892fdf32cd46b Mon Sep 17 00:00:00 2001 From: Vivek Agrawal Date: Sun, 13 Mar 2022 14:42:44 +0530 Subject: [PATCH 5/5] fix: minor tweak --- contentcuration/contentcuration/utils/publish.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contentcuration/contentcuration/utils/publish.py b/contentcuration/contentcuration/utils/publish.py index ff1a9f879b..bb200e018d 100644 --- a/contentcuration/contentcuration/utils/publish.py +++ b/contentcuration/contentcuration/utils/publish.py @@ -236,7 +236,7 @@ def create_bare_contentnode(ccnode, default_language, channel_id, channel_name): if ccnode_completion_criteria: if ccnode_completion_criteria["model"] == completion_criteria.TIME or ccnode_completion_criteria["model"] == completion_criteria.APPROX_TIME: duration = ccnode_completion_criteria["threshold"] - elif ccnode.kind_id in [content_kinds.AUDIO, content_kinds.VIDEO]: + if duration is None and ccnode.kind_id in [content_kinds.AUDIO, content_kinds.VIDEO]: # aggregate duration from associated files, choosing maximum if there are multiple, like hi and lo res videos. duration = ccnode.files.aggregate(duration=Max("duration")).get("duration")