Create Custom order for objective groups

This commit is contained in:
Lorenz Padberg 2024-02-16 12:55:42 +01:00
parent ea1816e3cc
commit 16da66da31
3 changed files with 118 additions and 92 deletions

View File

@ -3,6 +3,7 @@ from logging import getLogger
from django.core.management import BaseCommand from django.core.management import BaseCommand
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models
from books.models import Chapter, ObjectiveGroupSnapshot, Snapshot, ContentBlockSnapshot, ChapterSnapshot from books.models import Chapter, ObjectiveGroupSnapshot, Snapshot, ContentBlockSnapshot, ChapterSnapshot
from books.models import ContentBlock from books.models import ContentBlock
@ -11,6 +12,7 @@ from objectives.models import ObjectiveSnapshot, Objective, ObjectiveGroup
logger = getLogger(__name__) logger = getLogger(__name__)
class Command(BaseCommand): class Command(BaseCommand):
def handle(self, *args, **options): def handle(self, *args, **options):
ContentBlock.objects.filter(title__startswith="TESTOBJECTIVE").delete() ContentBlock.objects.filter(title__startswith="TESTOBJECTIVE").delete()
@ -21,110 +23,131 @@ class Command(BaseCommand):
migrate_objectives_to_content() migrate_objectives_to_content()
def migrate_objectives_to_content(): def migrate_objectives_to_content():
created_content_blocks = 0 created_content_blocks = 0
failed_modules = [] failed_modules = []
# This dict stores all content blocks that have been created for a set of objectives # This dict stores all content blocks that have been created for a set of objectives
# In this way we can reuse content blocks for the same set of objectives # In this way we can reuse content blocks for the same set of objectives
created_default_content_blocks = {} created_default_content_blocks = {}
for module in Module.objects.all(): for module in Module.objects.all():
try: try:
chapter = create_chapter_from_objective_group(module) chapter = create_chapter_from_objective_group(module)
for objective_group in module.objective_groups.all().order_by('title'): for objective_group in get_objectives_groups_in_specific_order(module):
default_objectives = list(objective_group.objectives.filter(owner__isnull=True, ) default_objectives = list(objective_group.objectives.filter(owner__isnull=True, )
.exclude(objectivesnapshot__isnull=False).order_by('order')) .exclude(objectivesnapshot__isnull=False).order_by('order'))
default_objectives_ids = tuple(objective.id for objective in default_objectives) default_objectives_ids = tuple(objective.id for objective in default_objectives)
# Create "Verlagsinhalte" content block if it does not exist yet # Create "Verlagsinhalte" content block if it does not exist yet
if default_objectives_ids not in created_default_content_blocks: if default_objectives_ids not in created_default_content_blocks:
default_content_block = create_default_content(objective_group, chapter) default_content_block = create_default_content(objective_group, chapter)
created_default_content_blocks[default_objectives_ids] = default_content_block created_default_content_blocks[default_objectives_ids] = default_content_block
else: else:
default_content_block = created_default_content_blocks[default_objectives_ids] default_content_block = created_default_content_blocks[default_objectives_ids]
custom_objectives_by_owner = get_objectives_by_owner(objective_group) custom_objectives_by_owner = get_objectives_by_owner(objective_group)
if default_objectives or custom_objectives_by_owner: if default_objectives or custom_objectives_by_owner:
contentblocks_by_merged_objectives_ids = {} contentblocks_by_merged_objectives_ids = {}
# cor custom objectives iterate over owners, # cor custom objectives iterate over owners,
# - one ownsers custom objectives must not be changed by another owner # - one ownsers custom objectives must not be changed by another owner
# - visibility is set per class # - visibility is set per class
for owner, owner_objectives in custom_objectives_by_owner.items(): for owner, owner_objectives in custom_objectives_by_owner.items():
print(f"Owner: {owner}") print(f"Owner: {owner}")
print(f" Objectives: ") print(f" Objectives: ")
visible_default_objectives_by_class = filter_visible_objectives_by_class(default_objectives, owner) visible_default_objectives_by_class = filter_visible_objectives_by_class(default_objectives,
owner)
for school_class, default_objectives_for_class in visible_default_objectives_by_class.items(): for school_class, default_objectives_for_class in visible_default_objectives_by_class.items():
custom_content_block = None custom_content_block = None
print(f" School class: {school_class}") print(f" School class: {school_class}")
# merge "Verlagsinhalte" and "benutzerdefinierte Inhalte" # merge "Verlagsinhalte" and "benutzerdefinierte Inhalte"
visible_owner_objectives = [objective for objective in owner_objectives if not objective.is_hidden_for_class(school_class)] visible_owner_objectives = [objective for objective in owner_objectives if
not objective.is_hidden_for_class(school_class)]
merged_objectives = default_objectives_for_class + visible_owner_objectives merged_objectives = default_objectives_for_class + visible_owner_objectives
merged_objectives_ids = tuple(objective.id for objective in merged_objectives) merged_objectives_ids = tuple(objective.id for objective in merged_objectives)
is_default_content = merged_objectives_ids == default_objectives_ids is_default_content = merged_objectives_ids == default_objectives_ids
if is_default_content: if is_default_content:
print(f" Objective: Reuse default content block") print(f" Objective: Reuse default content block")
# custom_content_block = default_content_block # custom_content_block = default_content_block
# Create content block if that set of objectives has not been created yet # Create content block if that set of objectives has not been created yet
if merged_objectives_ids not in contentblocks_by_merged_objectives_ids and not is_default_content: if merged_objectives_ids not in contentblocks_by_merged_objectives_ids and not is_default_content:
for objective in merged_objectives: for objective in merged_objectives:
print(f" Objective: {objective} {objective.owner}") print(f" Objective: {objective} {objective.owner}")
if merged_objectives_ids: if merged_objectives_ids:
custom_content_block = create_content_block_from_objective(objective_group, custom_content_block = create_content_block_from_objective(objective_group,
chapter, chapter,
owner=owner, owner=owner,
) )
contentblocks_by_merged_objectives_ids[ contentblocks_by_merged_objectives_ids[
merged_objectives_ids] = custom_content_block merged_objectives_ids] = custom_content_block
create_text_in_content_block(merged_objectives, custom_content_block) create_text_in_content_block(merged_objectives, custom_content_block)
created_content_blocks += 1 created_content_blocks += 1
else:
if not is_default_content:
print(f" Objective: Reuse content block")
custom_content_block = contentblocks_by_merged_objectives_ids[
merged_objectives_ids]
else: else:
if not is_default_content: print(f" Objective: Reuse default content block")
print(f" Objective: Reuse content block")
custom_content_block = contentblocks_by_merged_objectives_ids[
merged_objectives_ids]
else:
print(f" Objective: Reuse default content block")
# set visibility # set visibility
# hide default objectives if custom objectives exist # hide default objectives if custom objectives exist
if custom_content_block: if custom_content_block:
default_content_block.hidden_for.add(school_class) default_content_block.hidden_for.add(school_class)
default_content_block.save_revision().publish() default_content_block.save_revision().publish()
default_content_block.save() default_content_block.save()
custom_content_block.visible_for.add(school_class) custom_content_block.visible_for.add(school_class)
custom_content_block.save_revision().publish() custom_content_block.save_revision().publish()
custom_content_block.save() custom_content_block.save()
if objective_group.hidden_for.filter(id=school_class.id).exists(): if objective_group.hidden_for.filter(id=school_class.id).exists():
default_content_block.hidden_for.add(school_class) default_content_block.hidden_for.add(school_class)
default_content_block.save_revision().publish() default_content_block.save_revision().publish()
default_content_block.save() default_content_block.save()
except ValidationError as e: except ValidationError as e:
print(f"Error with module {module}") print(f"Error with module {module}")
logger.error(e) logger.error(e)
failed_modules.append(module) failed_modules.append(module)
print(f"Created {created_content_blocks} content blocks") print(f"Created {created_content_blocks} content blocks")
print(f"Failed modules: {len(failed_modules)}") print(f"Failed modules: {len(failed_modules)}")
for module in failed_modules: for module in failed_modules:
print(f"Faile module: {module}") print(f"Faile module: {module}")
def get_objectives_groups_in_specific_order(module):
# Create a specific order for the objective groups
# https://stackoverflow.com/questions/5966462/sort-queryset-by-values-in-list
# https://docs.djangoproject.com/en/5.0/ref/models/conditional-expressions/
order_of_objective_groups = ["Sprache & Kommunikation", "Gesellschaft", "Übergeordnete Lernziele"]
_whens = []
for sort_index, value in enumerate(order_of_objective_groups):
_whens.append(
models.When(title=value, then=sort_index)
)
qs = module.objective_groups.all().annotate(_sort_index=models.Case(*_whens,
output_field=models.IntegerField()
)
).order_by('_sort_index')
return qs
def create_default_content(objective_group, chapter): def create_default_content(objective_group, chapter):
@ -156,7 +179,8 @@ def filter_visible_objectives_by_class(objectives, user):
def get_objectives_by_owner(objective_group, exclude_snapshots=True): def get_objectives_by_owner(objective_group, exclude_snapshots=True):
custom_objectives = objective_group.objectives.filter(owner__isnull=False, objectivesnapshot__isnull=True).order_by('order') custom_objectives = objective_group.objectives.filter(owner__isnull=False, objectivesnapshot__isnull=True).order_by(
'order')
custom_objectives_by_owner = {} custom_objectives_by_owner = {}
for objective in custom_objectives: for objective in custom_objectives:
@ -233,15 +257,16 @@ def create_text_in_content_block(objectives, content_block, get_or_create=False)
if get_or_create: if get_or_create:
content_block_qs = ContentBlock.objects.filter(title=content_block.title, content_block_qs = ContentBlock.objects.filter(title=content_block.title,
owner=content_block.owner, owner=content_block.owner,
user_created=content_block.user_created, user_created=content_block.user_created,
contents=content_block.contents) contents=content_block.contents)
if content_block_qs.exists(): if content_block_qs.exists():
content_block = content_block_qs.first() content_block = content_block_qs.first()
content_block.save_revision().publish() content_block.save_revision().publish()
return content_block return content_block
def create_content_block_contents(objectives): def create_content_block_contents(objectives):
objectives = list(objectives) objectives = list(objectives)
objective_li = [f"<li>{objective.text}</li>" for objective in objectives if objective.text] objective_li = [f"<li>{objective.text}</li>" for objective in objectives if objective.text]
@ -254,6 +279,7 @@ def create_content_block_contents(objectives):
contents = json.dumps(texts) contents = json.dumps(texts)
return contents return contents
def analyze(): def analyze():
print(f""" print(f"""
OjectiveGroups: {ObjectiveGroup.objects.count()} OjectiveGroups: {ObjectiveGroup.objects.count()}

View File

@ -61,7 +61,7 @@ class TestObjectivesMigration(SkillboxTestCase):
}) })
module = result.data['module'] module = result.data['module']
chapter1 = module['chapters'][0] chapter1 = module['chapters'][0]
default_content, _, _ = chapter1['contentBlocks'] _, default_content, _,_ = chapter1['contentBlocks']
# default content block (Verlagsinhalte) exists but is hidden (since one objective is hidden for this class) # default content block (Verlagsinhalte) exists but is hidden (since one objective is hidden for this class)
self.assertEqual(default_content['title'], 'Gesellschaft') self.assertEqual(default_content['title'], 'Gesellschaft')
@ -76,7 +76,7 @@ class TestObjectivesMigration(SkillboxTestCase):
}) })
module = result.data['module'] module = result.data['module']
chapter1 = module['chapters'][0] chapter1 = module['chapters'][0]
_, custom, _ = chapter1['contentBlocks'] _,_, custom, _ = chapter1['contentBlocks']
# default content block (Verlagsinhalte) exists but is hidden (since one objective is hidden for this class) # default content block (Verlagsinhalte) exists but is hidden (since one objective is hidden for this class)
self.assertEqual(custom['title'], 'Gesellschaft') self.assertEqual(custom['title'], 'Gesellschaft')
@ -92,7 +92,7 @@ class TestObjectivesMigration(SkillboxTestCase):
}) })
module = result.data['module'] module = result.data['module']
chapter1 = module['chapters'][0] chapter1 = module['chapters'][0]
_, _, hidden_custom = chapter1['contentBlocks'] hidden_custom, _, _, _ = chapter1['contentBlocks']
# default content block (Verlagsinhalte) exists but is hidden (since one objective is hidden for this class) # default content block (Verlagsinhalte) exists but is hidden (since one objective is hidden for this class)
self.assertEqual(hidden_custom['title'], 'Sprache & Kommunikation') self.assertEqual(hidden_custom['title'], 'Sprache & Kommunikation')

