openedx-learning 0.1.1__py2.py3-none-any.whl → 0.1.2__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.1.1.dist-info → openedx_learning-0.1.2.dist-info}/METADATA +3 -2
- {openedx_learning-0.1.1.dist-info → openedx_learning-0.1.2.dist-info}/RECORD +10 -9
- openedx_tagging/core/tagging/api.py +43 -0
- openedx_tagging/core/tagging/migrations/0006_alter_objecttag_unique_together.py +16 -0
- openedx_tagging/core/tagging/models/base.py +47 -0
- openedx_tagging/core/tagging/rest_api/v1/views.py +3 -2
- {openedx_learning-0.1.1.dist-info → openedx_learning-0.1.2.dist-info}/LICENSE.txt +0 -0
- {openedx_learning-0.1.1.dist-info → openedx_learning-0.1.2.dist-info}/WHEEL +0 -0
- {openedx_learning-0.1.1.dist-info → openedx_learning-0.1.2.dist-info}/top_level.txt +0 -0
openedx_learning/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
1
|
+
__version__ = "0.1.2"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: openedx-learning
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: An experiment.
|
|
5
5
|
Home-page: https://github.com/openedx/openedx-learning
|
|
6
6
|
Author: David Ormsbee
|
|
@@ -18,8 +18,9 @@ Classifier: Programming Language :: Python :: 3
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.8
|
|
19
19
|
Requires-Python: >=3.8
|
|
20
20
|
Requires-Dist: djangorestframework (<4.0)
|
|
21
|
-
Requires-Dist:
|
|
21
|
+
Requires-Dist: edx-drf-extensions
|
|
22
22
|
Requires-Dist: rules (<4.0)
|
|
23
|
+
Requires-Dist: Django (<5.0)
|
|
23
24
|
|
|
24
25
|
openedx-learning
|
|
25
26
|
=============================
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
openedx_learning/__init__.py,sha256=
|
|
1
|
+
openedx_learning/__init__.py,sha256=YvuYzWnKtqBb-IqG8HAu-nhIYAsgj9Vmc_b9o7vO-js,22
|
|
2
2
|
openedx_learning/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
openedx_learning/contrib/media_server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
openedx_learning/contrib/media_server/apps.py,sha256=DWPy77j5hdv_tk8Y9MsoscgwRBxplO-sBVJT5grhHPA,754
|
|
@@ -41,7 +41,7 @@ openedx_learning/rest_api/v1/urls.py,sha256=lY-i3VzANvtdcJHjwygMtqEhfyMOjgIfaWpn
|
|
|
41
41
|
openedx_tagging/__init__.py,sha256=N_pklgjdCZO62lWysvHk0w9RnDRXCBVhok0GheQrFaE,28
|
|
42
42
|
openedx_tagging/core/tagging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
43
|
openedx_tagging/core/tagging/admin.py,sha256=efVW_mA5MEey14VJdazacyxnBgbYPPfJQVS7XLKaWEg,192
|
|
44
|
-
openedx_tagging/core/tagging/api.py,sha256=
|
|
44
|
+
openedx_tagging/core/tagging/api.py,sha256=ilRZVGWPq2AtQkF6n_jeiwOPIKEDvTbkYrneUiuEeXw,6602
|
|
45
45
|
openedx_tagging/core/tagging/apps.py,sha256=-gp0VYqX4XQzwjjd-G68Ev2Op0INLh9Byz5UOqF5_7k,345
|
|
46
46
|
openedx_tagging/core/tagging/rules.py,sha256=_a5waGxbHmAvSeFG6SefRCF4hpVdCw1hlJuyd440X_k,2688
|
|
47
47
|
openedx_tagging/core/tagging/urls.py,sha256=XOZ0CNp7w1Z4zcTZ9orX-uEfqogCRYTpLhVkNG8yfw8,159
|
|
@@ -53,18 +53,19 @@ openedx_tagging/core/tagging/migrations/0002_auto_20230718_2026.py,sha256=UG_q6U
|
|
|
53
53
|
openedx_tagging/core/tagging/migrations/0003_auto_20230721_1238.py,sha256=c8q2apdZPNcr9U4VstFaLSO5z1k0ZlzL87qObobN7Mw,2109
|
|
54
54
|
openedx_tagging/core/tagging/migrations/0004_auto_20230723_2001.py,sha256=Onnc6T12BxOQX5QTFUC9Qc5leiR7014qT2jVE7N8MBU,1080
|
|
55
55
|
openedx_tagging/core/tagging/migrations/0005_language_taxonomy.py,sha256=WaWh4JAnbPCqPo2uFRWHPZfUC1wa9zgGuLU8Qgri68U,708
|
|
56
|
+
openedx_tagging/core/tagging/migrations/0006_alter_objecttag_unique_together.py,sha256=Jx7B8ZQ5u8sZksKEkpZFrcaQpBXkaJVBtTXa-k3c4xY,376
|
|
56
57
|
openedx_tagging/core/tagging/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
57
58
|
openedx_tagging/core/tagging/models/__init__.py,sha256=2THzqS9MSZvFiKTh_tGTjzXPw0GRoP7BleQMvhEJkqw,197
|
|
58
|
-
openedx_tagging/core/tagging/models/base.py,sha256=
|
|
59
|
+
openedx_tagging/core/tagging/models/base.py,sha256=OYNMHqB2UHhgoAFv6E66KqtzSolsoguA337hQxMmZVU,25000
|
|
59
60
|
openedx_tagging/core/tagging/models/system_defined.py,sha256=o-x-ckdbiGSjLWQu0DJJsm5nETCuW4i9pPOPo-yqfxY,8422
|
|
60
61
|
openedx_tagging/core/tagging/rest_api/urls.py,sha256=HWK1uWlslaBeqcPdlgvRjcSk0_lm48Sq3cR3TPvTiKk,148
|
|
61
62
|
openedx_tagging/core/tagging/rest_api/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
62
63
|
openedx_tagging/core/tagging/rest_api/v1/permissions.py,sha256=xfiSuGzJ6b01XWzdPrbZWlJAAC8zjaEDhV6W7y_pq0Y,536
|
|
63
64
|
openedx_tagging/core/tagging/rest_api/v1/serializers.py,sha256=NX9AAVVTT9EChTIshLvitwej-TO1UJEt6VB5vgTR2y0,693
|
|
64
65
|
openedx_tagging/core/tagging/rest_api/v1/urls.py,sha256=Fc2nN4yFLZeKFOPPMYU_bXxNxd3Cw2gGXxiIJd5rV0c,292
|
|
65
|
-
openedx_tagging/core/tagging/rest_api/v1/views.py,sha256=
|
|
66
|
-
openedx_learning-0.1.
|
|
67
|
-
openedx_learning-0.1.
|
|
68
|
-
openedx_learning-0.1.
|
|
69
|
-
openedx_learning-0.1.
|
|
70
|
-
openedx_learning-0.1.
|
|
66
|
+
openedx_tagging/core/tagging/rest_api/v1/views.py,sha256=uUl_aNEGVTu1vj36HWpZjQfw154Qq-iEGNpQ-0koZVs,5881
|
|
67
|
+
openedx_learning-0.1.2.dist-info/LICENSE.txt,sha256=QTW2QN7q3XszgUAXm9Dzgtu5LXYKbR1SGnqMa7ufEuY,35139
|
|
68
|
+
openedx_learning-0.1.2.dist-info/METADATA,sha256=xgX75erxBaiHLfBiefAd-j2VPJiws5LdA9yBt8OlfKI,8640
|
|
69
|
+
openedx_learning-0.1.2.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110
|
|
70
|
+
openedx_learning-0.1.2.dist-info/top_level.txt,sha256=IYFbr5mgiEHd-LOtZmXj3q3a0bkGK1M9LY7GXgnfi4M,33
|
|
71
|
+
openedx_learning-0.1.2.dist-info/RECORD,,
|
|
@@ -137,3 +137,46 @@ def tag_object(
|
|
|
137
137
|
Preserves existing (valid) tags, adds new (valid) tags, and removes omitted (or invalid) tags.
|
|
138
138
|
"""
|
|
139
139
|
return taxonomy.cast().tag_object(tags, object_id)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def autocomplete_tags(
|
|
143
|
+
taxonomy: Taxonomy,
|
|
144
|
+
search: str,
|
|
145
|
+
object_id: str = None,
|
|
146
|
+
object_tags_only=True,
|
|
147
|
+
) -> QuerySet:
|
|
148
|
+
"""
|
|
149
|
+
Provides auto-complete suggestions by matching the `search` string against existing
|
|
150
|
+
ObjectTags linked to the given taxonomy. A case-insensitive search is used in order
|
|
151
|
+
to return the highest number of relevant tags.
|
|
152
|
+
|
|
153
|
+
If `object_id` is provided, then object tag values already linked to this object
|
|
154
|
+
are omitted from the returned suggestions. (ObjectTag values must be unique for a
|
|
155
|
+
given object + taxonomy, and so omitting these suggestions helps users avoid
|
|
156
|
+
duplication errors.).
|
|
157
|
+
|
|
158
|
+
Returns a QuerySet of dictionaries containing distinct `value` (string) and
|
|
159
|
+
`tag` (numeric ID) values, sorted alphabetically by `value`.
|
|
160
|
+
The `value` is what should be shown as a suggestion to users,
|
|
161
|
+
and if it's a free-text taxonomy, `tag` will be `None`: we include the `tag` ID
|
|
162
|
+
in anticipation of the second use case listed below.
|
|
163
|
+
|
|
164
|
+
Use cases:
|
|
165
|
+
* This method is useful for reducing tag variation in free-text taxonomies by showing
|
|
166
|
+
users tags that are similar to what they're typing. E.g., if the `search` string "dn"
|
|
167
|
+
shows that other objects have been tagged with "DNA", "DNA electrophoresis", and "DNA fingerprinting",
|
|
168
|
+
this encourages users to use those existing tags if relevant, instead of creating new ones that
|
|
169
|
+
look similar (e.g. "dna finger-printing").
|
|
170
|
+
* It could also be used to assist tagging for closed taxonomies with a list of possible tags which is too
|
|
171
|
+
large to return all at once, e.g. a user model taxonomy that dynamically creates tags on request for any
|
|
172
|
+
registered user in the database. (Note that this is not implemented yet, but may be as part of a future change.)
|
|
173
|
+
"""
|
|
174
|
+
if not object_tags_only:
|
|
175
|
+
raise NotImplementedError(
|
|
176
|
+
_(
|
|
177
|
+
"Using this would return a query set of tags instead of object tags."
|
|
178
|
+
"For now we recommend fetching all of the taxonomy's tags "
|
|
179
|
+
"using get_tags() and filtering them on the frontend."
|
|
180
|
+
)
|
|
181
|
+
)
|
|
182
|
+
return taxonomy.cast().autocomplete_tags(search, object_id)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Generated by Django 3.2.19 on 2023-08-02 16:20
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
dependencies = [
|
|
8
|
+
("oel_tagging", "0005_language_taxonomy"),
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
operations = [
|
|
12
|
+
migrations.AlterUniqueTogether(
|
|
13
|
+
name="objecttag",
|
|
14
|
+
unique_together={("taxonomy", "_value", "object_id")},
|
|
15
|
+
),
|
|
16
|
+
]
|
|
@@ -430,6 +430,52 @@ class Taxonomy(models.Model):
|
|
|
430
430
|
|
|
431
431
|
return updated_tags
|
|
432
432
|
|
|
433
|
+
def autocomplete_tags(
|
|
434
|
+
self,
|
|
435
|
+
search: str,
|
|
436
|
+
object_id: str = None,
|
|
437
|
+
) -> models.QuerySet:
|
|
438
|
+
"""
|
|
439
|
+
Provides auto-complete suggestions by matching the `search` string against existing
|
|
440
|
+
ObjectTags linked to the given taxonomy. A case-insensitive search is used in order
|
|
441
|
+
to return the highest number of relevant tags.
|
|
442
|
+
|
|
443
|
+
If `object_id` is provided, then object tag values already linked to this object
|
|
444
|
+
are omitted from the returned suggestions. (ObjectTag values must be unique for a
|
|
445
|
+
given object + taxonomy, and so omitting these suggestions helps users avoid
|
|
446
|
+
duplication errors.).
|
|
447
|
+
|
|
448
|
+
Returns a QuerySet of dictionaries containing distinct `value` (string) and `tag`
|
|
449
|
+
(numeric ID) values, sorted alphabetically by `value`.
|
|
450
|
+
|
|
451
|
+
Subclasses can override this method to perform their own autocomplete process.
|
|
452
|
+
Subclass use cases:
|
|
453
|
+
* Large taxonomy associated with a model. It can be overridden to get
|
|
454
|
+
the suggestions directly from the model by doing own filtering.
|
|
455
|
+
* Taxonomy with a list of available tags: It can be overridden to only
|
|
456
|
+
search the suggestions on a list of available tags.
|
|
457
|
+
"""
|
|
458
|
+
# Fetch tags that the object already has to exclude them from the result
|
|
459
|
+
excluded_tags = []
|
|
460
|
+
if object_id:
|
|
461
|
+
excluded_tags = self.objecttag_set.filter(object_id=object_id).values_list(
|
|
462
|
+
"_value", flat=True
|
|
463
|
+
)
|
|
464
|
+
return (
|
|
465
|
+
# Fetch object tags from this taxonomy whose value contains the search
|
|
466
|
+
self.objecttag_set.filter(_value__icontains=search)
|
|
467
|
+
# omit any tags whose values match the tags on the given object
|
|
468
|
+
.exclude(_value__in=excluded_tags)
|
|
469
|
+
# alphabetical ordering
|
|
470
|
+
.order_by("_value")
|
|
471
|
+
# Alias the `_value` field to `value` to make it nicer for users
|
|
472
|
+
.annotate(value=models.F("_value"))
|
|
473
|
+
# obtain tag values
|
|
474
|
+
.values("value", "tag_id")
|
|
475
|
+
# remove repeats
|
|
476
|
+
.distinct()
|
|
477
|
+
)
|
|
478
|
+
|
|
433
479
|
|
|
434
480
|
class ObjectTag(models.Model):
|
|
435
481
|
"""
|
|
@@ -497,6 +543,7 @@ class ObjectTag(models.Model):
|
|
|
497
543
|
models.Index(fields=["taxonomy", "object_id"]),
|
|
498
544
|
models.Index(fields=["taxonomy", "_value"]),
|
|
499
545
|
]
|
|
546
|
+
unique_together = ("taxonomy", "_value", "object_id")
|
|
500
547
|
|
|
501
548
|
def __repr__(self):
|
|
502
549
|
"""
|
|
@@ -9,8 +9,8 @@ from ...api import (
|
|
|
9
9
|
get_taxonomy,
|
|
10
10
|
get_taxonomies,
|
|
11
11
|
)
|
|
12
|
-
from .serializers import TaxonomyListQueryParamsSerializer, TaxonomySerializer
|
|
13
12
|
from .permissions import TaxonomyObjectPermissions
|
|
13
|
+
from .serializers import TaxonomyListQueryParamsSerializer, TaxonomySerializer
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class TaxonomyView(ModelViewSet):
|
|
@@ -19,6 +19,8 @@ class TaxonomyView(ModelViewSet):
|
|
|
19
19
|
|
|
20
20
|
**List Query Parameters**
|
|
21
21
|
* enabled (optional) - Filter by enabled status. Valid values: true, false, 1, 0, "true", "false", "1"
|
|
22
|
+
* page (optional) - Page number (default: 1)
|
|
23
|
+
* page_size (optional) - Number of items per page (default: 10)
|
|
22
24
|
|
|
23
25
|
**List Example Requests**
|
|
24
26
|
GET api/tagging/v1/taxonomy - Get all taxonomies
|
|
@@ -59,7 +61,6 @@ class TaxonomyView(ModelViewSet):
|
|
|
59
61
|
"allow_free_text": True,
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
|
|
63
64
|
**Create Query Returns**
|
|
64
65
|
* 201 - Success
|
|
65
66
|
* 403 - Permission denied
|
|
File without changes
|
|
File without changes
|
|
File without changes
|