openedx-learning 0.15.0__py2.py3-none-any.whl → 0.16.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.
- openedx_learning/__init__.py +1 -1
- {openedx_learning-0.15.0.dist-info → openedx_learning-0.16.0.dist-info}/METADATA +3 -3
- {openedx_learning-0.15.0.dist-info → openedx_learning-0.16.0.dist-info}/RECORD +11 -10
- openedx_tagging/core/tagging/api.py +32 -0
- openedx_tagging/core/tagging/migrations/0018_objecttag_is_copied.py +18 -0
- openedx_tagging/core/tagging/models/base.py +8 -0
- openedx_tagging/core/tagging/rest_api/v1/serializers.py +4 -1
- openedx_tagging/core/tagging/rest_api/v1/views.py +4 -0
- {openedx_learning-0.15.0.dist-info → openedx_learning-0.16.0.dist-info}/LICENSE.txt +0 -0
- {openedx_learning-0.15.0.dist-info → openedx_learning-0.16.0.dist-info}/WHEEL +0 -0
- {openedx_learning-0.15.0.dist-info → openedx_learning-0.16.0.dist-info}/top_level.txt +0 -0
openedx_learning/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: openedx-learning
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.16.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: edx-drf-extensions
|
|
22
21
|
Requires-Dist: celery
|
|
22
|
+
Requires-Dist: djangorestframework<4.0
|
|
23
23
|
Requires-Dist: attrs
|
|
24
24
|
Requires-Dist: Django<5.0
|
|
25
25
|
Requires-Dist: rules<4.0
|
|
26
|
-
Requires-Dist:
|
|
26
|
+
Requires-Dist: edx-drf-extensions
|
|
27
27
|
|
|
28
28
|
Open edX Learning Core (and Tagging)
|
|
29
29
|
====================================
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
openedx_learning/__init__.py,sha256=
|
|
1
|
+
openedx_learning/__init__.py,sha256=GHiA1alNbJesOg1QdBMkyg2GfyNlz1kwiYuSV7HUxcY,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
|
|
@@ -61,7 +61,7 @@ openedx_tagging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
61
61
|
openedx_tagging/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
62
62
|
openedx_tagging/core/tagging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
63
63
|
openedx_tagging/core/tagging/admin.py,sha256=Ngc2l9Mf6gkzmqu7aOwq-d0mgV8szx0GzSeuWFX7Kyg,1080
|
|
64
|
-
openedx_tagging/core/tagging/api.py,sha256=
|
|
64
|
+
openedx_tagging/core/tagging/api.py,sha256=HsdCmr1xv54fk6ggPuib3xKOwx5yJFbD3ZL4Lk5-d1o,19815
|
|
65
65
|
openedx_tagging/core/tagging/apps.py,sha256=-gp0VYqX4XQzwjjd-G68Ev2Op0INLh9Byz5UOqF5_7k,345
|
|
66
66
|
openedx_tagging/core/tagging/data.py,sha256=421EvmDzdM7H523dBVQk4J0W_UwTT4U5syqPRXUYK4g,1353
|
|
67
67
|
openedx_tagging/core/tagging/rules.py,sha256=Gzw2RCQxoAv2PpOwOWgpD17XoZfowlFnNgQqYn59q_g,6715
|
|
@@ -94,9 +94,10 @@ openedx_tagging/core/tagging/migrations/0014_minor_fixes.py,sha256=46_F-el1UylSR
|
|
|
94
94
|
openedx_tagging/core/tagging/migrations/0015_taxonomy_export_id.py,sha256=jhS-T8o2mwu61E7hPbjjE_6MPLKRPQFAVu7pJHZNRz4,1454
|
|
95
95
|
openedx_tagging/core/tagging/migrations/0016_object_tag_export_id.py,sha256=X62sThGNv_0_wCBhWPcYRcs90EJwz9km9Nszr9yMVkM,2371
|
|
96
96
|
openedx_tagging/core/tagging/migrations/0017_alter_tagimporttask_status.py,sha256=ljxONZOAGRW-tvPUIFmc0cNXN4Hoo3GJiyzGMncN6LI,567
|
|
97
|
+
openedx_tagging/core/tagging/migrations/0018_objecttag_is_copied.py,sha256=zmr4b65T0vX6fYc8MpvSmQnYkAiNMpx3RKEd5tudsl8,517
|
|
97
98
|
openedx_tagging/core/tagging/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
98
99
|
openedx_tagging/core/tagging/models/__init__.py,sha256=yYdOnthuc7EUdfEULtZgqRwn5Y4bbYQmJCjVZqR5GTM,236
|
|
99
|
-
openedx_tagging/core/tagging/models/base.py,sha256=
|
|
100
|
+
openedx_tagging/core/tagging/models/base.py,sha256=RX6rZcG7njrgLvM4huh79lC1vXzFDLxqElJqOU50Od8,39606
|
|
100
101
|
openedx_tagging/core/tagging/models/import_export.py,sha256=Aj0pleh0nh2LNS6zmdB1P4bpdgUMmvmobTkqBerORAI,4570
|
|
101
102
|
openedx_tagging/core/tagging/models/system_defined.py,sha256=_6LfvUZGEltvQMtm2OXy6TOLh3C8GnVTqtZDSAZW6K4,9062
|
|
102
103
|
openedx_tagging/core/tagging/models/utils.py,sha256=-A3Dj24twmTf65UB7G4WLvb_9qEvduEPIwahZ-FJDlg,1926
|
|
@@ -106,12 +107,12 @@ openedx_tagging/core/tagging/rest_api/urls.py,sha256=egXaRQv1EAgF04ThgVZBQuvLK1L
|
|
|
106
107
|
openedx_tagging/core/tagging/rest_api/utils.py,sha256=XZXixZ44vpNlxiyFplW8Lktyh_m1EfR3Y-tnyvA7acc,3620
|
|
107
108
|
openedx_tagging/core/tagging/rest_api/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
108
109
|
openedx_tagging/core/tagging/rest_api/v1/permissions.py,sha256=7HPE_NuKku_ISnkeE_HsFNXVYt0IbVkJN6M4wqwHGHU,2443
|
|
109
|
-
openedx_tagging/core/tagging/rest_api/v1/serializers.py,sha256=
|
|
110
|
+
openedx_tagging/core/tagging/rest_api/v1/serializers.py,sha256=D7brBbgmU7MnbU7Ln_xiLG-TtXno0aciE5AhjsXlClE,13903
|
|
110
111
|
openedx_tagging/core/tagging/rest_api/v1/urls.py,sha256=dNUKCtUCx_YzrwlbEbpDfjGVQbb2QdJ1VuJCkladj6E,752
|
|
111
|
-
openedx_tagging/core/tagging/rest_api/v1/views.py,sha256=
|
|
112
|
+
openedx_tagging/core/tagging/rest_api/v1/views.py,sha256=LA0EF7-p91JgVrLZpZaG474elKD1dswODGvoPIw47Mg,35837
|
|
112
113
|
openedx_tagging/core/tagging/rest_api/v1/views_import.py,sha256=kbHUPe5A6WaaJ3J1lFIcYCt876ecLNQfd19m7YYub6c,1470
|
|
113
|
-
openedx_learning-0.
|
|
114
|
-
openedx_learning-0.
|
|
115
|
-
openedx_learning-0.
|
|
116
|
-
openedx_learning-0.
|
|
117
|
-
openedx_learning-0.
|
|
114
|
+
openedx_learning-0.16.0.dist-info/LICENSE.txt,sha256=QTW2QN7q3XszgUAXm9Dzgtu5LXYKbR1SGnqMa7ufEuY,35139
|
|
115
|
+
openedx_learning-0.16.0.dist-info/METADATA,sha256=KFY2MnwnFFqI4lWfz9rVFuvcSvhhLWr9L26T-RIJRtI,8777
|
|
116
|
+
openedx_learning-0.16.0.dist-info/WHEEL,sha256=AHX6tWk3qWuce7vKLrj7lnulVHEdWoltgauo8bgCXgU,109
|
|
117
|
+
openedx_learning-0.16.0.dist-info/top_level.txt,sha256=IYFbr5mgiEHd-LOtZmXj3q3a0bkGK1M9LY7GXgnfi4M,33
|
|
118
|
+
openedx_learning-0.16.0.dist-info/RECORD,,
|
|
@@ -484,3 +484,35 @@ def delete_tags_from_taxonomy(
|
|
|
484
484
|
"""
|
|
485
485
|
taxonomy = taxonomy.cast()
|
|
486
486
|
taxonomy.delete_tags(tags, with_subtags)
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def copy_tags(source_object_id: str, dest_object_id: str):
|
|
490
|
+
"""
|
|
491
|
+
Copy all tags from one object to another.
|
|
492
|
+
|
|
493
|
+
This keeps all not-copied tags and delete all
|
|
494
|
+
previous copied tags of the dest object.
|
|
495
|
+
If there are not-copied tags that also are in 'source_object_id',
|
|
496
|
+
then they become copied.
|
|
497
|
+
"""
|
|
498
|
+
source_object_tags = get_object_tags(
|
|
499
|
+
source_object_id,
|
|
500
|
+
)
|
|
501
|
+
copied_tags = ObjectTag.objects.filter(
|
|
502
|
+
object_id=dest_object_id,
|
|
503
|
+
is_copied=True,
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
with transaction.atomic():
|
|
507
|
+
# Delete all copied tags of destination
|
|
508
|
+
copied_tags.delete()
|
|
509
|
+
|
|
510
|
+
# Copy an create object_tags in destination
|
|
511
|
+
for object_tag in source_object_tags:
|
|
512
|
+
ObjectTag.objects.update_or_create(
|
|
513
|
+
object_id=dest_object_id,
|
|
514
|
+
taxonomy_id=object_tag.taxonomy_id,
|
|
515
|
+
tag_id=object_tag.tag_id,
|
|
516
|
+
defaults={"is_copied": True},
|
|
517
|
+
# Note: _value and _export_id are set automatically
|
|
518
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Generated by Django 4.2.16 on 2024-10-04 19:21
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('oel_tagging', '0017_alter_tagimporttask_status'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AddField(
|
|
14
|
+
model_name='objecttag',
|
|
15
|
+
name='is_copied',
|
|
16
|
+
field=models.BooleanField(default=False, help_text="True if this object tag has been copied from one object to another using 'copy_tags' api function"),
|
|
17
|
+
),
|
|
18
|
+
]
|
|
@@ -795,6 +795,13 @@ class ObjectTag(models.Model):
|
|
|
795
795
|
"Tag associated with this object tag. Provides the tag's 'value' if set."
|
|
796
796
|
),
|
|
797
797
|
)
|
|
798
|
+
is_copied = models.BooleanField(
|
|
799
|
+
default=False,
|
|
800
|
+
help_text=_(
|
|
801
|
+
"True if this object tag has been copied from one object to another"
|
|
802
|
+
" using 'copy_tags' api function"
|
|
803
|
+
),
|
|
804
|
+
)
|
|
798
805
|
_export_id = case_insensitive_char_field(
|
|
799
806
|
max_length=255,
|
|
800
807
|
help_text=_(
|
|
@@ -981,6 +988,7 @@ class ObjectTag(models.Model):
|
|
|
981
988
|
self.tag = object_tag.tag
|
|
982
989
|
self.taxonomy = object_tag.taxonomy
|
|
983
990
|
self.object_id = object_tag.object_id
|
|
991
|
+
self.is_copied = object_tag.is_copied
|
|
984
992
|
self._value = object_tag._value # pylint: disable=protected-access
|
|
985
993
|
self._export_id = object_tag._export_id # pylint: disable=protected-access
|
|
986
994
|
return self
|
|
@@ -176,6 +176,9 @@ class ObjectTagsByTaxonomySerializer(UserPermissionsSerializerMixin, serializers
|
|
|
176
176
|
"""
|
|
177
177
|
Convert this list of ObjectTags to the serialized dictionary, grouped by Taxonomy
|
|
178
178
|
"""
|
|
179
|
+
# Allows consumers like edx-platform to override this
|
|
180
|
+
ObjectTagViewMinimalSerializer = self.context["view"].minimal_serializer_class
|
|
181
|
+
|
|
179
182
|
can_tag_object_perm = f"{self.app_label}.can_tag_object"
|
|
180
183
|
by_object: dict[str, dict[str, Any]] = {}
|
|
181
184
|
for obj_tag in instance:
|
|
@@ -194,7 +197,7 @@ class ObjectTagsByTaxonomySerializer(UserPermissionsSerializerMixin, serializers
|
|
|
194
197
|
"export_id": obj_tag.export_id,
|
|
195
198
|
}
|
|
196
199
|
taxonomies.append(tax_entry)
|
|
197
|
-
tax_entry["tags"].append(
|
|
200
|
+
tax_entry["tags"].append(ObjectTagViewMinimalSerializer(obj_tag, context=self.context).data)
|
|
198
201
|
return by_object
|
|
199
202
|
|
|
200
203
|
|
|
@@ -37,6 +37,7 @@ from ..utils import view_auth_classes
|
|
|
37
37
|
from .permissions import ObjectTagObjectPermissions, TaxonomyObjectPermissions, TaxonomyTagsObjectPermissions
|
|
38
38
|
from .serializers import (
|
|
39
39
|
ObjectTagListQueryParamsSerializer,
|
|
40
|
+
ObjectTagMinimalSerializer,
|
|
40
41
|
ObjectTagsByTaxonomySerializer,
|
|
41
42
|
ObjectTagSerializer,
|
|
42
43
|
ObjectTagUpdateBodySerializer,
|
|
@@ -443,7 +444,10 @@ class ObjectTagView(
|
|
|
443
444
|
* 405 - Method not allowed
|
|
444
445
|
"""
|
|
445
446
|
|
|
447
|
+
# Serializer used in `get_queryset` when getting tags per taxonomy
|
|
446
448
|
serializer_class = ObjectTagSerializer
|
|
449
|
+
# Serializer used in the result in `to_representation` in `ObjectTagsByTaxonomySerializer`
|
|
450
|
+
minimal_serializer_class = ObjectTagMinimalSerializer
|
|
447
451
|
permission_classes = [ObjectTagObjectPermissions]
|
|
448
452
|
lookup_field = "object_id"
|
|
449
453
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|