Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 49 additions & 4 deletions contentcuration/contentcuration/tests/test_exportchannel.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,20 @@ def setUp(self):
new_video.save()

first_topic = self.content_channel.main_tree.get_descendants().first()

# Add a publishable topic to ensure it does not inherit but that its children do
new_node = create_node({'kind_id': 'topic', 'title': 'Disinherited topic', 'children': []})
new_node.complete = True
new_node.parent = first_topic
new_node.save()

new_video = create_node({'kind_id': 'video', 'title': 'Inheriting video', 'children': []})
new_video.complete = True
new_video.parent = new_node
new_video.save()

first_topic.language_id = "fr"

first_topic.accessibility_labels = {
accessibility_categories.AUDIO_DESCRIPTION: True,
}
Expand All @@ -118,6 +132,7 @@ def setUp(self):
first_topic.save()

first_topic_first_child = first_topic.children.first()
first_topic_first_child.language_id = "sw"
first_topic_first_child.accessibility_labels = {
accessibility_categories.CAPTIONS_SUBTITLES: True,
}
Expand Down Expand Up @@ -230,10 +245,28 @@ def test_assessment_metadata(self):
self.assertTrue(isinstance(json.loads(asm.assessment_item_ids), list))
self.assertTrue(isinstance(json.loads(asm.mastery_model), dict))

def test_inherited_language(self):
first_topic_node_id = self.content_channel.main_tree.get_descendants().first().node_id
for child in kolibri_models.ContentNode.objects.filter(parent_id=first_topic_node_id)[1:]:
if child.kind == "topic":
self.assertIsNone(child.lang_id)
self.assertEqual(child.children.first().lang_id, "fr")
else:
self.assertEqual(child.lang_id, "fr")

def test_inherited_language_no_overwrite(self):
first_topic_node_id = self.content_channel.main_tree.get_descendants().first().node_id
first_child = kolibri_models.ContentNode.objects.filter(parent_id=first_topic_node_id).first()
self.assertEqual(first_child.lang_id, "sw")

def test_inherited_category(self):
first_topic_node_id = self.content_channel.main_tree.get_descendants().first().node_id
for child in kolibri_models.ContentNode.objects.filter(parent_id=first_topic_node_id)[1:]:
self.assertEqual(child.categories, subjects.MATHEMATICS)
if child.kind == "topic":
self.assertIsNone(child.categories)
self.assertEqual(child.children.first().categories, subjects.MATHEMATICS)
else:
self.assertEqual(child.categories, subjects.MATHEMATICS)

def test_inherited_category_no_overwrite(self):
first_topic_node_id = self.content_channel.main_tree.get_descendants().first().node_id
Expand All @@ -243,7 +276,11 @@ def test_inherited_category_no_overwrite(self):
def test_inherited_needs(self):
first_topic_node_id = self.content_channel.main_tree.get_descendants().first().node_id
for child in kolibri_models.ContentNode.objects.filter(parent_id=first_topic_node_id)[1:]:
self.assertEqual(child.learner_needs, needs.PRIOR_KNOWLEDGE)
if child.kind == "topic":
self.assertIsNone(child.learner_needs)
self.assertEqual(child.children.first().learner_needs, needs.PRIOR_KNOWLEDGE)
else:
self.assertEqual(child.learner_needs, needs.PRIOR_KNOWLEDGE)

def test_inherited_needs_no_overwrite(self):
first_topic_node_id = self.content_channel.main_tree.get_descendants().first().node_id
Expand All @@ -264,12 +301,20 @@ def test_child_no_inherit_accessibility_label(self):
def test_inherited_grade_levels(self):
first_topic_node_id = self.content_channel.main_tree.get_descendants().first().node_id
for child in kolibri_models.ContentNode.objects.filter(parent_id=first_topic_node_id):
self.assertEqual(child.grade_levels, levels.LOWER_SECONDARY)
if child.kind == "topic":
self.assertIsNone(child.grade_levels)
self.assertEqual(child.children.first().grade_levels, levels.LOWER_SECONDARY)
else:
self.assertEqual(child.grade_levels, levels.LOWER_SECONDARY)

def test_inherited_resource_types(self):
first_topic_node_id = self.content_channel.main_tree.get_descendants().first().node_id
for child in kolibri_models.ContentNode.objects.filter(parent_id=first_topic_node_id):
self.assertEqual(child.resource_types, resource_type.LESSON_PLAN)
if child.kind == "topic":
self.assertIsNone(child.resource_types)
self.assertEqual(child.children.first().resource_types, resource_type.LESSON_PLAN)
else:
self.assertEqual(child.resource_types, resource_type.LESSON_PLAN)

