Merge pull request #20135 from edx/rir/addProgramEnrollments
Add ProgramEnrollment app and model for masters
This commit is contained in:
0
lms/djangoapps/program_enrollments/__init__.py
Normal file
0
lms/djangoapps/program_enrollments/__init__.py
Normal file
17
lms/djangoapps/program_enrollments/admin.py
Normal file
17
lms/djangoapps/program_enrollments/admin.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Admin tool for the Program Enrollments models
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
from lms.djangoapps.program_enrollments.models import ProgramEnrollment
|
||||
|
||||
|
||||
class ProgramEnrollmentAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
Admin tool for the ProgramEnrollment model
|
||||
"""
|
||||
|
||||
admin.site.register(ProgramEnrollment, ProgramEnrollmentAdmin)
|
||||
18
lms/djangoapps/program_enrollments/apps.py
Normal file
18
lms/djangoapps/program_enrollments/apps.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
ProgramEnrollments Application Configuration
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ProgramEnrollmentsConfig(AppConfig):
|
||||
"""
|
||||
Application configuration for ProgramEnrollment
|
||||
"""
|
||||
name = 'lms.djangoapps.program_enrollments'
|
||||
|
||||
plugin_app = {
|
||||
'url_config': {},
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.20 on 2019-04-09 19:32
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import model_utils.fields
|
||||
import simple_history.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='HistoricalProgramEnrollment',
|
||||
fields=[
|
||||
('id', models.IntegerField(auto_created=True, blank=True, db_index=True, 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')),
|
||||
('external_user_key', models.CharField(db_index=True, max_length=255, null=True)),
|
||||
('program_uuid', models.UUIDField(db_index=True)),
|
||||
('curriculum_uuid', models.UUIDField(db_index=True)),
|
||||
('status', models.CharField(choices=[('enrolled', 'enrolled'), ('pending', 'pending'), ('suspended', 'suspended'), ('withdrawn', 'withdrawn')], max_length=9)),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField()),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('user', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': 'history_date',
|
||||
'verbose_name': 'historical program enrollment',
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ProgramEnrollment',
|
||||
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')),
|
||||
('external_user_key', models.CharField(db_index=True, max_length=255, null=True)),
|
||||
('program_uuid', models.UUIDField(db_index=True)),
|
||||
('curriculum_uuid', models.UUIDField(db_index=True)),
|
||||
('status', models.CharField(choices=[('enrolled', 'enrolled'), ('pending', 'pending'), ('suspended', 'suspended'), ('withdrawn', 'withdrawn')], max_length=9)),
|
||||
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
63
lms/djangoapps/program_enrollments/models.py
Normal file
63
lms/djangoapps/program_enrollments/models.py
Normal file
@@ -0,0 +1,63 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Django model specifications for the Program Enrollments API
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
from model_utils.models import TimeStampedModel
|
||||
from simple_history.models import HistoricalRecords
|
||||
|
||||
|
||||
class ProgramEnrollment(TimeStampedModel): # pylint: disable=model-missing-unicode
|
||||
"""
|
||||
This is a model for Program Enrollments from the registrar service
|
||||
|
||||
.. pii: PII is found in the external key for a program enrollment
|
||||
.. pii_types: other
|
||||
.. pii_retirement: local_api
|
||||
"""
|
||||
STATUSES = (
|
||||
('enrolled', 'enrolled'),
|
||||
('pending', 'pending'),
|
||||
('suspended', 'suspended'),
|
||||
('withdrawn', 'withdrawn'),
|
||||
)
|
||||
|
||||
class Meta(object):
|
||||
app_label = "program_enrollments"
|
||||
|
||||
user = models.ForeignKey(
|
||||
User,
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
external_user_key = models.CharField(
|
||||
db_index=True,
|
||||
max_length=255,
|
||||
null=True
|
||||
)
|
||||
program_uuid = models.UUIDField(db_index=True, null=False)
|
||||
curriculum_uuid = models.UUIDField(db_index=True, null=False)
|
||||
status = models.CharField(max_length=9, choices=STATUSES)
|
||||
historical_records = HistoricalRecords()
|
||||
|
||||
@classmethod
|
||||
def retire_user(cls, user_id):
|
||||
"""
|
||||
With the parameter user_id, retire the external_user_key field
|
||||
|
||||
Return True if there is data that was retired
|
||||
Return False if there is no matching data
|
||||
"""
|
||||
|
||||
enrollments = cls.objects.filter(user=user_id)
|
||||
if not enrollments:
|
||||
return False
|
||||
|
||||
for enrollment in enrollments:
|
||||
enrollment.historical_records.update(external_user_key=None)
|
||||
|
||||
enrollments.update(external_user_key=None)
|
||||
return True
|
||||
56
lms/djangoapps/program_enrollments/tests/test_models.py
Normal file
56
lms/djangoapps/program_enrollments/tests/test_models.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""
|
||||
Unit tests for ProgramEnrollment models.
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from uuid import uuid4
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from lms.djangoapps.program_enrollments.models import ProgramEnrollment
|
||||
from student.tests.factories import UserFactory
|
||||
|
||||
|
||||
class ProgramEnrollmentModelTests(TestCase):
|
||||
"""
|
||||
Tests for the ProgramEnrollment model.
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up the test data used in the specific tests
|
||||
"""
|
||||
super(ProgramEnrollmentModelTests, self).setUp()
|
||||
self.user = UserFactory.create()
|
||||
self.enrollment = ProgramEnrollment.objects.create(
|
||||
user=self.user,
|
||||
external_user_key='abc',
|
||||
program_uuid=uuid4(),
|
||||
curriculum_uuid=uuid4(),
|
||||
status='enrolled'
|
||||
)
|
||||
|
||||
def test_user_retirement(self):
|
||||
"""
|
||||
Test that the external_user_key is uccessfully retired for a user's program enrollments and history.
|
||||
"""
|
||||
new_status = 'withdrawn'
|
||||
|
||||
self.enrollment.status = new_status
|
||||
self.enrollment.save()
|
||||
|
||||
# Ensure that all the records had values for external_user_key
|
||||
self.assertEquals(self.enrollment.external_user_key, 'abc')
|
||||
|
||||
self.assertTrue(self.enrollment.historical_records.all())
|
||||
for record in self.enrollment.historical_records.all():
|
||||
self.assertEquals(record.external_user_key, 'abc')
|
||||
|
||||
ProgramEnrollment.retire_user(self.user.id)
|
||||
self.enrollment.refresh_from_db()
|
||||
|
||||
# Ensure those values are retired
|
||||
self.assertEquals(self.enrollment.external_user_key, None)
|
||||
|
||||
self.assertTrue(self.enrollment.historical_records.all())
|
||||
for record in self.enrollment.historical_records.all():
|
||||
self.assertEquals(record.external_user_key, None)
|
||||
9
lms/djangoapps/program_enrollments/views.py
Normal file
9
lms/djangoapps/program_enrollments/views.py
Normal file
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
ProgramEnrollment Views
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
3
setup.py
3
setup.py
@@ -82,7 +82,8 @@ setup(
|
||||
"zendesk_proxy = openedx.core.djangoapps.zendesk_proxy.apps:ZendeskProxyConfig",
|
||||
"instructor = lms.djangoapps.instructor.apps:InstructorConfig",
|
||||
"password_policy = openedx.core.djangoapps.password_policy.apps:PasswordPolicyConfig",
|
||||
"user_authn = openedx.core.djangoapps.user_authn.apps:UserAuthnConfig"
|
||||
"user_authn = openedx.core.djangoapps.user_authn.apps:UserAuthnConfig",
|
||||
"program_enrollments = lms.djangoapps.program_enrollments.apps:ProgramEnrollmentsConfig",
|
||||
],
|
||||
"cms.djangoapp": [
|
||||
"announcements = openedx.features.announcements.apps:AnnouncementsConfig",
|
||||
|
||||
Reference in New Issue
Block a user