Skip to content

Commit

Permalink
Merge pull request #245 from eduNEXT/cag/add-event-tracking
Browse files Browse the repository at this point in the history
feat: add tracking log for completion events
  • Loading branch information
UsamaSadiq authored Sep 21, 2023
2 parents bd8a126 + 82551e6 commit 9c84fe9
Show file tree
Hide file tree
Showing 9 changed files with 267 additions and 9 deletions.
26 changes: 26 additions & 0 deletions completion/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@

from model_utils.models import TimeStampedModel

from eventtracking import tracker

from . import waffle

log = logging.getLogger(__name__)
User = auth.get_user_model()


BLOCK_COMPLETION_CHANGED_EVENT_TYPE = 'edx.completion.block_completion.changed'


def validate_percent(value):
"""
Verify that the passed value is between 0.0 and 1.0.
Expand Down Expand Up @@ -115,17 +120,21 @@ def submit_completion(self, user, block_key, completion):
block_key=block_key,
)
is_new = False

if not is_new and obj.completion != completion:
obj.completion = completion
obj.full_clean()
obj.save(update_fields={'completion', 'modified'})

obj.emit_tracking_log()
else:
# If the feature is not enabled, this method should not be called.
# Error out with a RuntimeError.
raise RuntimeError(
"BlockCompletion.objects.submit_completion should not be \
called when the feature is disabled."
)

return obj, is_new

@transaction.atomic()
Expand Down Expand Up @@ -321,3 +330,20 @@ class Meta:

def __unicode__(self):
return f'BlockCompletion: {self.user.username}, {self.context_key}, {self.block_key}: {self.completion}'

def emit_tracking_log(self):
"""
Emit a tracking log when a block completion is created or updated.
"""
tracker.emit(
BLOCK_COMPLETION_CHANGED_EVENT_TYPE,
{
'user_id': self.user.id,
'course_id': str(self.context_key),
'block_id': str(self.block_key),
'block_type': self.block_type,
'completion': self.completion,
'modified': self.modified,
'created': self.created,
}
)
51 changes: 51 additions & 0 deletions completion/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
from datetime import datetime

from django.contrib import auth
from django.test.utils import override_settings
from edx_toggles.toggles.testutils import override_waffle_switch
from eventtracking import tracker
from django.test import TestCase
from eventtracking.django import DjangoTracker
import factory
from factory.django import DjangoModelFactory
from opaque_keys.edx.keys import UsageKey
Expand Down Expand Up @@ -120,3 +124,50 @@ def override_completion_switch(self, enabled):
"""
with override_waffle_switch(waffle.ENABLE_COMPLETION_TRACKING_SWITCH, enabled):
yield


IN_MEMORY_BACKEND_CONFIG = {
'mem': {
'ENGINE': 'completion.test_utils.InMemoryBackend'
}
}


class InMemoryBackend:
"""A backend that simply stores all events in memory"""

def __init__(self):
super().__init__() # lint-amnesty, pylint: disable=super-with-arguments
self.events = []

def send(self, event):
"""Store the event in a list"""
self.events.append(event)


@override_settings(
EVENT_TRACKING_BACKENDS=IN_MEMORY_BACKEND_CONFIG
)
class EventTrackingTestCase(TestCase):
"""
Supports capturing of emitted events in memory and inspecting them.
Each test gets a "clean slate" and can retrieve any events emitted during their execution.
"""

# Make this more robust to the addition of new events that the test doesn't care about.

def setUp(self):
super().setUp() # lint-amnesty, pylint: disable=super-with-arguments

self.recreate_tracker()

def recreate_tracker(self):
"""
Re-initialize the tracking system using updated django settings.
Use this if you make use of the @override_settings decorator to customize the tracker configuration.
"""
self.tracker = DjangoTracker()
tracker.register_tracker(self.tracker)
8 changes: 4 additions & 4 deletions completion/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from opaque_keys.edx.keys import CourseKey, UsageKey

from .. import models
from ..test_utils import CompletionSetUpMixin, UserFactory, submit_completions_for_testing
from ..test_utils import CompletionSetUpMixin, EventTrackingTestCase, UserFactory, submit_completions_for_testing


class PercentValidatorTestCase(TestCase):
Expand All @@ -29,7 +29,7 @@ def test_invalid_percent(self):
self.assertRaises(ValidationError, models.validate_percent, value)


