Skip to content

Commit fe88cd4

Browse files
rtibblesbotclaude
andcommitted
feat: populate included_presets at publish
In create_associated_file_objects, set each renderable (non-supplementary) file's own-preset bit (2 ** RENDERABLE_PRESETS_ORDER.index(preset_id)) and leave supplementary files NULL. A preset missing from the append-only ordering is logged and skipped rather than aborting the channel publish. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent c50f3b0 commit fe88cd4

2 files changed

Lines changed: 33 additions & 0 deletions

File tree

contentcuration/contentcuration/tests/test_exportchannel.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from le_utils.constants import exercises
2020
from le_utils.constants import format_presets
2121
from le_utils.constants import modalities
22+
from le_utils.constants.format_presets import RENDERABLE_PRESETS_ORDER
2223
from le_utils.constants.labels import accessibility_categories
2324
from le_utils.constants.labels import learning_activities
2425
from le_utils.constants.labels import levels
@@ -505,6 +506,21 @@ def test_contentnode_file_size_data(self):
505506
for file in files.prefetch_related("local_file"):
506507
self.assertEqual(file.file_size, file.local_file.file_size)
507508

509+
def test_file_included_presets_renderable(self):
510+
# Every non-supplementary (renderable) exported file carries its own preset bit.
511+
files = kolibri_models.File.objects.filter(supplementary=False)
512+
assert files.count() > 0
513+
for file in files:
514+
expected = 2 ** RENDERABLE_PRESETS_ORDER.index(file.preset)
515+
self.assertEqual(file.included_presets, expected)
516+
517+
def test_file_included_presets_supplementary_null(self):
518+
# Supplementary files (e.g. thumbnails) leave included_presets NULL.
519+
files = kolibri_models.File.objects.filter(supplementary=True)
520+
assert files.count() > 0
521+
for file in files:
522+
self.assertIsNone(file.included_presets)
523+
508524
def test_channel_icon_encoding(self):
509525
self.assertIsNotNone(self.content_channel.icon_encoding)
510526

contentcuration/contentcuration/utils/publish.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from le_utils.constants import licenses
3939
from le_utils.constants import modalities
4040
from le_utils.constants import roles
41+
from le_utils.constants.format_presets import RENDERABLE_PRESETS_ORDER
4142
from search.models import ChannelFullTextSearch
4243
from search.models import ContentNodeFullTextSearch
4344
from search.utils import get_fts_annotated_channel_qs
@@ -666,6 +667,21 @@ def create_associated_file_objects(kolibrinode, ccnode):
666667
},
667668
)
668669

670+
included_presets = None
671+
if not preset.supplementary:
672+
try:
673+
included_presets = 2 ** RENDERABLE_PRESETS_ORDER.index(preset.pk)
674+
except ValueError:
675+
# Renderable preset not in the (append-only) ordering — e.g. a newer
676+
# le-utils preset. Log and leave included_presets NULL for this file
677+
# rather than aborting the whole channel publish.
678+
logging.warning(
679+
"Preset %s missing from RENDERABLE_PRESETS_ORDER; leaving "
680+
"included_presets NULL for file %s",
681+
preset.pk,
682+
ccfilemodel.pk,
683+
)
684+
669685
kolibrimodels.File.objects.create(
670686
pk=ccfilemodel.pk,
671687
checksum=ccfilemodel.checksum,
@@ -679,6 +695,7 @@ def create_associated_file_objects(kolibrinode, ccnode):
679695
thumbnail=preset.thumbnail,
680696
priority=preset.order,
681697
local_file=kolibrilocalfilemodel,
698+
included_presets=included_presets,
682699
)
683700

684701

0 commit comments

Comments
 (0)