openedx-learning 0.14.0__py2.py3-none-any.whl → 0.15.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
1
  """
2
2
  Open edX Learning ("Learning Core").
3
3
  """
4
- __version__ = "0.14.0"
4
+ __version__ = "0.15.0"
@@ -12,16 +12,18 @@ are stored in this app.
12
12
  """
13
13
  from __future__ import annotations
14
14
 
15
- from datetime import datetime
15
+ from datetime import datetime, timezone
16
16
  from enum import StrEnum, auto
17
17
  from logging import getLogger
18
18
  from pathlib import Path
19
19
  from uuid import UUID
20
20
 
21
+ from django.core.exceptions import ValidationError
21
22
  from django.db.models import Q, QuerySet
22
23
  from django.db.transaction import atomic
23
24
  from django.http.response import HttpResponse, HttpResponseNotFound
24
25
 
26
+ from ..collections.models import Collection, CollectionPublishableEntity
25
27
  from ..contents import api as contents_api
26
28
  from ..publishing import api as publishing_api
27
29
  from .models import Component, ComponentType, ComponentVersion, ComponentVersionContent
@@ -48,6 +50,7 @@ __all__ = [
48
50
  "look_up_component_version_content",
49
51
  "AssetError",
50
52
  "get_redirect_response_for_component_asset",
53
+ "set_collections",
51
54
  ]
52
55
 
53
56
 
@@ -603,3 +606,54 @@ def get_redirect_response_for_component_asset(
603
606
  )
604
607
 
605
608
  return HttpResponse(headers={**info_headers, **redirect_headers})
609
+
610
+
611
+ def set_collections(
612
+ learning_package_id: int,
613
+ component: Component,
614
+ collection_qset: QuerySet[Collection],
615
+ created_by: int | None = None,
616
+ ) -> set[Collection]:
617
+ """
618
+ Set collections for a given component.
619
+
620
+ These Collections must belong to the same LearningPackage as the Component, or a ValidationError will be raised.
621
+
622
+ Modified date of all collections related to component is updated.
623
+
624
+ Returns the updated collections.
625
+ """
626
+ # Disallow adding entities outside the collection's learning package
627
+ invalid_collection = collection_qset.exclude(learning_package_id=learning_package_id).first()
628
+ if invalid_collection:
629
+ raise ValidationError(
630
+ f"Cannot add collection {invalid_collection.pk} in learning package "
631
+ f"{invalid_collection.learning_package_id} to component {component} in "
632
+ f"learning package {learning_package_id}."
633
+ )
634
+ current_relations = CollectionPublishableEntity.objects.filter(
635
+ entity=component.publishable_entity
636
+ ).select_related('collection')
637
+ # Clear other collections for given component and add only new collections from collection_qset
638
+ removed_collections = set(
639
+ r.collection for r in current_relations.exclude(collection__in=collection_qset)
640
+ )
641
+ new_collections = set(collection_qset.exclude(
642
+ id__in=current_relations.values_list('collection', flat=True)
643
+ ))
644
+ # Use `remove` instead of `CollectionPublishableEntity.delete()` to trigger m2m_changed signal which will handle
645
+ # updating component index.
646
+ component.publishable_entity.collections.remove(*removed_collections)
647
+ component.publishable_entity.collections.add(
648
+ *new_collections,
649
+ through_defaults={"created_by_id": created_by},
650
+ )
651
+ # Update modified date via update to avoid triggering post_save signal for collections
652
+ # The signal triggers index update for each collection synchronously which will be very slow in this case.
653
+ # Instead trigger the index update in the caller function asynchronously.
654
+ affected_collection = removed_collections | new_collections
655
+ Collection.objects.filter(
656
+ id__in=[collection.id for collection in affected_collection]
657
+ ).update(modified=datetime.now(tz=timezone.utc))
658
+
659
+ return affected_collection
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: openedx-learning
3
- Version: 0.14.0
3
+ Version: 0.15.0
4
4
  Summary: Open edX Learning Core and Tagging.
5
5
  Home-page: https://github.com/openedx/openedx-learning
6
6
  Author: David Ormsbee
@@ -18,12 +18,12 @@ Classifier: Programming Language :: Python :: 3.11
18
18
  Classifier: Programming Language :: Python :: 3.12
19
19
  Requires-Python: >=3.11
20
20
  License-File: LICENSE.txt
21
- Requires-Dist: attrs
21
+ Requires-Dist: edx-drf-extensions
22
22
  Requires-Dist: celery
23
- Requires-Dist: djangorestframework<4.0
24
- Requires-Dist: rules<4.0
23
+ Requires-Dist: attrs
25
24
  Requires-Dist: Django<5.0
26
- Requires-Dist: edx-drf-extensions
25
+ Requires-Dist: rules<4.0
26
+ Requires-Dist: djangorestframework<4.0
27
27
 
28
28
  Open edX Learning Core (and Tagging)
29
29
  ====================================
@@ -1,4 +1,4 @@
1
- openedx_learning/__init__.py,sha256=SLzxSo0bKJgEYEmz17a3mAeKVzFkuo0Z1DWkbFaBRxc,68
1
+ openedx_learning/__init__.py,sha256=fJlzpA8ZDMYQSqZnlzD-fmJITs1x7c9Xb6Nk13d6cVE,68
2
2
  openedx_learning/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  openedx_learning/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  openedx_learning/api/authoring.py,sha256=vbRpiQ2wOfN3oR2bbN0-bI2ra0QRtha9tVixKW1ENis,929
@@ -18,7 +18,7 @@ openedx_learning/apps/authoring/collections/migrations/0005_alter_collection_opt
18
18
  openedx_learning/apps/authoring/collections/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  openedx_learning/apps/authoring/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  openedx_learning/apps/authoring/components/admin.py,sha256=3kFu_PR0BFb8U0zVv3WWhi27i__TDuJG0pFlwr3tKAw,4614
21
- openedx_learning/apps/authoring/components/api.py,sha256=pm5kM3YG6-o6BK_UQvR55NLKfGV6OdawVML_9fbJJ7o,23285
21
+ openedx_learning/apps/authoring/components/api.py,sha256=TCLFCPb7ScoRWhe7YpQ7hBiJGjoa47OmazQWcN2hIwU,25759
22
22
  openedx_learning/apps/authoring/components/apps.py,sha256=YoYPsI9gcleA3uEs8CiLIrjUncRMo2DKbYt4mDfzePg,770
23
23
  openedx_learning/apps/authoring/components/models.py,sha256=T-wc7vxaWMlulQmMsVH7m6Pd857P3Eguo0vtflTLURI,13415
24
24
  openedx_learning/apps/authoring/components/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -110,8 +110,8 @@ openedx_tagging/core/tagging/rest_api/v1/serializers.py,sha256=gbvEBLvsmfPc3swWz
110
110
  openedx_tagging/core/tagging/rest_api/v1/urls.py,sha256=dNUKCtUCx_YzrwlbEbpDfjGVQbb2QdJ1VuJCkladj6E,752
111
111
  openedx_tagging/core/tagging/rest_api/v1/views.py,sha256=ZRkSILdb8g5k_BcuuVVfdffEdY9vFQ_YtMa3JrN0Xz8,35581
112
112
  openedx_tagging/core/tagging/rest_api/v1/views_import.py,sha256=kbHUPe5A6WaaJ3J1lFIcYCt876ecLNQfd19m7YYub6c,1470
113
- openedx_learning-0.14.0.dist-info/LICENSE.txt,sha256=QTW2QN7q3XszgUAXm9Dzgtu5LXYKbR1SGnqMa7ufEuY,35139
114
- openedx_learning-0.14.0.dist-info/METADATA,sha256=lhKFt6PNk29mBmXqRSrTIl8cFuONTNjSHjs7d0xQHKo,8777
115
- openedx_learning-0.14.0.dist-info/WHEEL,sha256=AHX6tWk3qWuce7vKLrj7lnulVHEdWoltgauo8bgCXgU,109
116
- openedx_learning-0.14.0.dist-info/top_level.txt,sha256=IYFbr5mgiEHd-LOtZmXj3q3a0bkGK1M9LY7GXgnfi4M,33
117
- openedx_learning-0.14.0.dist-info/RECORD,,
113
+ openedx_learning-0.15.0.dist-info/LICENSE.txt,sha256=QTW2QN7q3XszgUAXm9Dzgtu5LXYKbR1SGnqMa7ufEuY,35139
114
+ openedx_learning-0.15.0.dist-info/METADATA,sha256=6cN09KcBqzdTVsxN7te-_yMVFRCPHldYO9rE81dtEWI,8777
115
+ openedx_learning-0.15.0.dist-info/WHEEL,sha256=AHX6tWk3qWuce7vKLrj7lnulVHEdWoltgauo8bgCXgU,109
116
+ openedx_learning-0.15.0.dist-info/top_level.txt,sha256=IYFbr5mgiEHd-LOtZmXj3q3a0bkGK1M9LY7GXgnfi4M,33
117
+ openedx_learning-0.15.0.dist-info/RECORD,,