diff --git a/common/djangoapps/entitlements/admin.py b/common/djangoapps/entitlements/admin.py index e06d8d346f..ffe2aff21e 100644 --- a/common/djangoapps/entitlements/admin.py +++ b/common/djangoapps/entitlements/admin.py @@ -1,6 +1,11 @@ +"""Admin forms for Course Entitlements""" +from django import forms from django.contrib import admin -from .models import CourseEntitlement, CourseEntitlementPolicy +from opaque_keys import InvalidKeyError +from opaque_keys.edx.keys import CourseKey +from xmodule.modulestore.django import modulestore +from .models import CourseEntitlement, CourseEntitlementPolicy, CourseEntitlementSupportDetail @admin.register(CourseEntitlement) @@ -17,6 +22,49 @@ class CourseEntitlementAdmin(admin.ModelAdmin): raw_id_fields = ('enrollment_course_run', 'user',) +class CourseEntitlementSupportDetailForm(forms.ModelForm): + """Form for adding entitlement support details, exists mostly for testing purposes""" + def __init__(self, *args, **kwargs): + super(CourseEntitlementSupportDetailForm, self).__init__(*args, **kwargs) + + if self.data.get('unenrolled_run'): + try: + self.data['unenrolled_run'] = CourseKey.from_string(self.data['unenrolled_run']) + except InvalidKeyError: + raise forms.ValidationError("No valid CourseKey for id {}!".format(self.data['unenrolled_run'])) + + def clean_course_id(self): + """Cleans course id and attempts to make course key from string version of key""" + course_id = self.cleaned_data['unenrolled_run'] + try: + course_key = CourseKey.from_string(course_id) + except InvalidKeyError: + raise forms.ValidationError("Cannot make a valid CourseKey from id {}!".format(course_id)) + + if not modulestore().has_course(course_key): + raise forms.ValidationError("Cannot find course with id {} in the modulestore".format(course_id)) + + return course_key + + class Meta: + fields = '__all__' + model = CourseEntitlementSupportDetail + + +@admin.register(CourseEntitlementSupportDetail) +class CourseEntitlementSupportDetailAdmin(admin.ModelAdmin): + """ + Registration of CourseEntitlementSupportDetail for Django Admin + """ + list_display = ('entitlement', + 'support_user', + 'reason', + 'comments', + 'unenrolled_run') + raw_id_fields = ('unenrolled_run', 'support_user',) + form = CourseEntitlementSupportDetailForm + + @admin.register(CourseEntitlementPolicy) class CourseEntitlementPolicyAdmin(admin.ModelAdmin): """ diff --git a/common/djangoapps/entitlements/migrations/0005_courseentitlementsupportdetail.py b/common/djangoapps/entitlements/migrations/0005_courseentitlementsupportdetail.py new file mode 100644 index 0000000000..3dc823cfa0 --- /dev/null +++ b/common/djangoapps/entitlements/migrations/0005_courseentitlementsupportdetail.py @@ -0,0 +1,35 @@ +# -*- 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 + + +class Migration(migrations.Migration): + + dependencies = [ + ('course_overviews', '0014_courseoverview_certificate_available_date'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('entitlements', '0004_auto_20171206_1729'), + ] + + operations = [ + migrations.CreateModel( + name='CourseEntitlementSupportDetail', + 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)), + ('reason', models.CharField(max_length=15, choices=[(b'LEAVE', 'Learner requested leave session for expired entitlement'), (b'CHANGE', 'Learner requested session change for expired entitlement'), (b'LEARNER_NEW', 'Learner requested new entitlement'), (b'COURSE_TEAM_NEW', 'Course team requested entitlement for learnerg'), (b'OTHER', 'Other')])), + ('comments', models.TextField(null=True)), + ('entitlement', models.ForeignKey(to='entitlements.CourseEntitlement')), + ('support_user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ('unenrolled_run', models.ForeignKey(db_constraint=False, blank=True, to='course_overviews.CourseOverview', null=True)), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/common/djangoapps/entitlements/models.py b/common/djangoapps/entitlements/models.py index bf2365902a..36d3b5d9b1 100644 --- a/common/djangoapps/entitlements/models.py +++ b/common/djangoapps/entitlements/models.py @@ -301,3 +301,42 @@ class CourseEntitlement(TimeStampedModel): expired_at__isnull=False, enrollment_course_run=None ).select_related('user').select_related('enrollment_course_run') + + +class CourseEntitlementSupportDetail(TimeStampedModel): + """ + Table recording support interactions with an entitlement + """ + LEAVE_SESSION = 'LEAVE' + CHANGE_SESSION = 'CHANGE' + LEARNER_REQUEST_NEW = 'LEARNER_NEW' + COURSE_TEAM_REQUEST_NEW = 'COURSE_TEAM_NEW' + OTHER = 'OTHER' + ENTITLEMENT_SUPPORT_REASONS = ( + (LEAVE_SESSION, u'Learner requested leave session for expired entitlement'), + (CHANGE_SESSION, u'Learner requested session change for expired entitlement'), + (LEARNER_REQUEST_NEW, u'Learner requested new entitlement'), + (COURSE_TEAM_REQUEST_NEW, u'Course team requested entitlement for learnerg'), + (OTHER, u'Other'), + ) + entitlement = models.ForeignKey('entitlements.CourseEntitlement') + support_user = models.ForeignKey(settings.AUTH_USER_MODEL) + + reason = models.CharField(max_length=15, choices=ENTITLEMENT_SUPPORT_REASONS) + comments = models.TextField(null=True) + + unenrolled_run = models.ForeignKey( + CourseOverview, + null=True, + blank=True, + db_constraint=False, + ) + + def __unicode__(self): + """Unicode representation of an Entitlement""" + return u'Course Entitlement Suppor Detail: entitlement: {}, support_user: {}, reason: {}'\ + .format( + self.entitlement, + self.support_user, + self.reason, + )