openedx-learning 0.3.3__py2.py3-none-any.whl → 0.3.5__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.3.3.dist-info → openedx_learning-0.3.5.dist-info}/METADATA +7 -6
- {openedx_learning-0.3.3.dist-info → openedx_learning-0.3.5.dist-info}/RECORD +8 -8
- openedx_tagging/core/tagging/api.py +37 -1
- openedx_tagging/core/tagging/rest_api/v1/views.py +9 -15
- {openedx_learning-0.3.3.dist-info → openedx_learning-0.3.5.dist-info}/LICENSE.txt +0 -0
- {openedx_learning-0.3.3.dist-info → openedx_learning-0.3.5.dist-info}/WHEEL +0 -0
- {openedx_learning-0.3.3.dist-info → openedx_learning-0.3.5.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.
|
|
3
|
+
Version: 0.3.5
|
|
4
4
|
Summary: An experiment.
|
|
5
5
|
Home-page: https://github.com/openedx/openedx-learning
|
|
6
6
|
Author: David Ormsbee
|
|
@@ -17,12 +17,12 @@ Classifier: Natural Language :: English
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.8
|
|
19
19
|
Requires-Python: >=3.8
|
|
20
|
-
Requires-Dist: Django (<5.0)
|
|
21
|
-
Requires-Dist: celery
|
|
22
|
-
Requires-Dist: attrs
|
|
23
|
-
Requires-Dist: edx-drf-extensions
|
|
24
20
|
Requires-Dist: djangorestframework (<4.0)
|
|
21
|
+
Requires-Dist: celery
|
|
25
22
|
Requires-Dist: rules (<4.0)
|
|
23
|
+
Requires-Dist: Django (<5.0)
|
|
24
|
+
Requires-Dist: edx-drf-extensions
|
|
25
|
+
Requires-Dist: attrs
|
|
26
26
|
|
|
27
27
|
openedx-learning
|
|
28
28
|
=============================
|
|
@@ -210,9 +210,10 @@ Change Log
|
|
|
210
210
|
Unreleased
|
|
211
211
|
~~~~~~~~~~
|
|
212
212
|
|
|
213
|
+
* Removed usage of ``tox-battery`` and added support for ``tox 4.0``
|
|
213
214
|
* Switch from ``edx-sphinx-theme`` to ``sphinx-book-theme`` since the former is
|
|
214
215
|
deprecated. See https://github.com/openedx/edx-sphinx-theme/issues/184 for
|
|
215
|
-
more details.
|
|
216
|
+
more details.
|
|
216
217
|
|
|
217
218
|
[0.1.0] - 2021-08-08
|
|
218
219
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
openedx_learning/__init__.py,sha256=
|
|
1
|
+
openedx_learning/__init__.py,sha256=Ho0NTFgVeG0pxXs5miy44Bt4snnD-dyzeE4yKvRL1bE,67
|
|
2
2
|
openedx_learning/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
openedx_learning/contrib/media_server/__init__.py,sha256=iYijWFCl5RNR9omSu22kMl49EfponoqXBqXr0HMp4QI,56
|
|
4
4
|
openedx_learning/contrib/media_server/apps.py,sha256=FPT0rsUFtPyhFpWKjSI1e_s58wU0IbDyaAW_66V6sY4,816
|
|
@@ -43,7 +43,7 @@ openedx_tagging/__init__.py,sha256=V9N8M7f9LYlAbA_DdPUsHzTnWjYRXKGa5qHw9P1JnNI,3
|
|
|
43
43
|
openedx_tagging/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
44
44
|
openedx_tagging/core/tagging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
45
|
openedx_tagging/core/tagging/admin.py,sha256=5mSTxlftMq5MVGzdE8xl3AcxgtfpnrKpXPNdvw7hAno,1075
|
|
46
|
-
openedx_tagging/core/tagging/api.py,sha256=
|
|
46
|
+
openedx_tagging/core/tagging/api.py,sha256=KFXA3x06ahaUeYRVZ9ZaELCJogDedm3lqFj558FjCK8,13840
|
|
47
47
|
openedx_tagging/core/tagging/apps.py,sha256=-gp0VYqX4XQzwjjd-G68Ev2Op0INLh9Byz5UOqF5_7k,345
|
|
48
48
|
openedx_tagging/core/tagging/data.py,sha256=C5uRsR3she-iB3n1OyPdQm6PsIc1Lg-XOI7HT0yNLPI,1327
|
|
49
49
|
openedx_tagging/core/tagging/rules.py,sha256=vhBaVpRBAjsx7B2tbeFqorU3H-LZgWERlbpFKFaEeM4,6113
|
|
@@ -87,10 +87,10 @@ openedx_tagging/core/tagging/rest_api/v1/permissions.py,sha256=FeSulmsFD7wAAuYpx
|
|
|
87
87
|
openedx_tagging/core/tagging/rest_api/v1/serializers.py,sha256=HMDWJ--VoRx1KfmCIRdjHsz8yNUHDYT3BoxsU_kZbiY,8558
|
|
88
88
|
openedx_tagging/core/tagging/rest_api/v1/urls.py,sha256=dNUKCtUCx_YzrwlbEbpDfjGVQbb2QdJ1VuJCkladj6E,752
|
|
89
89
|
openedx_tagging/core/tagging/rest_api/v1/utils.py,sha256=PR8ERgXaL4_r9kiOLpOSgGdtK7oA6jIP74ej2gDWCUs,822
|
|
90
|
-
openedx_tagging/core/tagging/rest_api/v1/views.py,sha256
|
|
90
|
+
openedx_tagging/core/tagging/rest_api/v1/views.py,sha256=MixTy8JSnJ6-F6Km-udNDh-SY6IKcijGILP7Q3aWCvc,28636
|
|
91
91
|
openedx_tagging/core/tagging/rest_api/v1/views_import.py,sha256=kbHUPe5A6WaaJ3J1lFIcYCt876ecLNQfd19m7YYub6c,1470
|
|
92
|
-
openedx_learning-0.3.
|
|
93
|
-
openedx_learning-0.3.
|
|
94
|
-
openedx_learning-0.3.
|
|
95
|
-
openedx_learning-0.3.
|
|
96
|
-
openedx_learning-0.3.
|
|
92
|
+
openedx_learning-0.3.5.dist-info/LICENSE.txt,sha256=QTW2QN7q3XszgUAXm9Dzgtu5LXYKbR1SGnqMa7ufEuY,35139
|
|
93
|
+
openedx_learning-0.3.5.dist-info/METADATA,sha256=Fs4z5Sc1ZxmTxQSamHeACYCb1JjoQx9V8ecsI2RjXPo,8758
|
|
94
|
+
openedx_learning-0.3.5.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110
|
|
95
|
+
openedx_learning-0.3.5.dist-info/top_level.txt,sha256=IYFbr5mgiEHd-LOtZmXj3q3a0bkGK1M9LY7GXgnfi4M,33
|
|
96
|
+
openedx_learning-0.3.5.dist-info/RECORD,,
|
|
@@ -12,6 +12,8 @@ are stored in this app.
|
|
|
12
12
|
"""
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
15
17
|
from django.db import models, transaction
|
|
16
18
|
from django.db.models import F, QuerySet, Value
|
|
17
19
|
from django.db.models.functions import Coalesce, Concat, Lower
|
|
@@ -157,6 +159,7 @@ def resync_object_tags(object_tags: QuerySet | None = None) -> int:
|
|
|
157
159
|
def get_object_tags(
|
|
158
160
|
object_id: str,
|
|
159
161
|
taxonomy_id: int | None = None,
|
|
162
|
+
include_deleted: bool = False,
|
|
160
163
|
object_tag_class: type[ObjectTag] = ObjectTag
|
|
161
164
|
) -> QuerySet[ObjectTag]:
|
|
162
165
|
"""
|
|
@@ -165,8 +168,16 @@ def get_object_tags(
|
|
|
165
168
|
Pass taxonomy_id to limit the returned object_tags to a specific taxonomy.
|
|
166
169
|
"""
|
|
167
170
|
filters = {"taxonomy_id": taxonomy_id} if taxonomy_id else {}
|
|
171
|
+
base_qs = (
|
|
172
|
+
object_tag_class.objects
|
|
173
|
+
.filter(object_id=object_id, **filters)
|
|
174
|
+
.exclude(taxonomy__enabled=False) # Exclude if the whole taxonomy is disabled
|
|
175
|
+
)
|
|
176
|
+
if not include_deleted:
|
|
177
|
+
base_qs = base_qs.exclude(taxonomy_id=None) # Exclude if the whole taxonomy was deleted
|
|
178
|
+
base_qs = base_qs.exclude(tag_id=None, taxonomy__allow_free_text=False) # Exclude if just the tag is deleted
|
|
168
179
|
tags = (
|
|
169
|
-
|
|
180
|
+
base_qs
|
|
170
181
|
# Preload related objects, including data for the "get_lineage" method on ObjectTag/Tag:
|
|
171
182
|
.select_related("taxonomy", "tag", "tag__parent", "tag__parent__parent")
|
|
172
183
|
# Sort the tags within each taxonomy in "tree order". See Taxonomy._get_filtered_tags_deep for details on this:
|
|
@@ -185,6 +196,31 @@ def get_object_tags(
|
|
|
185
196
|
return tags
|
|
186
197
|
|
|
187
198
|
|
|
199
|
+
def get_object_tag_counts(object_id_pattern: str) -> dict[str, int]:
|
|
200
|
+
"""
|
|
201
|
+
Given an object ID, a "starts with" glob pattern like
|
|
202
|
+
"course-v1:foo+bar+baz@*", or a list of "comma,separated,IDs", return a
|
|
203
|
+
dict of matching object IDs and how many tags each object has.
|
|
204
|
+
|
|
205
|
+
Deleted tags and disabled taxonomies are excluded from the counts, even if
|
|
206
|
+
ObjectTag data about them is present.
|
|
207
|
+
"""
|
|
208
|
+
# Note: in the future we may add an option to exclude system taxonomies from the count.
|
|
209
|
+
qs: Any = ObjectTag.objects
|
|
210
|
+
if object_id_pattern.endswith("*"):
|
|
211
|
+
qs = qs.filter(object_id__startswith=object_id_pattern[0:len(object_id_pattern) - 1])
|
|
212
|
+
elif "*" in object_id_pattern:
|
|
213
|
+
raise ValueError("Wildcard matches are only supported if the * is at the end.")
|
|
214
|
+
else:
|
|
215
|
+
qs = qs.filter(object_id__in=object_id_pattern.split(","))
|
|
216
|
+
# Don't include deleted tags or disabled taxonomies:
|
|
217
|
+
qs = qs.exclude(taxonomy_id=None) # The whole taxonomy was deleted
|
|
218
|
+
qs = qs.exclude(taxonomy__enabled=False) # The whole taxonomy is disabled
|
|
219
|
+
qs = qs.exclude(tag_id=None, taxonomy__allow_free_text=False) # The taxonomy exists but the tag is deleted
|
|
220
|
+
qs = qs.values("object_id").annotate(num_tags=models.Count("id")).order_by("object_id")
|
|
221
|
+
return {row["object_id"]: row["num_tags"] for row in qs}
|
|
222
|
+
|
|
223
|
+
|
|
188
224
|
def delete_object_tags(object_id: str):
|
|
189
225
|
"""
|
|
190
226
|
Delete all ObjectTag entries for a given object.
|
|
@@ -3,8 +3,6 @@ Tagging API Views
|
|
|
3
3
|
"""
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
-
from typing import Any
|
|
7
|
-
|
|
8
6
|
from django.db import models
|
|
9
7
|
from django.http import Http404, HttpResponse
|
|
10
8
|
from rest_framework import mixins, status
|
|
@@ -20,6 +18,7 @@ from ...api import (
|
|
|
20
18
|
add_tag_to_taxonomy,
|
|
21
19
|
create_taxonomy,
|
|
22
20
|
delete_tags_from_taxonomy,
|
|
21
|
+
get_object_tag_counts,
|
|
23
22
|
get_object_tags,
|
|
24
23
|
get_taxonomies,
|
|
25
24
|
get_taxonomy,
|
|
@@ -29,7 +28,7 @@ from ...api import (
|
|
|
29
28
|
from ...data import TagDataQuerySet
|
|
30
29
|
from ...import_export.api import export_tags, get_last_import_log, import_tags
|
|
31
30
|
from ...import_export.parsers import ParserFormat
|
|
32
|
-
from ...models import
|
|
31
|
+
from ...models import Taxonomy
|
|
33
32
|
from ...rules import ObjectTagPermissionItem
|
|
34
33
|
from ..paginators import TAGS_THRESHOLD, DisabledTagsPagination, TagsPagination
|
|
35
34
|
from .permissions import ObjectTagObjectPermissions, TagObjectPermissions, TaxonomyObjectPermissions
|
|
@@ -295,7 +294,8 @@ class TaxonomyView(ModelViewSet):
|
|
|
295
294
|
@action(detail=True, url_path="tags/import", methods=["put"])
|
|
296
295
|
def update_import(self, request: Request, **_kwargs) -> Response:
|
|
297
296
|
"""
|
|
298
|
-
Imports tags from the uploaded file to an already created taxonomy
|
|
297
|
+
Imports tags from the uploaded file to an already created taxonomy,
|
|
298
|
+
overwriting any existing tags.
|
|
299
299
|
"""
|
|
300
300
|
body = TaxonomyImportBodySerializer(data=request.data)
|
|
301
301
|
body.is_valid(raise_exception=True)
|
|
@@ -305,7 +305,7 @@ class TaxonomyView(ModelViewSet):
|
|
|
305
305
|
|
|
306
306
|
taxonomy = self.get_object()
|
|
307
307
|
try:
|
|
308
|
-
import_success = import_tags(taxonomy, file, parser_format)
|
|
308
|
+
import_success = import_tags(taxonomy, file, parser_format, replace=True)
|
|
309
309
|
|
|
310
310
|
if import_success:
|
|
311
311
|
serializer = self.get_serializer(taxonomy)
|
|
@@ -517,16 +517,10 @@ class ObjectTagCountsView(
|
|
|
517
517
|
"""
|
|
518
518
|
# This API does NOT bother doing any permission checks as the # of tags is not considered sensitive information.
|
|
519
519
|
object_id_pattern = self.kwargs["object_id_pattern"]
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
raise ValidationError("Wildcard matches are only supported if the * is at the end.")
|
|
525
|
-
else:
|
|
526
|
-
qs = qs.filter(object_id__in=object_id_pattern.split(","))
|
|
527
|
-
|
|
528
|
-
qs = qs.values("object_id").annotate(num_tags=models.Count("id")).order_by("object_id")
|
|
529
|
-
return Response({row["object_id"]: row["num_tags"] for row in qs})
|
|
520
|
+
try:
|
|
521
|
+
return Response(get_object_tag_counts(object_id_pattern))
|
|
522
|
+
except ValueError as err:
|
|
523
|
+
raise ValidationError(err.args[0]) from err
|
|
530
524
|
|
|
531
525
|
|
|
532
526
|
@view_auth_classes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|