def test_topics_no_learning_activity(self):
first_topic_node_id = self.content_channel.main_tree.get_descendants().first().node_id
Expand Down
36 changes: 26 additions & 10 deletions contentcuration/contentcuration/utils/publish.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,17 @@ def assign_license_to_contentcuration_nodes(channel, license):
channel.main_tree.get_family().update(license_id=license.pk)


inheritable_fields = [
inheritable_map_fields = [
"grade_levels",
"resource_types",
"categories",
"learner_needs",
]

inheritable_simple_value_fields = [
"language",
]


class TreeMapper:
def __init__(
Expand Down Expand Up @@ -194,15 +198,15 @@ def _node_completed(self):
def map_nodes(self):
self.recurse_nodes(self.root_node, {})

def recurse_nodes(self, node, inherited_fields):
def recurse_nodes(self, node, inherited_fields): # noqa C901
logging.debug("Mapping node with id {id}".format(id=node.pk))

# Only process nodes that are either non-topics or have non-topic descendants
if node.get_descendants(include_self=True).exclude(kind_id=content_kinds.TOPIC).exists() and node.complete:

metadata = {}

for field in inheritable_fields:
for field in inheritable_map_fields:
metadata[field] = {}
inherited_keys = (inherited_fields.get(field) or {}).keys()
own_keys = (getattr(node, field) or {}).keys()
Expand All @@ -212,6 +216,12 @@ def recurse_nodes(self, node, inherited_fields):
if not any(k != key and k.startswith(key) for k in all_keys):
metadata[field][key] = True

for field in inheritable_simple_value_fields:
if field in inherited_fields:
metadata[field] = inherited_fields[field]
if getattr(node, field):
metadata[field] = getattr(node, field)

kolibrinode = create_bare_contentnode(node, self.default_language, self.channel_id, self.channel_name, metadata)

if node.kind.kind == content_kinds.EXERCISE:
Expand Down Expand Up @@ -268,9 +278,9 @@ def create_bare_contentnode(ccnode, default_language, channel_id, channel_name,
if ccnode.license is not None:
kolibri_license = create_kolibri_license_object(ccnode)[0]

language = None
if ccnode.language or default_language:
language, _new = get_or_create_language(ccnode.language or default_language)
language = (ccnode.language if ccnode.kind_id == content_kinds.TOPIC else metadata.get("language")) or default_language
if language:
language, _new = get_or_create_language(language)

duration = None
if ccnode.kind_id in [content_kinds.AUDIO, content_kinds.VIDEO]:
Expand Down Expand Up @@ -298,6 +308,12 @@ def create_bare_contentnode(ccnode, default_language, channel_id, channel_name,
if ccnode.accessibility_labels:
accessibility_labels = ",".join(ccnode.accessibility_labels.keys())

# Do not use the inherited metadata if this is a topic, just read from its own metadata instead.
grade_levels = ccnode.grade_levels if ccnode.kind_id == content_kinds.TOPIC else metadata["grade_levels"]
resource_types = ccnode.resource_types if ccnode.kind_id == content_kinds.TOPIC else metadata["resource_types"]
categories = ccnode.categories if ccnode.kind_id == content_kinds.TOPIC else metadata["categories"]
learner_needs = ccnode.learner_needs if ccnode.kind_id == content_kinds.TOPIC else metadata["learner_needs"]

kolibrinode, is_new = kolibrimodels.ContentNode.objects.update_or_create(
pk=ccnode.node_id,
defaults={
Expand All @@ -319,12 +335,12 @@ def create_bare_contentnode(ccnode, default_language, channel_id, channel_name,
'duration': duration,
'options': json.dumps(options),
# Fields for metadata labels
"grade_levels": ",".join(metadata["grade_levels"].keys()) if metadata["grade_levels"] else None,
"resource_types": ",".join(metadata["resource_types"].keys()) if metadata["resource_types"] else None,
"grade_levels": ",".join(grade_levels.keys()) if grade_levels else None,
"resource_types": ",".join(resource_types.keys()) if resource_types else None,
"learning_activities": learning_activities,
"accessibility_labels": accessibility_labels,
"categories": ",".join(metadata["categories"].keys()) if metadata["categories"] else None,
"learner_needs": ",".join(metadata["learner_needs"].keys()) if metadata["learner_needs"] else None,
"categories": ",".join(categories.keys()) if categories else None,
"learner_needs": ",".join(learner_needs.keys()) if learner_needs else None,
}
)

Expand Down