Add new enrollment message to the dashboard
The body of the enrollment message template Tokenize platform name in message. Changing to a datetime enrollment approach Adding sorting. A little refactoring. Adding confguration model for time delta Adding admin registration and basic form for new config model. Fixing docstring typo Updating default time delta to 0, adding test to show it disabled functionality. Removing the form from configuration and tweaking the enrollment message html
This commit is contained in:
committed by
stephensanchez
parent
7f4cc1c75c
commit
bf112f7ef0
@@ -157,7 +157,7 @@ class ChooseModeView(View):
|
||||
# it doesn't matter, but it will avoid hitting the database.
|
||||
if requested_mode == 'honor':
|
||||
CourseEnrollment.enroll(user, course_key, requested_mode)
|
||||
return redirect('dashboard')
|
||||
return redirect(reverse('dashboard'))
|
||||
|
||||
mode_info = allowed_modes[requested_mode]
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
'''
|
||||
django admin pages for courseware model
|
||||
'''
|
||||
from config_models.admin import ConfigurationModelAdmin
|
||||
|
||||
from student.models import UserProfile, UserTestGroup, CourseEnrollmentAllowed
|
||||
from student.models import UserProfile, UserTestGroup, CourseEnrollmentAllowed, DashboardConfiguration
|
||||
from student.models import CourseEnrollment, Registration, PendingNameChange, CourseAccessRole, CourseAccessRoleAdmin
|
||||
from ratelimitbackend import admin
|
||||
|
||||
@@ -19,3 +20,5 @@ admin.site.register(Registration)
|
||||
admin.site.register(PendingNameChange)
|
||||
|
||||
admin.site.register(CourseAccessRole, CourseAccessRoleAdmin)
|
||||
|
||||
admin.site.register(DashboardConfiguration, ConfigurationModelAdmin)
|
||||
|
||||
@@ -6,6 +6,7 @@ from django.contrib.auth.models import User
|
||||
from django.contrib.auth.forms import PasswordResetForm
|
||||
from django.contrib.auth.hashers import UNUSABLE_PASSWORD
|
||||
|
||||
|
||||
class PasswordResetFormNoActive(PasswordResetForm):
|
||||
def clean_email(self):
|
||||
"""
|
||||
@@ -21,4 +22,4 @@ class PasswordResetFormNoActive(PasswordResetForm):
|
||||
if any((user.password == UNUSABLE_PASSWORD)
|
||||
for user in self.users_cache):
|
||||
raise forms.ValidationError(self.error_messages['unusable'])
|
||||
return email
|
||||
return email
|
||||
@@ -0,0 +1,179 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'DashboardConfiguration'
|
||||
db.create_table('student_dashboardconfiguration', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('change_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
|
||||
('changed_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, on_delete=models.PROTECT)),
|
||||
('enabled', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||
('recent_enrollment_time_delta', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)),
|
||||
))
|
||||
db.send_create_signal('student', ['DashboardConfiguration'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'DashboardConfiguration'
|
||||
db.delete_table('student_dashboardconfiguration')
|
||||
|
||||
|
||||
models = {
|
||||
'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'auth.permission': {
|
||||
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||
},
|
||||
'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
'student.anonymoususerid': {
|
||||
'Meta': {'object_name': 'AnonymousUserId'},
|
||||
'anonymous_user_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}),
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
|
||||
},
|
||||
'student.courseaccessrole': {
|
||||
'Meta': {'unique_together': "(('user', 'org', 'course_id', 'role'),)", 'object_name': 'CourseAccessRole'},
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'org': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'blank': 'True'}),
|
||||
'role': ('django.db.models.fields.CharField', [], {'max_length': '64', 'db_index': 'True'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
|
||||
},
|
||||
'student.courseenrollment': {
|
||||
'Meta': {'ordering': "('user', 'course_id')", 'unique_together': "(('user', 'course_id'),)", 'object_name': 'CourseEnrollment'},
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'mode': ('django.db.models.fields.CharField', [], {'default': "'honor'", 'max_length': '100'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
|
||||
},
|
||||
'student.courseenrollmentallowed': {
|
||||
'Meta': {'unique_together': "(('email', 'course_id'),)", 'object_name': 'CourseEnrollmentAllowed'},
|
||||
'auto_enroll': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
|
||||
'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'student.dashboardconfiguration': {
|
||||
'Meta': {'object_name': 'DashboardConfiguration'},
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'recent_enrollment_time_delta': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
|
||||
},
|
||||
'student.loginfailures': {
|
||||
'Meta': {'object_name': 'LoginFailures'},
|
||||
'failure_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'lockout_until': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
|
||||
},
|
||||
'student.passwordhistory': {
|
||||
'Meta': {'object_name': 'PasswordHistory'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'time_set': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
|
||||
},
|
||||
'student.pendingemailchange': {
|
||||
'Meta': {'object_name': 'PendingEmailChange'},
|
||||
'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'new_email': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
|
||||
},
|
||||
'student.pendingnamechange': {
|
||||
'Meta': {'object_name': 'PendingNameChange'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'new_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'rationale': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
|
||||
},
|
||||
'student.registration': {
|
||||
'Meta': {'object_name': 'Registration', 'db_table': "'auth_registration'"},
|
||||
'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
|
||||
},
|
||||
'student.userprofile': {
|
||||
'Meta': {'object_name': 'UserProfile', 'db_table': "'auth_userprofile'"},
|
||||
'allow_certificate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'city': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'null': 'True', 'blank': 'True'}),
|
||||
'courseware': ('django.db.models.fields.CharField', [], {'default': "'course.xml'", 'max_length': '255', 'blank': 'True'}),
|
||||
'gender': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '6', 'null': 'True', 'blank': 'True'}),
|
||||
'goals': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'language': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
|
||||
'level_of_education': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '6', 'null': 'True', 'blank': 'True'}),
|
||||
'location': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
|
||||
'mailing_address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'meta': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"}),
|
||||
'year_of_birth': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
'student.usersignupsource': {
|
||||
'Meta': {'object_name': 'UserSignupSource'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'site': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
|
||||
},
|
||||
'student.userstanding': {
|
||||
'Meta': {'object_name': 'UserStanding'},
|
||||
'account_status': ('django.db.models.fields.CharField', [], {'max_length': '31', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'standing_last_changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'standing'", 'unique': 'True', 'to': "orm['auth.User']"})
|
||||
},
|
||||
'student.usertestgroup': {
|
||||
'Meta': {'object_name': 'UserTestGroup'},
|
||||
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}),
|
||||
'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'db_index': 'True', 'symmetrical': 'False'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['student']
|
||||
@@ -32,6 +32,7 @@ from django.dispatch import receiver, Signal
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.utils.translation import ugettext_noop
|
||||
from django_countries import CountryField
|
||||
from config_models.models import ConfigurationModel
|
||||
from track import contexts
|
||||
from eventtracking import tracker
|
||||
from importlib import import_module
|
||||
@@ -1388,3 +1389,20 @@ def enforce_single_login(sender, request, user, signal, **kwargs): # pylint:
|
||||
else:
|
||||
key = None
|
||||
user.profile.set_login_session(key)
|
||||
|
||||
|
||||
class DashboardConfiguration(ConfigurationModel):
|
||||
"""Dashboard Configuration settings.
|
||||
|
||||
Includes configuration options for the dashboard, which impact behavior and rendering for the application.
|
||||
|
||||
"""
|
||||
recent_enrollment_time_delta = models.PositiveIntegerField(
|
||||
default=0,
|
||||
help_text="The number of seconds in which a new enrollment is considered 'recent'. "
|
||||
"Used to display notifications."
|
||||
)
|
||||
|
||||
@property
|
||||
def recent_enrollment_seconds(self):
|
||||
return self.recent_enrollment_time_delta
|
||||
|
||||
127
common/djangoapps/student/tests/test_recent_enrollments.py
Normal file
127
common/djangoapps/student/tests/test_recent_enrollments.py
Normal file
@@ -0,0 +1,127 @@
|
||||
"""
|
||||
Tests for the recently enrolled messaging within the Dashboard.
|
||||
"""
|
||||
import datetime
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import Client
|
||||
from opaque_keys.edx import locator
|
||||
from pytz import UTC
|
||||
import unittest
|
||||
|
||||
from student.tests.factories import UserFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from student.models import CourseEnrollment, DashboardConfiguration
|
||||
from student.views import get_course_enrollment_pairs, _get_recently_enrolled_courses
|
||||
|
||||
|
||||
class TestRecentEnrollments(ModuleStoreTestCase):
|
||||
"""
|
||||
Unit tests for getting the list of courses for a logged in user
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Add a student
|
||||
"""
|
||||
super(TestRecentEnrollments, self).setUp()
|
||||
self.student = UserFactory()
|
||||
|
||||
# Old Course
|
||||
old_course_location = locator.CourseLocator('Org0', 'Course0', 'Run0')
|
||||
course, enrollment = self._create_course_and_enrollment(old_course_location)
|
||||
enrollment.created = datetime.datetime(1900, 12, 31, 0, 0, 0, 0)
|
||||
enrollment.save()
|
||||
|
||||
# New Course
|
||||
course_location = locator.CourseLocator('Org1', 'Course1', 'Run1')
|
||||
self._create_course_and_enrollment(course_location)
|
||||
|
||||
def _create_course_and_enrollment(self, course_location):
|
||||
""" Creates a course and associated enrollment. """
|
||||
course = CourseFactory.create(
|
||||
org=course_location.org,
|
||||
number=course_location.course,
|
||||
run=course_location.run
|
||||
)
|
||||
enrollment = CourseEnrollment.enroll(self.student, course.id)
|
||||
return course, enrollment
|
||||
|
||||
def test_recently_enrolled_courses(self):
|
||||
"""
|
||||
Test if the function for filtering recent enrollments works appropriately.
|
||||
"""
|
||||
config = DashboardConfiguration(recent_enrollment_time_delta=60)
|
||||
config.save()
|
||||
# get courses through iterating all courses
|
||||
courses_list = list(get_course_enrollment_pairs(self.student, None, []))
|
||||
self.assertEqual(len(courses_list), 2)
|
||||
|
||||
recent_course_list = _get_recently_enrolled_courses(courses_list)
|
||||
self.assertEqual(len(recent_course_list), 1)
|
||||
|
||||
def test_zero_second_delta(self):
|
||||
"""
|
||||
Tests that the recent enrollment list is empty if configured to zero seconds.
|
||||
"""
|
||||
config = DashboardConfiguration(recent_enrollment_time_delta=0)
|
||||
config.save()
|
||||
courses_list = list(get_course_enrollment_pairs(self.student, None, []))
|
||||
self.assertEqual(len(courses_list), 2)
|
||||
|
||||
recent_course_list = _get_recently_enrolled_courses(courses_list)
|
||||
self.assertEqual(len(recent_course_list), 0)
|
||||
|
||||
def test_enrollments_sorted_most_recent(self):
|
||||
"""
|
||||
Test that the list of newly created courses are properly sorted to show the most
|
||||
recent enrollments first.
|
||||
|
||||
"""
|
||||
config = DashboardConfiguration(recent_enrollment_time_delta=600)
|
||||
config.save()
|
||||
|
||||
# Create a number of new enrollments and courses, and force their creation behind
|
||||
# the first enrollment
|
||||
course_location = locator.CourseLocator('Org2', 'Course2', 'Run2')
|
||||
_, enrollment2 = self._create_course_and_enrollment(course_location)
|
||||
enrollment2.created = datetime.datetime.now(UTC) - datetime.timedelta(seconds=5)
|
||||
enrollment2.save()
|
||||
|
||||
course_location = locator.CourseLocator('Org3', 'Course3', 'Run3')
|
||||
_, enrollment3 = self._create_course_and_enrollment(course_location)
|
||||
enrollment3.created = datetime.datetime.now(UTC) - datetime.timedelta(seconds=10)
|
||||
enrollment3.save()
|
||||
|
||||
course_location = locator.CourseLocator('Org4', 'Course4', 'Run4')
|
||||
_, enrollment4 = self._create_course_and_enrollment(course_location)
|
||||
enrollment4.created = datetime.datetime.now(UTC) - datetime.timedelta(seconds=15)
|
||||
enrollment4.save()
|
||||
|
||||
course_location = locator.CourseLocator('Org5', 'Course5', 'Run5')
|
||||
_, enrollment5 = self._create_course_and_enrollment(course_location)
|
||||
enrollment5.created = datetime.datetime.now(UTC) - datetime.timedelta(seconds=20)
|
||||
enrollment5.save()
|
||||
|
||||
courses_list = list(get_course_enrollment_pairs(self.student, None, []))
|
||||
self.assertEqual(len(courses_list), 6)
|
||||
|
||||
recent_course_list = _get_recently_enrolled_courses(courses_list)
|
||||
self.assertEqual(len(recent_course_list), 5)
|
||||
|
||||
self.assertEqual(recent_course_list[1][1], enrollment2)
|
||||
self.assertEqual(recent_course_list[2][1], enrollment3)
|
||||
self.assertEqual(recent_course_list[3][1], enrollment4)
|
||||
self.assertEqual(recent_course_list[4][1], enrollment5)
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
def test_dashboard_rendering(self):
|
||||
"""
|
||||
Tests that the dashboard renders the recent enrollment messages appropriately.
|
||||
"""
|
||||
config = DashboardConfiguration(recent_enrollment_time_delta=600)
|
||||
config.save()
|
||||
self.client = Client()
|
||||
self.client.login(username=self.student.username, password='test')
|
||||
response = self.client.get(reverse("dashboard"))
|
||||
self.assertContains(response, "You have successfully enrolled in")
|
||||
@@ -46,8 +46,8 @@ from student.models import (
|
||||
Registration, UserProfile, PendingNameChange,
|
||||
PendingEmailChange, CourseEnrollment, unique_id_for_user,
|
||||
CourseEnrollmentAllowed, UserStanding, LoginFailures,
|
||||
create_comments_service_user, PasswordHistory, UserSignupSource
|
||||
)
|
||||
create_comments_service_user, PasswordHistory, UserSignupSource,
|
||||
DashboardConfiguration)
|
||||
from student.forms import PasswordResetFormNoActive
|
||||
|
||||
from verify_student.models import SoftwareSecurePhotoVerification, MidcourseReverificationWindow
|
||||
@@ -471,6 +471,10 @@ def dashboard(request):
|
||||
# enrollments, because it could have been a data push snafu.
|
||||
course_enrollment_pairs = list(get_course_enrollment_pairs(user, course_org_filter, org_filter_out_set))
|
||||
|
||||
# Check to see if the student has recently enrolled in a course. If so, display a notification message confirming
|
||||
# the enrollment.
|
||||
enrollment_message = _create_recent_enrollment_message(course_enrollment_pairs)
|
||||
|
||||
course_optouts = Optout.objects.filter(user=user).values_list('course_id', flat=True)
|
||||
|
||||
message = ""
|
||||
@@ -551,6 +555,7 @@ def dashboard(request):
|
||||
current_language = settings.LANGUAGE_DICT[settings.LANGUAGE_CODE]
|
||||
|
||||
context = {
|
||||
'enrollment_message': enrollment_message,
|
||||
'course_enrollment_pairs': course_enrollment_pairs,
|
||||
'course_optouts': course_optouts,
|
||||
'message': message,
|
||||
@@ -586,6 +591,49 @@ def dashboard(request):
|
||||
return render_to_response('dashboard.html', context)
|
||||
|
||||
|
||||
def _create_recent_enrollment_message(course_enrollment_pairs):
|
||||
"""Builds a recent course enrollment message
|
||||
|
||||
Constructs a new message template based on any recent course enrollments for the student.
|
||||
|
||||
Args:
|
||||
course_enrollment_pairs (list): A list of tuples containing courses, and the associated enrollment information.
|
||||
|
||||
Returns:
|
||||
A string representing the HTML message output from the message template.
|
||||
|
||||
"""
|
||||
recent_course_enrollment_pairs = _get_recently_enrolled_courses(course_enrollment_pairs)
|
||||
if recent_course_enrollment_pairs:
|
||||
return render_to_string(
|
||||
'enrollment/course_enrollment_message.html',
|
||||
{'recent_course_enrollment_pairs': recent_course_enrollment_pairs,}
|
||||
)
|
||||
|
||||
|
||||
def _get_recently_enrolled_courses(course_enrollment_pairs):
|
||||
"""Checks to see if the student has recently enrolled in courses.
|
||||
|
||||
Checks to see if any of the enrollments in the course_enrollment_pairs have been recently created and activated.
|
||||
|
||||
Args:
|
||||
course_enrollment_pairs (list): A list of tuples containing courses, and the associated enrollment information.
|
||||
|
||||
Returns:
|
||||
A list of tuples for the course and enrollment.
|
||||
|
||||
"""
|
||||
seconds = DashboardConfiguration.current().recent_enrollment_time_delta
|
||||
sorted_list = sorted(course_enrollment_pairs, key=lambda created: created[1].created, reverse=True)
|
||||
time_delta = (datetime.datetime.now(UTC) - datetime.timedelta(seconds=seconds))
|
||||
return [
|
||||
(course, enrollment) for course, enrollment in sorted_list
|
||||
# If the enrollment has no created date, we are explicitly excluding the course
|
||||
# from the list of recent enrollments.
|
||||
if enrollment.is_active and enrollment.created > time_delta
|
||||
]
|
||||
|
||||
|
||||
def try_change_enrollment(request):
|
||||
"""
|
||||
This method calls change_enrollment if the necessary POST
|
||||
|
||||
@@ -192,6 +192,10 @@
|
||||
</section>
|
||||
%endif
|
||||
|
||||
%if enrollment_message:
|
||||
${enrollment_message}
|
||||
%endif
|
||||
|
||||
% if duplicate_provider:
|
||||
<section class="dashboard-banner third-party-auth">
|
||||
## Translators: this message is displayed when a user tries to link their account with a third-party authentication provider (for example, Google or LinkedIn) with a given edX account, but their third-party account is already associated with another edX account. provider_name is the name of the third-party authentication provider, and platform_name is the name of the edX deployment.
|
||||
|
||||
6
lms/templates/enrollment/course_enrollment_message.html
Normal file
6
lms/templates/enrollment/course_enrollment_message.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
% for course, enrollment in recent_course_enrollment_pairs:
|
||||
<section class="dashboard-banner">
|
||||
<p class='activation-message'>${_("You have successfully enrolled in {enrolled_course}.").format(enrolled_course=course.display_name)}</p>
|
||||
</section>
|
||||
% endfor
|
||||
Reference in New Issue
Block a user