Skip to content
Draft
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
2 changes: 1 addition & 1 deletion src/openedx_content/applets/backup_restore/zipper.py
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ def _save_collections(self, learning_package, collections):
)
collection = collections_api.add_to_collection(
learning_package_id=learning_package.id,
key=collection.key,
collection_code=collection.key,
entities_qset=publishing_api.get_publishable_entities(learning_package.id).filter(key__in=entities)
)

Expand Down
34 changes: 17 additions & 17 deletions src/openedx_content/applets/collections/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

def create_collection(
learning_package_id: int,
key: str,
collection_code: str,
*,
title: str,
created_by: int | None,
Expand All @@ -45,7 +45,7 @@ def create_collection(
"""
collection = Collection.objects.create(
learning_package_id=learning_package_id,
key=key,
collection_code=collection_code,
title=title,
created_by_id=created_by,
description=description,
Expand All @@ -54,24 +54,24 @@ def create_collection(
return collection


def get_collection(learning_package_id: int, collection_key: str) -> Collection:
def get_collection(learning_package_id: int, collection_code: str) -> Collection:
"""
Get a Collection by ID
"""
return Collection.objects.get_by_key(learning_package_id, collection_key)
return Collection.objects.get_by_code(learning_package_id, collection_code)


def update_collection(
learning_package_id: int,
key: str,
collection_code: str,
*,
title: str | None = None,
description: str | None = None,
) -> Collection:
"""
Update a Collection identified by the learning_package_id + key.
Update a Collection identified by the learning_package_id + collection_code.
"""
collection = get_collection(learning_package_id, key)
collection = get_collection(learning_package_id, collection_code)

# If no changes were requested, there's nothing to update, so just return
# the Collection as-is
Expand All @@ -89,17 +89,17 @@ def update_collection(

def delete_collection(
learning_package_id: int,
key: str,
collection_code: str,
*,
hard_delete=False,
) -> Collection:
"""
Disables or deletes a collection identified by the given learning_package + key.
Disables or deletes a collection identified by the given learning_package + collection_code.

By default (hard_delete=False), the collection is "soft deleted", i.e disabled.
Soft-deleted collections can be re-enabled using restore_collection.
"""
collection = get_collection(learning_package_id, key)
collection = get_collection(learning_package_id, collection_code)

if hard_delete:
collection.delete()
Expand All @@ -111,12 +111,12 @@ def delete_collection(

def restore_collection(
learning_package_id: int,
key: str,
collection_code: str,
) -> Collection:
"""
Undo a "soft delete" by re-enabling a Collection.
"""
collection = get_collection(learning_package_id, key)
collection = get_collection(learning_package_id, collection_code)

collection.enabled = True
collection.save()
Expand All @@ -125,7 +125,7 @@ def restore_collection(

def add_to_collection(
learning_package_id: int,
key: str,
collection_code: str,
entities_qset: QuerySet[PublishableEntity],
created_by: int | None = None,
) -> Collection:
Expand All @@ -145,10 +145,10 @@ def add_to_collection(
if invalid_entity:
raise ValidationError(
f"Cannot add entity {invalid_entity.pk} in learning package {invalid_entity.learning_package_id} "
f"to collection {key} in learning package {learning_package_id}."
f"to collection {collection_code} in learning package {learning_package_id}."
)

collection = get_collection(learning_package_id, key)
collection = get_collection(learning_package_id, collection_code)
collection.entities.add(
*entities_qset.all(),
through_defaults={"created_by_id": created_by},
Expand All @@ -161,7 +161,7 @@ def add_to_collection(

def remove_from_collection(
learning_package_id: int,
key: str,
collection_code: str,
entities_qset: QuerySet[PublishableEntity],
) -> Collection:
"""
Expand All @@ -173,7 +173,7 @@ def remove_from_collection(

Returns the updated Collection.
"""
collection = get_collection(learning_package_id, key)
collection = get_collection(learning_package_id, collection_code)

collection.entities.remove(*entities_qset.all())
collection.modified = datetime.now(tz=timezone.utc)
Expand Down
18 changes: 11 additions & 7 deletions src/openedx_content/applets/collections/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@
from django.db import models
from django.utils.translation import gettext_lazy as _

from openedx_django_lib.fields import MultiCollationTextField, case_insensitive_char_field, key_field
from openedx_django_lib.fields import (
MultiCollationTextField, case_insensitive_char_field, case_sensitive_slug_field
)
from openedx_django_lib.validators import validate_utc_datetime

from ..publishing.models import LearningPackage, PublishableEntity
Expand All @@ -85,12 +87,12 @@ class CollectionManager(models.Manager):
"""
Custom manager for Collection class.
"""
def get_by_key(self, learning_package_id: int, key: str):
def get_by_code(self, learning_package_id: int, collection_code: str):
"""
Get the Collection for the given Learning Package + key.
Get the Collection for the given Learning Package + collection code.
"""
return self.select_related('learning_package') \
.get(learning_package_id=learning_package_id, key=key)
.get(learning_package_id=learning_package_id, collection_code=collection_code)


class Collection(models.Model):
Expand All @@ -105,10 +107,12 @@ class Collection(models.Model):
learning_package = models.ForeignKey(LearningPackage, on_delete=models.CASCADE)

# Every collection is uniquely and permanently identified within its learning package
# by a 'key' that is set during creation. Both will appear in the
# by a 'code' that is set during creation. Both will appear in the
# collection's opaque key:
# e.g. "lib-collection:lib:key" is the opaque key for a library collection.
key = key_field(db_column='_key')
# e.g. "lib-collection:{org_code}:{library_code}:{collection_code}"
# is the opaque key for a library collection.
# Formerly known as the "key", hence the column name.
code = case_sensitive_slug_field(db_column='_key')

title = case_insensitive_char_field(
null=False,
Expand Down
4 changes: 2 additions & 2 deletions src/openedx_content/applets/components/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ def get_components( # pylint: disable=too-many-positional-arguments

def get_collection_components(
learning_package_id: int,
collection_key: str,
collection_code: str,
) -> QuerySet[Component]:
"""
Returns a QuerySet of Components relating to the PublishableEntities in a Collection.
Expand All @@ -445,7 +445,7 @@ def get_collection_components(
"""
return Component.objects.filter(
learning_package_id=learning_package_id,
publishable_entity__collections__key=collection_key,
publishable_entity__collections__collection_code=collection_code,
).order_by('pk')


Expand Down
4 changes: 2 additions & 2 deletions src/openedx_content/applets/publishing/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1745,7 +1745,7 @@ def get_containers(

def get_collection_containers(
learning_package_id: int,
collection_key: str,
collection_code: str,
) -> QuerySet[Container]:
"""
Returns a QuerySet of Containers relating to the PublishableEntities in a Collection.
Expand All @@ -1754,7 +1754,7 @@ def get_collection_containers(
"""
return Container.objects.filter(
publishable_entity__learning_package_id=learning_package_id,
publishable_entity__collections__key=collection_key,
publishable_entity__collections__collection_code=collection_code,
).order_by('pk')


Expand Down
28 changes: 28 additions & 0 deletions src/openedx_django_lib/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,26 @@ def case_sensitive_char_field(**kwargs) -> MultiCollationCharField:
return MultiCollationCharField(**final_kwargs)


def case_sensitive_slug_field(**kwargs) -> MultiCollationCharField:
"""
Return a case-sensitive ``MultiCollationSlugField``.

See `case_sensitive_slug_field`.
"""
# Set our default arguments
final_kwargs = {
"null": False,
"db_collations": {
"sqlite": "BINARY",
"mysql": "utf8mb4_bin",
},
}
# Override our defaults with whatever is passed in.
final_kwargs.update(kwargs)

return MultiCollationSlugField(**final_kwargs)


def immutable_uuid_field() -> models.UUIDField:
"""
Stable, randomly-generated UUIDs.
Expand Down Expand Up @@ -198,6 +218,14 @@ class MultiCollationCharField(MultiCollationMixin, models.CharField):
"""


class MultiCollationSlugField(MultiCollationMixin, models.SlugField):
"""
SlugField subclass with per-database-vendor collation settings.

See `MultiCollationCharField`.
"""


class MultiCollationTextField(MultiCollationMixin, models.TextField):
"""
TextField subclass with per-database-vendor collation settings.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def setUpTestData(cls):

cls.collection = api.create_collection(
cls.learning_package.id,
key="COL1",
collection_code="COL1",
created_by=cls.user.id,
title="Collection 1",
description="Description of Collection 1",
Expand Down
Loading
Loading