From 730c973554e5488be94858b81591f0bd078463eb Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Mon, 26 Jun 2023 16:54:25 -0700 Subject: [PATCH] Add ancestor annotation to kolibri_public export mapper. --- .../kolibri_public/tests/test_mapper.py | 2 ++ .../kolibri_public/utils/mapper.py | 26 ++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/contentcuration/kolibri_public/tests/test_mapper.py b/contentcuration/kolibri_public/tests/test_mapper.py index 4411032ba4..a10ef159e1 100644 --- a/contentcuration/kolibri_public/tests/test_mapper.py +++ b/contentcuration/kolibri_public/tests/test_mapper.py @@ -79,6 +79,8 @@ def _assert_node(self, source, mapped): for mapped_tag in mapped.tags.all(): self.assertEqual(OKAY_TAG, mapped_tag.tag_name) + self.assertEqual(mapped.ancestors, [{"id": ancestor.id, "title": ancestor.title} for ancestor in source.get_ancestors()]) + def _recurse_and_assert(self, sources, mappeds, recursion_depth=0): recursion_depths = [] for source, mapped in zip(sources, mappeds): diff --git a/contentcuration/kolibri_public/utils/mapper.py b/contentcuration/kolibri_public/utils/mapper.py index a74f369403..f7605d7e1b 100644 --- a/contentcuration/kolibri_public/utils/mapper.py +++ b/contentcuration/kolibri_public/utils/mapper.py @@ -51,6 +51,8 @@ def run(self): self.mapped_channel.public = self.public self.mapped_channel.save_base(raw=True) annotate_label_bitmasks(self.mapped_root.get_descendants(include_self=True)) + # Rather than set the ancestors fields after mapping, like it is done in Kolibri + # here we set it during mapping as we are already recursing through the tree. set_channel_metadata_fields(self.mapped_channel.id, public=self.public) def _map_model(self, source, Model): @@ -69,22 +71,30 @@ def _map_and_bulk_create_model(self, sources, Model): Model.objects.bulk_create(cloned_sources, ignore_conflicts=True) - def _map_node(self, source): - return self._map_model(source, kolibri_public_models.ContentNode) + def _map_node(self, source, ancestors): + node = self._map_model(source, kolibri_public_models.ContentNode) + node.ancestors = ancestors + return node + + def _extend_ancestors(self, ancestors, new_ancestor): + return ancestors + [{"id": new_ancestor.id, "title": new_ancestor.title.replace('"', '\\"')}] def _recurse_to_create_tree( self, source, nodes_by_parent, + ancestors, ): - nodes_to_create = [self._map_node(source)] + nodes_to_create = [self._map_node(source, ancestors)] if source.kind == content_kinds.TOPIC and source.id in nodes_by_parent: children = sorted(nodes_by_parent[source.id], key=lambda x: x.lft) + ancestors = self._extend_ancestors(ancestors, source) for child in children: nodes_to_create.extend(self._recurse_to_create_tree( child, nodes_by_parent, + ancestors, )) return nodes_to_create @@ -110,6 +120,7 @@ def _map( self, node, batch_size, + ancestors=[], progress_tracker=None, ): """ @@ -118,13 +129,16 @@ def _map( if node.rght - node.lft < batch_size: copied_nodes = self._deep_map( node, + ancestors, ) if progress_tracker: progress_tracker.increment(len(copied_nodes)) return copied_nodes node_copy = self._shallow_map( node, + ancestors, ) + ancestors = self._extend_ancestors(ancestors, node) if progress_tracker: progress_tracker.increment() children = node.get_children().order_by("lft") @@ -132,6 +146,7 @@ def _map( self._map( child, batch_size, + ancestors=ancestors, progress_tracker=progress_tracker, ) return [node_copy] @@ -189,8 +204,9 @@ def _copy_associated_objects(self, nodes): def _shallow_map( self, node, + ancestors, ): - mapped_node = self._map_node(node) + mapped_node = self._map_node(node, ancestors) mapped_node.save_base(raw=True) @@ -200,6 +216,7 @@ def _shallow_map( def _deep_map( self, node, + ancestors, ): source_nodes = node.get_descendants(include_self=True) @@ -212,6 +229,7 @@ def _deep_map( nodes_to_create = self._recurse_to_create_tree( node, nodes_by_parent, + ancestors, ) mapped_nodes = kolibri_public_models.ContentNode.objects.bulk_create(nodes_to_create)