Implement Entitlement Model for Course Centric Architecture
[LEARNER-1733]
This commit is contained in:
0
common/djangoapps/entitlements/__init__.py
Normal file
0
common/djangoapps/entitlements/__init__.py
Normal file
15
common/djangoapps/entitlements/admin.py
Normal file
15
common/djangoapps/entitlements/admin.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from django.contrib import admin
|
||||
from .models import CourseEntitlement
|
||||
|
||||
|
||||
@admin.register(CourseEntitlement)
|
||||
class EntitlementAdmin(admin.ModelAdmin):
|
||||
list_display = ('user',
|
||||
'uuid',
|
||||
'course_uuid',
|
||||
'created',
|
||||
'modified',
|
||||
'expired_at',
|
||||
'mode',
|
||||
'enrollment_course_run',
|
||||
'order_number')
|
||||
37
common/djangoapps/entitlements/migrations/0001_initial.py
Normal file
37
common/djangoapps/entitlements/migrations/0001_initial.py
Normal file
@@ -0,0 +1,37 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
from django.conf import settings
|
||||
import model_utils.fields
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('student', '0013_delete_historical_enrollment_records'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CourseEntitlement',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)),
|
||||
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)),
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False)),
|
||||
('course_uuid', models.UUIDField()),
|
||||
('expired_at', models.DateTimeField(null=True)),
|
||||
('mode', models.CharField(default=b'audit', max_length=100)),
|
||||
('order_number', models.CharField(max_length=128, null=True)),
|
||||
('enrollment_course_run', models.ForeignKey(to='student.CourseEnrollment', null=True)),
|
||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
97
common/djangoapps/entitlements/models.py
Normal file
97
common/djangoapps/entitlements/models.py
Normal file
@@ -0,0 +1,97 @@
|
||||
import uuid as uuid_tools
|
||||
|
||||
from django.db import models
|
||||
from model_utils.models import TimeStampedModel
|
||||
from django.contrib.auth.models import User
|
||||
from course_modes.models import CourseMode
|
||||
|
||||
|
||||
class CourseEntitlement(TimeStampedModel):
|
||||
"""
|
||||
Represents a Student's Entitlement to a Course Run for a given Course.
|
||||
"""
|
||||
|
||||
user = models.ForeignKey(User)
|
||||
uuid = models.UUIDField(default=uuid_tools.uuid4, editable=False)
|
||||
course_uuid = models.UUIDField()
|
||||
|
||||
# The date that an the entitlement expired
|
||||
# if NULL the entitlement has not expired
|
||||
expired_at = models.DateTimeField(null=True)
|
||||
|
||||
# The mode of the Course that will be applied
|
||||
mode = models.CharField(default=CourseMode.DEFAULT_MODE_SLUG, max_length=100)
|
||||
|
||||
# The ID of the course enrollment for this Entitlement
|
||||
# if NULL the entitlement is not in use
|
||||
enrollment_course_run = models.ForeignKey('student.CourseEnrollment', null=True)
|
||||
order_number = models.CharField(max_length=128, null=True)
|
||||
|
||||
@classmethod
|
||||
def entitlements_for_user(cls, user):
|
||||
"""
|
||||
Retrieve all the Entitlements for a User
|
||||
|
||||
Arguments:
|
||||
user: A Django User object identifying the current user
|
||||
|
||||
Returns:
|
||||
All of the Entitlements for the User
|
||||
"""
|
||||
return cls.objects.filter(user=user)
|
||||
|
||||
@classmethod
|
||||
def get_user_course_entitlement(cls, user, course_uuid):
|
||||
"""
|
||||
Retrieve The entitlement for the given parent course id if it exists for the User
|
||||
|
||||
Arguments:
|
||||
user: A Django User object identifying the current user
|
||||
course_uuid(string): The parent course uuid
|
||||
|
||||
Returns:
|
||||
The single entitlement for the requested parent course id
|
||||
"""
|
||||
return cls.objects.filter(user=user, course_uuid=course_uuid).first()
|
||||
|
||||
@classmethod
|
||||
def update_or_create_new_entitlement(cls, user, course_uuid, entitlement_data):
|
||||
"""
|
||||
Updates or creates a new Course Entitlement
|
||||
|
||||
Arguments:
|
||||
user: A Django User object identifying the current user
|
||||
course_uuid(string): The parent course uuid
|
||||
entitlement_data(dict): The dictionary containing all the data for the entitlement
|
||||
e.g. entitlement_data = {
|
||||
'user': user,
|
||||
'course_uuid': course_uuid
|
||||
'enroll_end_date': '2017-09-14 11:47:58.000000',
|
||||
'mode': 'verified',
|
||||
}
|
||||
|
||||
Returns:
|
||||
stored_entitlement: The new or updated CourseEntitlement object
|
||||
is_created (bool): Boolean representing whether or not the Entitlement was created or updated
|
||||
"""
|
||||
stored_entitlement, is_created = cls.objects.update_or_create(
|
||||
user=user,
|
||||
course_uuid=course_uuid,
|
||||
defaults=entitlement_data
|
||||
)
|
||||
return stored_entitlement, is_created
|
||||
|
||||
@classmethod
|
||||
def update_entitlement_enrollment(cls, user, course_uuid, course_run_enrollment):
|
||||
"""
|
||||
Sets the enrollment course for a given entitlement
|
||||
|
||||
Arguments:
|
||||
user: A Django User object identifying the current user
|
||||
course_uuid(string): The parent course uuid
|
||||
course_run_enrollment (CourseEnrollment): The CourseEnrollment object to store, None to clear the Enrollment
|
||||
"""
|
||||
return cls.objects.filter(
|
||||
user=user,
|
||||
course_uuid=course_uuid
|
||||
).update(enrollment_course_run_id=course_run_enrollment)
|
||||
107
common/djangoapps/entitlements/tests/test_data.py
Normal file
107
common/djangoapps/entitlements/tests/test_data.py
Normal file
@@ -0,0 +1,107 @@
|
||||
"""
|
||||
Test the Data Aggregation Layer for Course Entitlements.
|
||||
"""
|
||||
import unittest
|
||||
import uuid
|
||||
|
||||
import ddt
|
||||
from django.conf import settings
|
||||
|
||||
from entitlements.models import CourseEntitlement
|
||||
from student.tests.factories import UserFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from student.tests.factories import CourseEnrollmentFactory
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
class EntitlementDataTest(ModuleStoreTestCase):
|
||||
"""
|
||||
Test course entitlement data aggregation.
|
||||
"""
|
||||
USERNAME = "Bob"
|
||||
EMAIL = "bob@example.com"
|
||||
PASSWORD = "edx"
|
||||
|
||||
def setUp(self):
|
||||
"""Create a course and user, then log in. """
|
||||
super(EntitlementDataTest, self).setUp()
|
||||
self.course = CourseFactory.create()
|
||||
self.course_uuid = uuid.uuid4()
|
||||
self.user = UserFactory.create(username=self.USERNAME, email=self.EMAIL, password=self.PASSWORD)
|
||||
self.client.login(username=self.USERNAME, password=self.PASSWORD)
|
||||
|
||||
def _add_entitlement_for_user(self, course, user, parent_uuid):
|
||||
entitlement_data = {
|
||||
'user': user,
|
||||
'course_uuid': parent_uuid,
|
||||
'mode': 'verified',
|
||||
}
|
||||
stored_entitlement, is_created = CourseEntitlement.update_or_create_new_entitlement(
|
||||
user,
|
||||
parent_uuid,
|
||||
entitlement_data
|
||||
)
|
||||
return stored_entitlement, is_created
|
||||
|
||||
def test_get_entitlement_info(self):
|
||||
stored_entitlement, is_created = self._add_entitlement_for_user(self.course, self.user, self.course_uuid)
|
||||
self.assertTrue(is_created)
|
||||
|
||||
# Get the Entitlement and verify the data
|
||||
entitlement = CourseEntitlement.get_user_course_entitlement(self.user, self.course_uuid)
|
||||
self.assertEqual(entitlement.course_uuid, self.course_uuid)
|
||||
self.assertEqual(entitlement.mode, 'verified')
|
||||
self.assertIsNone(entitlement.enrollment_course_run)
|
||||
|
||||
def test_get_course_entitlements(self):
|
||||
course2 = CourseFactory.create()
|
||||
|
||||
stored_entitlement, is_created = self._add_entitlement_for_user(self.course, self.user, self.course_uuid)
|
||||
self.assertTrue(is_created)
|
||||
|
||||
course2_uuid = uuid.uuid4()
|
||||
stored_entitlement2, is_created2 = self._add_entitlement_for_user(course2, self.user, course2_uuid)
|
||||
self.assertTrue(is_created2)
|
||||
|
||||
# Get the Entitlement and verify the data
|
||||
entitlement_list = CourseEntitlement.entitlements_for_user(self.user)
|
||||
|
||||
self.assertEqual(2, len(entitlement_list))
|
||||
self.assertEqual(self.course_uuid, entitlement_list[0].course_uuid)
|
||||
self.assertEqual(course2_uuid, entitlement_list[1].course_uuid)
|
||||
|
||||
def test_set_enrollment(self):
|
||||
stored_entitlement, is_created = self._add_entitlement_for_user(self.course, self.user, self.course_uuid)
|
||||
self.assertTrue(is_created)
|
||||
|
||||
# Entitlement set not enroll the user in the Course run
|
||||
enrollment = CourseEnrollmentFactory(
|
||||
user=self.user,
|
||||
course_id=self.course.id,
|
||||
mode="verified",
|
||||
)
|
||||
CourseEntitlement.update_entitlement_enrollment(self.user, self.course_uuid, enrollment)
|
||||
|
||||
entitlement = CourseEntitlement.get_user_course_entitlement(self.user, self.course_uuid)
|
||||
self.assertIsNotNone(entitlement.enrollment_course_run)
|
||||
|
||||
def test_remove_enrollment(self):
|
||||
stored_entitlement, is_created = self._add_entitlement_for_user(self.course, self.user, self.course_uuid)
|
||||
self.assertTrue(is_created)
|
||||
|
||||
# Entitlement set not enroll the user in the Course run
|
||||
enrollment = CourseEnrollmentFactory(
|
||||
user=self.user,
|
||||
course_id=self.course.id,
|
||||
mode="verified",
|
||||
)
|
||||
CourseEntitlement.update_entitlement_enrollment(self.user, self.course_uuid, enrollment)
|
||||
|
||||
entitlement = CourseEntitlement.get_user_course_entitlement(self.user, self.course_uuid)
|
||||
self.assertIsNotNone(entitlement.enrollment_course_run)
|
||||
|
||||
CourseEntitlement.update_entitlement_enrollment(self.user, self.course_uuid, None)
|
||||
entitlement = CourseEntitlement.get_user_course_entitlement(self.user, self.course_uuid)
|
||||
self.assertIsNone(entitlement.enrollment_course_run)
|
||||
@@ -2128,6 +2128,9 @@ INSTALLED_APPS = [
|
||||
# Enrollment API
|
||||
'enrollment',
|
||||
|
||||
# Entitlement API
|
||||
'entitlements',
|
||||
|
||||
# Bulk Enrollment API
|
||||
'bulk_enroll',
|
||||
|
||||
|
||||
Reference in New Issue
Block a user