Skip to content

Commit d355a4f

Browse files
committed
feat: add tag and taxonomy dump
fix: add lineage information for tag sink chore: quality fixes fix: serialize lineage data as json
1 parent 9ed870b commit d355a4f

File tree

6 files changed

+205
-0
lines changed

6 files changed

+205
-0
lines changed

platform_plugin_aspects/settings/common.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,13 @@ def plugin_settings(settings):
9494
"module": "openedx.core.djangoapps.user_api.models",
9595
"model": "UserPreference",
9696
},
97+
"tag": {"module": "openedx_tagging.core.tagging.models", "model": "Tag"},
98+
"taxonomy": {
99+
"module": "openedx_tagging.core.tagging.models",
100+
"model": "Taxonomy",
101+
},
102+
"object_tag": {
103+
"module": "openedx_tagging.core.tagging.models",
104+
"model": "ObjectTag",
105+
},
97106
}

platform_plugin_aspects/signals.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
from platform_plugin_aspects.sinks import (
1010
CourseEnrollmentSink,
1111
ExternalIdSink,
12+
ObjectTagSink,
13+
TagSink,
14+
TaxonomySink,
1215
UserProfileSink,
1316
UserRetirementSink,
1417
)
@@ -143,3 +146,81 @@ def on_user_retirement( # pylint: disable=unused-argument # pragma: no cover
143146
sink_name=sink.__class__.__name__,
144147
object_id=str(user.id),
145148
)
149+
150+
151+
def on_tag_saved( # pylint: disable=unused-argument # pragma: no cover
152+
sender, instance, **kwargs
153+
):
154+
"""
155+
Receives post save signal and queues the dump job.
156+
"""
157+
# import here, because signal is registered at startup, but items in tasks are not yet able to be loaded
158+
from platform_plugin_aspects.tasks import ( # pylint: disable=import-outside-toplevel
159+
dump_data_to_clickhouse,
160+
)
161+
162+
sink = TagSink(None, None)
163+
dump_data_to_clickhouse.delay(
164+
sink_module=sink.__module__,
165+
sink_name=sink.__class__.__name__,
166+
object_id=str(instance.id),
167+
)
168+
169+
170+
# Connect the ExternalId.post_save signal handler only if we have a model to attach to.
171+
# (prevents celery errors during tests)
172+
_tag = get_model("tag")
173+
if _tag:
174+
post_save.connect(on_tag_saved, sender=_tag) # pragma: no cover
175+
176+
177+
def on_taxonomy_saved( # pylint: disable=unused-argument # pragma: no cover
178+
sender, instance, **kwargs
179+
):
180+
"""
181+
Receives post save signal and queues the dump job.
182+
"""
183+
# import here, because signal is registered at startup, but items in tasks are not yet able to be loaded
184+
from platform_plugin_aspects.tasks import ( # pylint: disable=import-outside-toplevel
185+
dump_data_to_clickhouse,
186+
)
187+
188+
sink = TaxonomySink(None, None)
189+
dump_data_to_clickhouse.delay(
190+
sink_module=sink.__module__,
191+
sink_name=sink.__class__.__name__,
192+
object_id=str(instance.id),
193+
)
194+
195+
196+
# Connect the ExternalId.post_save signal handler only if we have a model to attach to.
197+
# (prevents celery errors during tests)
198+
_taxonomy = get_model("taxonomy")
199+
if _taxonomy:
200+
post_save.connect(on_taxonomy_saved, sender=_taxonomy) # pragma: no cover
201+
202+
203+
def on_object_tag_saved( # pylint: disable=unused-argument # pragma: no cover
204+
sender, instance, **kwargs
205+
):
206+
"""
207+
Receives post save signal and queues the dump job.
208+
"""
209+
# import here, because signal is registered at startup, but items in tasks are not yet able to be loaded
210+
from platform_plugin_aspects.tasks import ( # pylint: disable=import-outside-toplevel
211+
dump_data_to_clickhouse,
212+
)
213+
214+
sink = ObjectTagSink(None, None)
215+
dump_data_to_clickhouse.delay(
216+
sink_module=sink.__module__,
217+
sink_name=sink.__class__.__name__,
218+
object_id=str(instance.id),
219+
)
220+
221+
222+
# Connect the ExternalId.post_save signal handler only if we have a model to attach to.
223+
# (prevents celery errors during tests)
224+
_object_tag = get_model("object_tag")
225+
if _object_tag:
226+
post_save.connect(on_object_tag_saved, sender=_object_tag) # pragma: no cover

