Skip to content

Commit

Permalink
feat: add function to clear course progress for a learner (#284)
Browse files Browse the repository at this point in the history
* feat: add function to clear course progress for a learner

* chore: version

* fix: incorrect comments
  • Loading branch information
jansenk authored Mar 21, 2024
1 parent 633f214 commit 6191018
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 1 deletion.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ Change Log
Unreleased
~~~~~~~~~~

[4.5.0] - 2024-3-19
~~~~~~~~~~~~~~~~~~~~
* Added ``clear_learning_context_completion`` to enable clearing a learner's
completion for a course

[4.4.1] - 2023-10-27
~~~~~~~~~~~~~~~~~~~~
* Fix RemovedInDjango41Warning by removing `django_app_config`
Expand Down
2 changes: 1 addition & 1 deletion completion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
"""


__version__ = '4.4.1'
__version__ = '4.5.0'
16 changes: 16 additions & 0 deletions completion/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,22 @@ def submit_batch_completion(self, user, blocks):
block_completions[block_completion] = is_new
return block_completions

@transaction.atomic()
def clear_learning_context_completion(self, user, context_key):
"""
Performs a batch delete of all completion objects for a specified user and context.
Parameters:
* user (django.contrib.auth.models.User): The user for whom the
completions are being deleted.
* context_key: (ContextKey) The course / context identifier for which
completions are being deleted.
Return Value: (int) The number of models deleted
"""
total, _ = BlockCompletion.user_learning_context_completion_queryset(user, context_key).delete()
return total


# pylint: disable=model-has-unicode
class BlockCompletion(TimeStampedModel, models.Model):
Expand Down
91 changes: 91 additions & 0 deletions completion/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
"""

import datetime
from random import randint
from uuid import uuid4
from pytz import UTC

from django.core.exceptions import ValidationError
Expand Down Expand Up @@ -215,3 +217,92 @@ def test_latest_blocks_completed_all_courses(self):
self.course_key_one: (datetime.datetime(2050, 1, 3, tzinfo=UTC), self.block_keys_one[2])
}
)


class CompletionClearingTestCase(CompletionSetUpMixin, TestCase):
"""
Tests for clear_learning_context_completion
"""
COMPLETION_SWITCH_ENABLED = True
BLOCKS_PER_CONTEXT = 3

def setUp(self):
super().setUp()
# Create two learning contexts with some blocks
self.context_key, self.blocks = self._set_up_course('SomeCourse')
self.other_context_key, self.other_blocks = self._set_up_course('SomeOtherCourse')

# Create two users
self.user = UserFactory()
self.other_user = UserFactory()

# Create completions for all blocks in both contexts for each learner
self._create_test_completions(self.user)
self._create_test_completions(self.other_user)

def _create_test_completions(self, user):
# Create random completions for `user` for all blocks in both test contexts
models.BlockCompletion.objects.submit_batch_completion(
user,
[
(block, float(f"0.{randint(1,9)}"))
for block in self.blocks + self.other_blocks
]
)

def _set_up_course(self, course):
""" Create a context with some blocks """
blocks = [
UsageKey.from_string(f'block-v1:edx+{course}+run+type@problem+block@{uuid4()}')
for _ in range(self.BLOCKS_PER_CONTEXT)
]
return blocks[0].context_key, blocks

def _assert_completions(self, user, context, expect_completions):
""" Helper to assert the existance of completions for a given learner and context """
completions = models.BlockCompletion.get_learning_context_completions(user, context)
if expect_completions:
assert len(completions) == self.BLOCKS_PER_CONTEXT
else:
assert not completions

def test_clear_learning_context_completion(self):
"""
When we clear learning context completion, it should clear all completion records for
the given user and the given context without affecting any other user or context
"""
self._assert_completions(self.user, self.context_key, True)
self._assert_completions(self.user, self.other_context_key, True)
self._assert_completions(self.other_user, self.context_key, True)
self._assert_completions(self.other_user, self.other_context_key, True)

deleted = models.BlockCompletion.objects.clear_learning_context_completion(
self.user, self.context_key
)
assert deleted == self.BLOCKS_PER_CONTEXT

self._assert_completions(self.user, self.context_key, False)
self._assert_completions(self.user, self.other_context_key, True)
self._assert_completions(self.other_user, self.context_key, True)
self._assert_completions(self.other_user, self.other_context_key, True)

deleted = models.BlockCompletion.objects.clear_learning_context_completion(
self.other_user, self.other_context_key
)
assert deleted == self.BLOCKS_PER_CONTEXT

self._assert_completions(self.user, self.context_key, False)
self._assert_completions(self.user, self.other_context_key, True)
self._assert_completions(self.other_user, self.context_key, True)
self._assert_completions(self.other_user, self.other_context_key, False)

def test_user_no_completions(self):
"""
Calling the method for a user with no completions does nothing and raises no error
"""
stranger = UserFactory()
assert not models.BlockCompletion.objects.filter(user=stranger).exists()
deleted = models.BlockCompletion.objects.clear_learning_context_completion(
stranger, self.context_key
)
assert deleted == 0

0 comments on commit 6191018

Please sign in to comment.