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
10 changes: 6 additions & 4 deletions openedx_tagging/core/tagging/import_export/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,8 +373,7 @@ class DeleteTag(ImportAction):
"""

def __str__(self) -> str:
taxonomy_tag = self._get_tag()
return str(_("Delete tag (external_id={external_id})").format(external_id=taxonomy_tag.external_id))
return str(_("Delete tag (external_id={external_id})").format(external_id=self.tag.id))

name = "delete"

Expand All @@ -397,8 +396,11 @@ def execute(self) -> None:
"""
Delete a tag
"""
taxonomy_tag = self._get_tag()
taxonomy_tag.delete()
try:
taxonomy_tag = self._get_tag()
taxonomy_tag.delete()
except Tag.DoesNotExist:
pass # The tag may be already cascade deleted if the parent tag was deleted


class WithoutChanges(ImportAction):
Expand Down
18 changes: 10 additions & 8 deletions openedx_tagging/core/tagging/import_export/import_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,16 @@ def _build_delete_actions(self, tags: dict):
# Verify if there is not a parent update before
if not self._search_parent_update(child.external_id, tag.external_id):
# Change parent to avoid delete childs
self._build_action(
UpdateParentTag,
TagItem(
id=child.external_id,
value=child.value,
parent_id=None,
),
)
if child.external_id not in tags:
# Only update parent if the child is not going to be deleted
self._build_action(
UpdateParentTag,
TagItem(
id=child.external_id,
value=child.value,
parent_id=None,
),
)

# Delete action
self._build_action(
Expand Down
41 changes: 40 additions & 1 deletion tests/openedx_tagging/core/tagging/import_export/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import openedx_tagging.core.tagging.import_export.api as import_export_api
from openedx_tagging.core.tagging.import_export import ParserFormat
from openedx_tagging.core.tagging.models import LanguageTaxonomy, TagImportTask, TagImportTaskState, Taxonomy
from openedx_tagging.core.tagging.models import LanguageTaxonomy, Tag, TagImportTask, TagImportTaskState, Taxonomy

from .mixins import TestImportExportMixin

Expand Down Expand Up @@ -197,3 +197,42 @@ def test_import_with_export_output(self) -> None:
if tag.parent:
assert new_tag.parent
assert tag.parent.external_id == new_tag.parent.external_id

def test_import_removing_with_childs(self) -> None:
"""
Test import need to remove childs with parents that will also be removed
"""
new_taxonomy = Taxonomy(name="New taxonomy")
new_taxonomy.save()
level2 = Tag.objects.create(
id=1000,
external_id="tag_2",
value="Tag 2",
taxonomy=new_taxonomy,
)
level1 = Tag.objects.create(
id=1001,
external_id="tag_1",
value="Tag 1",
taxonomy=new_taxonomy,
)
level3 = Tag.objects.create(
id=1002,
external_id="tag_3",
value="Tag 3",
taxonomy=new_taxonomy,
)
level2.parent = level1
level2.save()

level3.parent = level3
level3.save()

# Import with empty tags, to remove all tags
importFile = BytesIO(json.dumps({"tags": []}).encode())
assert import_export_api.import_tags(
new_taxonomy,
importFile,
ParserFormat.JSON,
replace=True,
)
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,15 @@ def test_build_delete_actions(self) -> None:
assert len(self.import_plan.errors) == 0

# Check actions in order
# #1 Update parent of 'tag_2'
assert self.import_plan.actions[0].name == 'update_parent'
assert self.import_plan.actions[0].tag.id == 'tag_2'
assert self.import_plan.actions[0].tag.parent_id is None
# #2 Delete 'tag_1'
# #1 Delete 'tag_1'
assert self.import_plan.actions[0].name == 'delete'
assert self.import_plan.actions[0].tag.id == 'tag_1'
# #2 Delete 'tag_2'
assert self.import_plan.actions[1].name == 'delete'
assert self.import_plan.actions[1].tag.id == 'tag_1'
# #3 Delete 'tag_2'
assert self.import_plan.actions[1].tag.id == 'tag_2'
# #3 Delete 'tag_3'
assert self.import_plan.actions[2].name == 'delete'
assert self.import_plan.actions[2].tag.id == 'tag_2'
# #4 Update parent of 'tag_4'
assert self.import_plan.actions[3].name == 'update_parent'
assert self.import_plan.actions[3].tag.id == 'tag_4'
assert self.import_plan.actions[3].tag.parent_id is None
# #5 Delete 'tag_3'
assert self.import_plan.actions[4].name == 'delete'
assert self.import_plan.actions[4].tag.id == 'tag_3'
assert self.import_plan.actions[2].tag.id == 'tag_3'

@ddt.data(
# Test valid actions
Expand Down Expand Up @@ -193,10 +185,6 @@ def test_build_delete_actions(self) -> None:
'name': 'without_changes',
'id': 'tag_4',
},
{
'name': 'update_parent',
'id': 'tag_2',
},
{
'name': 'delete',
'id': 'tag_1',
Expand Down Expand Up @@ -330,13 +318,11 @@ def test_generate_actions(self, tags, replace, expected_errors, expected_actions
"Import plan for Import Taxonomy Test\n"
"--------------------------------\n"
"#1: No changes needed for tag (external_id=tag_4)\n"
"#2: Update the parent of tag (external_id=tag_2) from parent (external_id=tag_1) "
"to parent (external_id=None).\n"
"#3: Delete tag (external_id=tag_1)\n"
"#4: Delete tag (external_id=tag_2)\n"
"#5: Update the parent of tag (external_id=tag_4) from parent (external_id=tag_3) "
"#2: Delete tag (external_id=tag_1)\n"
"#3: Delete tag (external_id=tag_2)\n"
"#4: Update the parent of tag (external_id=tag_4) from parent (external_id=tag_3) "
"to parent (external_id=None).\n"
"#6: Delete tag (external_id=tag_3)\n"
"#5: Delete tag (external_id=tag_3)\n"
),
)
@ddt.unpack
Expand Down