platform_plugin_aspects/sinks/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
from .course_enrollment_sink import CourseEnrollmentSink
77
from .course_overview_sink import CourseOverviewSink, XBlockSink
88
from .external_id_sink import ExternalIdSink
9+
from .tag_sink import ObjectTagSink, TagSink, TaxonomySink
910
from .user_profile_sink import UserProfileSink
1011
from .user_retire_sink import UserRetirementSink

platform_plugin_aspects/sinks/serializers.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,67 @@ class Meta:
206206
def get_course_key(self, obj):
207207
"""Return the course key as a string."""
208208
return str(obj.course_id)
209+
210+
211+
class TagSerializer(BaseSinkSerializer, serializers.ModelSerializer):
212+
"""Serializer for the Tag model."""
213+
214+
lineage = serializers.SerializerMethodField()
215+
216+
class Meta:
217+
"""Meta class for the TagSerializer."""
218+
219+
model = get_model("tag")
220+
fields = [
221+
"id",
222+
"taxonomy",
223+
"parent",
224+
"value",
225+
"external_id",
226+
"lineage",
227+
"dump_id",
228+
"time_last_dumped",
229+
]
230+
231+
def get_lineage(self, instance):
232+
return json.dumps(instance.get_lineage())
233+
234+
235+
class ObjectTagSerializer(BaseSinkSerializer, serializers.ModelSerializer):
236+
"""Serializer for the ObjectTag model."""
237+
238+
lineage = serializers.SerializerMethodField()
239+
240+
class Meta:
241+
"""Meta class for the ObjectTagSerializer"""
242+
243+
model = get_model("object_tag")
244+
fields = [
245+
"id",
246+
"object_id",
247+
"taxonomy",
248+
"tag",
249+
"_value",
250+
"_export_id",
251+
"lineage",
252+
"dump_id",
253+
"time_last_dumped",
254+
]
255+
256+
def get_lineage(self, instance):
257+
return json.dumps(instance.get_lineage())
258+
259+
260+
class TaxonomySerializer(BaseSinkSerializer, serializers.ModelSerializer):
261+
"""Serializer for the Taxonomy model."""
262+
263+
class Meta:
264+
"""Meta class for the TaxonomySerializer."""
265+
266+
model = get_model("taxonomy")
267+
fields = [
268+
"id",
269+
"name",
270+
"dump_id",
271+
"time_last_dumped",
272+
]
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""Tag sink"""
2+
3+
from platform_plugin_aspects.sinks.base_sink import ModelBaseSink
4+
from platform_plugin_aspects.sinks.serializers import (
5+
ObjectTagSerializer,
6+
TagSerializer,
7+
TaxonomySerializer,
8+
)
9+
10+
11+
class TagSink(ModelBaseSink): # pylint: disable=abstract-method
12+
"""
13+
Sink for content tags
14+
"""
15+
16+
model = "tag"
17+
unique_key = "id"
18+
clickhouse_table_name = "tag"
19+
timestamp_field = "time_last_dumped"
20+
name = "Tag"
21+
serializer_class = TagSerializer
22+
23+
24+
class TaxonomySink(ModelBaseSink): # pylint: disable=abstract-method
25+
"""
26+
Sink for content taxonomy
27+
"""
28+
29+
model = "taxonomy"
30+
unique_key = "id"
31+
clickhouse_table_name = "taxonomy"
32+
timestamp_field = "time_last_dumped"
33+
name = "Taxonomy"
34+
serializer_class = TaxonomySerializer
35+
36+
37+
class ObjectTagSink(ModelBaseSink): # pylint: disable=abstract-method
38+
"""
39+
Sink for tagged objects
40+
"""
41+
42+
model = "object_tag"
43+
unique_key = "id"
44+
clickhouse_table_name = "object_tag"
45+
timestamp_field = "time_last_dumped"
46+
name = "ObjectTag"
47+
serializer_class = ObjectTagSerializer

platform_plugin_aspects/tasks.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,6 @@ def dump_data_to_clickhouse(
5959
if Sink.is_enabled():
6060
sink = Sink(connection_overrides=connection_overrides, log=celery_log)
6161
sink.dump(object_id)
62+
return "Dumped"
63+
64+
return "Disabled"

0 commit comments

Comments
 (0)