class SubmitCompletionTestCase(CompletionSetUpMixin, TestCase):
class SubmitCompletionTestCase(CompletionSetUpMixin, EventTrackingTestCase, TestCase):
"""
Test that BlockCompletion.objects.submit_completion has the desired
semantics.
Expand All @@ -41,7 +41,7 @@ def setUp(self):
self.set_up_completion()

def test_changed_value(self):
with self.assertNumQueries(6): # Get, update, 2 * savepoints, 2 * exists checks
with self.assertNumQueries(7): # 2 * Get, update, 2 * savepoints, 2 * exists checks
completion, isnew = models.BlockCompletion.objects.submit_completion(
user=self.user,
block_key=self.block_key,
Expand All @@ -53,7 +53,7 @@ def test_changed_value(self):
self.assertEqual(models.BlockCompletion.objects.count(), 1)

def test_unchanged_value(self):
with self.assertNumQueries(3): # Get + 2 * savepoints
with self.assertNumQueries(4): # 2 * Get + 2 * savepoints
completion, isnew = models.BlockCompletion.objects.submit_completion(
user=self.user,
block_key=self.block_key,
Expand Down
1 change: 1 addition & 0 deletions requirements/base.in
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ edx-drf-extensions>=1.11.0
edx-toggles>=1.2.0
pytz
XBlock>=1.2.2
event-tracking
47 changes: 46 additions & 1 deletion requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#
# make upgrade
#
amqp==5.1.1
# via kombu
appdirs==1.4.4
# via fs
asgiref==3.7.2
Expand All @@ -12,8 +14,16 @@ astroid==2.15.6
# via
# pylint
# pylint-celery
backports-zoneinfo[tzdata]==0.2.1
# via
# celery
# kombu
billiard==4.1.0
# via celery
build==1.0.3
# via pip-tools
celery==5.3.4
# via event-tracking
certifi==2023.7.22
# via requests
cffi==1.15.1
Expand All @@ -26,13 +36,23 @@ charset-normalizer==3.2.0
# via requests
click==8.1.7
# via
# celery
# click-didyoumean
# click-log
# click-plugins
# click-repl
# code-annotations
# edx-django-utils
# edx-lint
# pip-tools
click-didyoumean==0.3.0
# via celery
click-log==0.4.0
# via edx-lint
click-plugins==1.1.1
# via celery
click-repl==0.3.0
# via celery
code-annotations==1.5.0
# via
# edx-lint
Expand Down Expand Up @@ -64,6 +84,7 @@ django==3.2.21
# edx-drf-extensions
# edx-i18n-tools
# edx-toggles
# event-tracking
django-crum==0.7.9
# via
# edx-django-utils
Expand All @@ -88,6 +109,7 @@ edx-django-utils==5.7.0
# via
# edx-drf-extensions
# edx-toggles
# event-tracking
edx-drf-extensions==8.9.2
# via -r requirements/base.in
edx-i18n-tools==1.1.0
Expand All @@ -102,6 +124,8 @@ edx-opaque-keys[django]==2.5.0
# edx-drf-extensions
edx-toggles==5.1.0
# via -r requirements/base.in
event-tracking==2.2.0
# via -r requirements/base.in
exceptiongroup==1.1.3
# via pytest
factory-boy==3.3.0
Expand Down Expand Up @@ -143,6 +167,8 @@ jinja2==3.1.2
# diff-cover
keyring==24.2.0
# via twine
kombu==5.3.2
# via celery
lazy-object-proxy==1.9.0
# via astroid
lxml==4.9.3
Expand Down Expand Up @@ -189,6 +215,8 @@ pluggy==1.3.0
# tox
polib==1.2.0
# via edx-i18n-tools
prompt-toolkit==3.0.39
# via click-repl
psutil==5.9.5
# via edx-django-utils
py==1.11.0
Expand Down Expand Up @@ -223,7 +251,9 @@ pylint-plugin-utils==0.8.2
# pylint-celery
# pylint-django
pymongo==3.13.0
# via edx-opaque-keys
# via
# edx-opaque-keys
# event-tracking
pynacl==1.5.0
# via edx-django-utils
pyproject-hooks==1.0.0
Expand All @@ -238,6 +268,7 @@ pytest-django==4.5.2
# via -r requirements/test.in
python-dateutil==2.8.2
# via
# celery
# edx-drf-extensions
# faker
# freezegun
Expand All @@ -249,6 +280,7 @@ pytz==2023.3.post1
# -r requirements/base.in
# django
# djangorestframework
# event-tracking
# xblock
pyyaml==6.0.1
# via
Expand Down Expand Up @@ -276,6 +308,7 @@ six==1.16.0
# via
# edx-drf-extensions
# edx-lint
# event-tracking
# fs
# python-dateutil
# tox
Expand Down Expand Up @@ -317,14 +350,26 @@ typing-extensions==4.7.1
# edx-opaque-keys
# faker
# filelock
# kombu
# pylint
# rich
tzdata==2023.3
# via
# backports-zoneinfo
# celery
urllib3==2.0.4
# via
# requests
# twine
vine==5.0.0
# via
# amqp
# celery
# kombu
virtualenv==20.24.5
# via tox
wcwidth==0.2.6
# via prompt-toolkit
web-fragments==2.1.0
# via xblock
webob==1.8.7
Expand Down
Loading

0 comments on commit 9c84fe9

Please sign in to comment.