add pending role assignment model (#23506)
This commit is contained in:
@@ -5,7 +5,11 @@ from django.contrib import admin
|
||||
from django.urls import reverse
|
||||
from django.utils.html import format_html
|
||||
|
||||
from lms.djangoapps.program_enrollments.models import ProgramCourseEnrollment, ProgramEnrollment
|
||||
from lms.djangoapps.program_enrollments.models import (
|
||||
CourseAccessRoleAssignment,
|
||||
ProgramCourseEnrollment,
|
||||
ProgramEnrollment
|
||||
)
|
||||
|
||||
|
||||
class ProgramEnrollmentAdmin(admin.ModelAdmin):
|
||||
@@ -67,7 +71,6 @@ def _pce_ce(pce):
|
||||
enrollment=enrollment, active_string=active_string
|
||||
)
|
||||
|
||||
|
||||
_pce_pe_id.short_description = "Program Enrollment"
|
||||
_pce_pe_user.short_description = "Pgm Enrollment: User"
|
||||
_pce_pe_external_user_key.short_description = "Pgm Enrollment: Ext User Key"
|
||||
@@ -102,5 +105,45 @@ class ProgramCourseEnrollmentAdmin(admin.ModelAdmin):
|
||||
raw_id_fields = ('program_enrollment', 'course_enrollment')
|
||||
|
||||
|
||||
def _pending_role_assignment_enrollment_id(pending_role_assignment):
|
||||
"""
|
||||
Generate a link to edit enrollment, with ID in link text.
|
||||
"""
|
||||
pce = pending_role_assignment.enrollment
|
||||
if not pce:
|
||||
return None
|
||||
link_url = reverse(
|
||||
"admin:program_enrollments_programcourseenrollment_change",
|
||||
args=[pce.id],
|
||||
)
|
||||
link_text = "id={pce.id:05}".format(pce=pce)
|
||||
return format_html("<a href={}>{}</a>", link_url, link_text)
|
||||
|
||||
|
||||
def _pending_role_assignment_external_user_key(pending_role_assignment):
|
||||
"""
|
||||
Generate the external user key for a pending role assignment
|
||||
"""
|
||||
pce = pending_role_assignment.enrollment
|
||||
return _pce_pe_external_user_key(pce)
|
||||
|
||||
_pending_role_assignment_enrollment_id.short_description = "Program Course Enrollment"
|
||||
_pending_role_assignment_external_user_key.short_description = "Pgm Enrollment: Ext User Key"
|
||||
|
||||
|
||||
class CourseAccessRoleAssignmentAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
Admin tool for the CourseAccessRoleAssignment model
|
||||
"""
|
||||
list_display = (
|
||||
'id',
|
||||
'role',
|
||||
_pending_role_assignment_enrollment_id,
|
||||
_pending_role_assignment_external_user_key
|
||||
)
|
||||
list_filter = ('role',)
|
||||
raw_id_fields = ('enrollment',)
|
||||
|
||||
admin.site.register(ProgramEnrollment, ProgramEnrollmentAdmin)
|
||||
admin.site.register(ProgramCourseEnrollment, ProgramCourseEnrollmentAdmin)
|
||||
admin.site.register(CourseAccessRoleAssignment, CourseAccessRoleAssignmentAdmin)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Constants used throughout the program_enrollments app and exposed to other
|
||||
in-process apps through api.py.
|
||||
"""
|
||||
from student.roles import CourseStaffRole
|
||||
|
||||
|
||||
class ProgramEnrollmentStatuses(object):
|
||||
@@ -113,3 +114,17 @@ class ProgramCourseOperationStatuses(
|
||||
__OK__ = ProgramCourseEnrollmentStatuses.__ALL__
|
||||
__ERRORS__ = (NOT_FOUND,) + _EnrollmentErrorStatuses.__ALL__
|
||||
__ALL__ = __OK__ + __ERRORS__
|
||||
|
||||
|
||||
class ProgramCourseEnrollmentRoles(object):
|
||||
"""
|
||||
Valid roles that can be assigned as part of a ProgramCourseEnrollment
|
||||
"""
|
||||
COURSE_STAFF = CourseStaffRole.ROLE
|
||||
__ALL__ = (COURSE_STAFF,)
|
||||
|
||||
# Note: Any changes to this value will trigger a migration on
|
||||
# CourseAccessRoleAssignment!
|
||||
__MODEL_CHOICES__ = (
|
||||
(role, role) for role in __ALL__
|
||||
)
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.29 on 2020-03-25 19:28
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import model_utils.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('program_enrollments', '0009_update_course_enrollment_field_to_foreign_key'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CourseAccessRoleAssignment',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||
('role', models.CharField(choices=[('staff', 'staff')], max_length=64)),
|
||||
('enrollment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='program_enrollments.ProgramCourseEnrollment')),
|
||||
],
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='CourseAccessRoleAssignment',
|
||||
unique_together=set([('role', 'enrollment')]),
|
||||
),
|
||||
]
|
||||
@@ -14,7 +14,7 @@ from simple_history.models import HistoricalRecords
|
||||
|
||||
from student.models import CourseEnrollment
|
||||
|
||||
from .constants import ProgramCourseEnrollmentStatuses, ProgramEnrollmentStatuses
|
||||
from .constants import ProgramCourseEnrollmentRoles, ProgramCourseEnrollmentStatuses, ProgramEnrollmentStatuses
|
||||
|
||||
|
||||
class ProgramEnrollment(TimeStampedModel):
|
||||
@@ -149,3 +149,28 @@ class ProgramCourseEnrollment(TimeStampedModel):
|
||||
" status={self.status!r}"
|
||||
">"
|
||||
).format(self=self)
|
||||
|
||||
|
||||
class CourseAccessRoleAssignment(TimeStampedModel):
|
||||
"""
|
||||
This model represents a role that should be assigned to the eventual user of a pending enrollment.
|
||||
|
||||
.. no_pii:
|
||||
"""
|
||||
class Meta(object):
|
||||
unique_together = ('role', 'enrollment')
|
||||
|
||||
role = models.CharField(max_length=64, choices=ProgramCourseEnrollmentRoles.__MODEL_CHOICES__)
|
||||
enrollment = models.ForeignKey(ProgramCourseEnrollment, on_delete=models.CASCADE)
|
||||
|
||||
def __str__(self):
|
||||
return '[CourseAccessRoleAssignment id={}]'.format(self.id)
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
"<CourseAccessRoleAssignment" # pylint: disable=missing-format-attribute
|
||||
" id={self.id}"
|
||||
" role={self.role!r}"
|
||||
" enrollment={self.enrollment!r}"
|
||||
">"
|
||||
).format(self=self)
|
||||
|
||||
@@ -45,3 +45,12 @@ class ProgramCourseEnrollmentFactory(DjangoModelFactory):
|
||||
)
|
||||
)
|
||||
status = 'active'
|
||||
|
||||
|
||||
class CourseAccessRoleAssignmentFactory(DjangoModelFactory):
|
||||
""" A factory for the CourseAccessRoleAssignment model. """
|
||||
class Meta(object):
|
||||
model = models.CourseAccessRoleAssignment
|
||||
|
||||
enrollment = factory.SubFactory(ProgramCourseEnrollmentFactory)
|
||||
role = 'staff'
|
||||
|
||||
@@ -15,8 +15,13 @@ from course_modes.models import CourseMode
|
||||
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
|
||||
from student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
|
||||
from ..constants import ProgramCourseEnrollmentRoles
|
||||
from ..models import ProgramEnrollment
|
||||
from .factories import ProgramCourseEnrollmentFactory, ProgramEnrollmentFactory
|
||||
from .factories import (
|
||||
CourseAccessRoleAssignmentFactory,
|
||||
ProgramCourseEnrollmentFactory,
|
||||
ProgramEnrollmentFactory
|
||||
)
|
||||
|
||||
|
||||
class ProgramEnrollmentModelTests(TestCase):
|
||||
@@ -200,3 +205,41 @@ class ProgramCourseEnrollmentModelTests(TestCase):
|
||||
course_enrollment=None,
|
||||
status="active"
|
||||
)
|
||||
|
||||
|
||||
class CourseAccessRoleAssignmentTests(TestCase):
|
||||
"""
|
||||
Tests for the CourseAccessRoleAssignment model.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(CourseAccessRoleAssignmentTests, self).setUp()
|
||||
self.program_course_enrollment = ProgramCourseEnrollmentFactory()
|
||||
self.pending_role_assignment = CourseAccessRoleAssignmentFactory(
|
||||
enrollment=self.program_course_enrollment,
|
||||
role=ProgramCourseEnrollmentRoles.COURSE_STAFF,
|
||||
)
|
||||
|
||||
def test_str_and_repr(self):
|
||||
"""
|
||||
Make sure str() and repr() work correctly on instances of this model.
|
||||
"""
|
||||
assert str(self.pending_role_assignment) == "[CourseAccessRoleAssignment id=1]"
|
||||
|
||||
# The record contains timestamp information, and a repeat of the ProgramCourseEnrollment repr()
|
||||
# already tested above, let's just test the parts of the repr()
|
||||
# that come before that.
|
||||
assert (
|
||||
"<CourseAccessRoleAssignment id=1"
|
||||
" role='staff'"
|
||||
" enrollment=<ProgramCourseEnrollment id=1"
|
||||
) in repr(self.pending_role_assignment)
|
||||
|
||||
def test_unique(self):
|
||||
"""
|
||||
Multiple records with the same enrollment and role cannot be created
|
||||
"""
|
||||
with self.assertRaises(IntegrityError):
|
||||
CourseAccessRoleAssignmentFactory(
|
||||
enrollment=self.program_course_enrollment,
|
||||
role=ProgramCourseEnrollmentRoles.COURSE_STAFF,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user