View File

@ -80,7 +80,7 @@ class TestSnapshotMigration(SkillboxTestCase):
}) })
module = result.data['module'] module = result.data['module']
chapter1 = module['chapters'][0] chapter1 = module['chapters'][0]
default_content, _, _ = chapter1['contentBlocks'] _, default_content, _ = chapter1['contentBlocks']
# default content block (Verlagsinhalte) exists but is hidden (since one objective is hidden for this class) # default content block (Verlagsinhalte) exists but is hidden (since one objective is hidden for this class)
self.assertEqual(default_content['title'], 'Gesellschaft') self.assertEqual(default_content['title'], 'Gesellschaft')
@ -95,7 +95,7 @@ class TestSnapshotMigration(SkillboxTestCase):
}) })
module = result.data['module'] module = result.data['module']
chapter1 = module['chapters'][0] chapter1 = module['chapters'][0]
_, custom, _ = chapter1['contentBlocks'] _, _, custom = chapter1['contentBlocks']
self.assertEqual(custom['title'], 'Gesellschaft') self.assertEqual(custom['title'], 'Gesellschaft')
self.assertTrue(custom['originalCreator'] is not None) self.assertTrue(custom['originalCreator'] is not None)
@ -110,7 +110,7 @@ class TestSnapshotMigration(SkillboxTestCase):
}) })
module = result.data['module'] module = result.data['module']
chapter1 = module['chapters'][0] chapter1 = module['chapters'][0]
_, _, hidden_custom = chapter1['contentBlocks'] hidden_custom, _, _ = chapter1['contentBlocks']
# default content block (Verlagsinhalte) exists but is hidden (since one objective is hidden for this class) # default content block (Verlagsinhalte) exists but is hidden (since one objective is hidden for this class)
self.assertEqual(hidden_custom['title'], 'Sprache & Kommunikation') self.assertEqual(hidden_custom['title'], 'Sprache & Kommunikation')
@ -127,7 +127,7 @@ class TestSnapshotMigration(SkillboxTestCase):
}) })
module = result.data['module'] module = result.data['module']
chapter1 = module['chapters'][0] chapter1 = module['chapters'][0]
default_content, _, _, _ = chapter1['contentBlocks'] _, default_content, _, _ = chapter1['contentBlocks']
# default content block (Verlagsinhalte) exists but is hidden (since one objective is hidden for this class) # default content block (Verlagsinhalte) exists but is hidden (since one objective is hidden for this class)
self.assertEqual(default_content['title'], 'Gesellschaft') self.assertEqual(default_content['title'], 'Gesellschaft')
@ -146,7 +146,7 @@ class TestSnapshotMigration(SkillboxTestCase):
}) })
module = result.data['module'] module = result.data['module']
chapter1 = module['chapters'][0] chapter1 = module['chapters'][0]
_, custom, _, _ = chapter1['contentBlocks'] _, _, custom, _ = chapter1['contentBlocks']
self.assertEqual(custom['title'], 'Gesellschaft') self.assertEqual(custom['title'], 'Gesellschaft')
self.assertTrue(custom['userCreated']) self.assertTrue(custom['userCreated'])
@ -165,7 +165,7 @@ class TestSnapshotMigration(SkillboxTestCase):
}) })
module = result.data['module'] module = result.data['module']
chapter1 = module['chapters'][0] chapter1 = module['chapters'][0]
_, _, hidden_custom, _ = chapter1['contentBlocks'] hidden_custom, _, _, _ = chapter1['contentBlocks']
# default content block (Verlagsinhalte) exists but is hidden (since one objective is hidden for this class) # default content block (Verlagsinhalte) exists but is hidden (since one objective is hidden for this class)
self.assertEqual(hidden_custom['title'], 'Sprache & Kommunikation') self.assertEqual(hidden_custom['title'], 'Sprache & Kommunikation')