This emits enrollment mode changes events
This commit is contained in:
@@ -328,6 +328,7 @@ class PendingEmailChange(models.Model):
|
||||
|
||||
EVENT_NAME_ENROLLMENT_ACTIVATED = 'edx.course.enrollment.activated'
|
||||
EVENT_NAME_ENROLLMENT_DEACTIVATED = 'edx.course.enrollment.deactivated'
|
||||
EVENT_NAME_ENROLLMENT_MODE_CHANGED = 'edx.course.enrollment.mode_changed'
|
||||
|
||||
|
||||
class PasswordHistory(models.Model):
|
||||
@@ -716,6 +717,10 @@ class CourseEnrollment(models.Model):
|
||||
u"offering:{}".format(self.course_id.offering),
|
||||
u"mode:{}".format(self.mode)]
|
||||
)
|
||||
if mode_changed:
|
||||
# the user's default mode is "honor" and disabled for a course
|
||||
# mode change events will only be emitted when the user's mode changes from this
|
||||
self.emit_event(EVENT_NAME_ENROLLMENT_MODE_CHANGED)
|
||||
|
||||
def emit_event(self, event_name):
|
||||
"""
|
||||
|
||||
@@ -310,6 +310,18 @@ class EnrollInCourseTest(TestCase):
|
||||
self.assertFalse(self.mock_tracker.emit.called) # pylint: disable=maybe-no-member
|
||||
self.mock_tracker.reset_mock()
|
||||
|
||||
def assert_enrollment_mode_change_event_was_emitted(self, user, course_key, mode):
|
||||
"""Ensures an enrollment mode change event was emitted"""
|
||||
self.mock_tracker.emit.assert_called_once_with( # pylint: disable=maybe-no-member
|
||||
'edx.course.enrollment.mode_changed',
|
||||
{
|
||||
'course_id': course_key.to_deprecated_string(),
|
||||
'user_id': user.pk,
|
||||
'mode': mode
|
||||
}
|
||||
)
|
||||
self.mock_tracker.reset_mock()
|
||||
|
||||
def assert_enrollment_event_was_emitted(self, user, course_key):
|
||||
"""Ensures an enrollment event was emitted since the last event related assertion"""
|
||||
self.mock_tracker.emit.assert_called_once_with( # pylint: disable=maybe-no-member
|
||||
@@ -447,6 +459,23 @@ class EnrollInCourseTest(TestCase):
|
||||
self.assertTrue(CourseEnrollment.is_enrolled(user, course_id))
|
||||
self.assert_enrollment_event_was_emitted(user, course_id)
|
||||
|
||||
def test_change_enrollment_modes(self):
|
||||
user = User.objects.create(username="justin", email="jh@fake.edx.org")
|
||||
course_id = SlashSeparatedCourseKey("edX", "Test101", "2013")
|
||||
|
||||
CourseEnrollment.enroll(user, course_id)
|
||||
self.assert_enrollment_event_was_emitted(user, course_id)
|
||||
|
||||
CourseEnrollment.enroll(user, course_id, "audit")
|
||||
self.assert_enrollment_mode_change_event_was_emitted(user, course_id, "audit")
|
||||
|
||||
# same enrollment mode does not emit an event
|
||||
CourseEnrollment.enroll(user, course_id, "audit")
|
||||
self.assert_no_events_were_emitted()
|
||||
|
||||
CourseEnrollment.enroll(user, course_id, "honor")
|
||||
self.assert_enrollment_mode_change_event_was_emitted(user, course_id, "honor")
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
|
||||
23
lms/djangoapps/courseware/features/change_enrollment.feature
Normal file
23
lms/djangoapps/courseware/features/change_enrollment.feature
Normal file
@@ -0,0 +1,23 @@
|
||||
Feature: Change Enrollment Events
|
||||
As a registered user
|
||||
I want to change my enrollment mode
|
||||
|
||||
|
||||
Scenario: I can change my enrollment
|
||||
Given The course "6.002x" exists
|
||||
And the course "6.002x" has all enrollment modes
|
||||
And I am logged in
|
||||
And I visit the courses page
|
||||
When I register to audit the course
|
||||
And a "edx.course.enrollment.activated" server event is emitted
|
||||
And a "edx.course.enrollment.mode_changed" server events is emitted
|
||||
|
||||
And I visit the dashboard
|
||||
And I click on Challenge Yourself
|
||||
And I choose an honor code upgrade
|
||||
Then I should be on the dashboard page
|
||||
Then 2 "edx.course.enrollment.mode_changed" server event is emitted
|
||||
|
||||
# don't emit another mode_changed event upon unenrollment
|
||||
When I unregister for the course numbered "6.002x"
|
||||
Then 2 "edx.course.enrollment.mode_changed" server events is emitted
|
||||
49
lms/djangoapps/courseware/features/change_enrollment.py
Normal file
49
lms/djangoapps/courseware/features/change_enrollment.py
Normal file
@@ -0,0 +1,49 @@
|
||||
""" Provides lettuce acceptance methods for course enrollment changes """
|
||||
|
||||
from __future__ import absolute_import
|
||||
from lettuce import world, step
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from logging import getLogger
|
||||
logger = getLogger(__name__)
|
||||
|
||||
|
||||
@step(u'the course "([^"]*)" has all enrollment modes$')
|
||||
def add_enrollment_modes_to_course(_step, course):
|
||||
""" Add honor, audit, and verified modes to the sample course """
|
||||
world.CourseModeFactory.create(
|
||||
course_id=SlashSeparatedCourseKey("edx", course, 'Test_Course'),
|
||||
mode_slug="verified",
|
||||
mode_display_name="Verified Course",
|
||||
min_price=3
|
||||
)
|
||||
world.CourseModeFactory.create(
|
||||
course_id=SlashSeparatedCourseKey("edx", course, 'Test_Course'),
|
||||
mode_slug="honor",
|
||||
mode_display_name="Honor Course",
|
||||
)
|
||||
|
||||
world.CourseModeFactory.create(
|
||||
course_id=SlashSeparatedCourseKey("edx", course, 'Test_Course'),
|
||||
mode_slug="audit",
|
||||
mode_display_name="Audit Course",
|
||||
)
|
||||
|
||||
|
||||
@step(u'I click on Challenge Yourself$')
|
||||
def challenge_yourself(_step):
|
||||
""" Simulates clicking 'Challenge Yourself' button on course """
|
||||
challenge_button = world.browser.find_by_css('.wrapper-tip')
|
||||
challenge_button.click()
|
||||
verified_button = world.browser.find_by_css('#upgrade-to-verified')
|
||||
verified_button.click()
|
||||
|
||||
|
||||
@step(u'I choose an honor code upgrade$')
|
||||
def honor_code_upgrade(_step):
|
||||
""" Simulates choosing the honor code mode on the upgrade page """
|
||||
honor_code_link = world.browser.find_by_css('.title-expand')
|
||||
honor_code_link.click()
|
||||
honor_code_checkbox = world.browser.find_by_css('#honor-code')
|
||||
honor_code_checkbox.click()
|
||||
upgrade_button = world.browser.find_by_name("certificate_mode")
|
||||
upgrade_button.click()
|
||||
@@ -15,7 +15,6 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from xmodule.course_module import CourseDescriptor
|
||||
from courseware.courses import get_course_by_id
|
||||
from xmodule import seq_module, vertical_module
|
||||
|
||||
from logging import getLogger
|
||||
logger = getLogger(__name__)
|
||||
|
||||
@@ -27,7 +26,7 @@ def configure_screenshots_for_all_steps(_step, action):
|
||||
automatic saving of screenshots before and after each step in a
|
||||
scenario.
|
||||
"""
|
||||
action=action.strip()
|
||||
action = action.strip()
|
||||
if action == 'enable':
|
||||
world.auto_capture_screenshots = True
|
||||
elif action == 'disable':
|
||||
@@ -35,6 +34,7 @@ def configure_screenshots_for_all_steps(_step, action):
|
||||
else:
|
||||
raise ValueError('Parameter `action` should be one of "enable" or "disable".')
|
||||
|
||||
|
||||
@world.absorb
|
||||
def capture_screenshot_before_after(func):
|
||||
"""
|
||||
@@ -43,12 +43,12 @@ def capture_screenshot_before_after(func):
|
||||
for each step in a scenario, but rather want to debug a single function.
|
||||
"""
|
||||
def inner(*args, **kwargs):
|
||||
prefix=round(time.time() * 1000)
|
||||
prefix = round(time.time() * 1000)
|
||||
|
||||
world.capture_screenshot("{}_{}_{}".format(
|
||||
prefix, func.func_name, 'before'
|
||||
))
|
||||
ret_val=func(*args, **kwargs)
|
||||
ret_val = func(*args, **kwargs)
|
||||
world.capture_screenshot("{}_{}_{}".format(
|
||||
prefix, func.func_name, 'after'
|
||||
))
|
||||
@@ -94,11 +94,11 @@ def i_am_registered_for_the_course(step, course):
|
||||
|
||||
# Create the user
|
||||
world.create_user('robot', 'test')
|
||||
u = User.objects.get(username='robot')
|
||||
user = User.objects.get(username='robot')
|
||||
|
||||
# If the user is not already enrolled, enroll the user.
|
||||
# TODO: change to factory
|
||||
CourseEnrollment.enroll(u, course_id(course))
|
||||
CourseEnrollment.enroll(user, course_id(course))
|
||||
|
||||
world.log_in(username='robot', password='test')
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ def reset_between_outline_scenarios(_scenario, order, outline, reasons_to_fail):
|
||||
world.event_collection.drop()
|
||||
|
||||
|
||||
@step('[aA]n? "(.*)" (server|browser) event is emitted')
|
||||
def event_is_emitted(_step, event_type, event_source):
|
||||
@step(r'([aA]n?|\d+) "(.*)" (server|browser) events? is emitted$')
|
||||
def n_events_are_emitted(_step, count, event_type, event_source):
|
||||
|
||||
# Ensure all events are written out to mongo before querying.
|
||||
world.mongo_client.fsync()
|
||||
@@ -54,8 +54,15 @@ def event_is_emitted(_step, event_type, event_source):
|
||||
'$ne': 'python/splinter'
|
||||
}
|
||||
}
|
||||
|
||||
cursor = world.event_collection.find(criteria)
|
||||
assert_equals(cursor.count(), 1)
|
||||
|
||||
try:
|
||||
number_events = int(count)
|
||||
except ValueError:
|
||||
number_events = 1
|
||||
|
||||
assert_equals(cursor.count(), number_events)
|
||||
|
||||
event = cursor.next()
|
||||
|
||||
|
||||
@@ -10,7 +10,16 @@ def i_register_for_the_course(_step, course):
|
||||
url = django_url('courses/%s/about' % world.scenario_dict['COURSE'].id.to_deprecated_string())
|
||||
world.browser.visit(url)
|
||||
world.css_click('section.intro a.register')
|
||||
assert world.is_css_present('section.container.dashboard')
|
||||
|
||||
|
||||
@step('I register to audit the course$')
|
||||
def i_register_to_audit_the_course(_step):
|
||||
url = django_url('courses/%s/about' % world.scenario_dict['COURSE'].id.to_deprecated_string())
|
||||
world.browser.visit(url)
|
||||
world.css_click('section.intro a.register')
|
||||
audit_button = world.browser.find_by_name("audit_mode")
|
||||
audit_button.click()
|
||||
assert world.is_css_present('section.container.dashboard')
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ from verify_student.models import SoftwareSecurePhotoVerification
|
||||
from reverification.tests.factories import MidcourseReverificationWindowFactory
|
||||
|
||||
|
||||
|
||||
def mock_render_to_response(*args, **kwargs):
|
||||
return render_to_response(*args, **kwargs)
|
||||
|
||||
@@ -386,8 +385,17 @@ class TestMidCourseReverifyView(TestCase):
|
||||
kwargs={"course_id": self.course_key.to_deprecated_string()})
|
||||
response = self.client.get(url)
|
||||
|
||||
# Check that user entering the reverify flow was logged
|
||||
self.mock_tracker.emit.assert_called_once_with( # pylint: disable=maybe-no-member
|
||||
self.mock_tracker.emit.assert_any_call( # pylint: disable=maybe-no-member
|
||||
'edx.course.enrollment.mode_changed',
|
||||
{
|
||||
'user_id': self.user.id,
|
||||
'course_id': self.course_key.to_deprecated_string(),
|
||||
'mode': "verified",
|
||||
}
|
||||
)
|
||||
|
||||
# Check that user entering the reverify flow was logged, and that it was the last call
|
||||
self.mock_tracker.emit.assert_called_with( # pylint: disable=maybe-no-member
|
||||
'edx.course.enrollment.reverify.started',
|
||||
{
|
||||
'user_id': self.user.id,
|
||||
@@ -395,6 +403,9 @@ class TestMidCourseReverifyView(TestCase):
|
||||
'mode': "verified",
|
||||
}
|
||||
)
|
||||
|
||||
self.assertTrue(self.mock_tracker.emit.call_count, 2)
|
||||
|
||||
self.mock_tracker.emit.reset_mock() # pylint: disable=maybe-no-member
|
||||
|
||||
self.assertEquals(response.status_code, 200)
|
||||
@@ -408,8 +419,17 @@ class TestMidCourseReverifyView(TestCase):
|
||||
|
||||
response = self.client.post(url, {'face_image': ','})
|
||||
|
||||
# Check that submission event was logged
|
||||
self.mock_tracker.emit.assert_called_once_with( # pylint: disable=maybe-no-member
|
||||
self.mock_tracker.emit.assert_any_call( # pylint: disable=maybe-no-member
|
||||
'edx.course.enrollment.mode_changed',
|
||||
{
|
||||
'user_id': self.user.id,
|
||||
'course_id': self.course_key.to_deprecated_string(),
|
||||
'mode': "verified",
|
||||
}
|
||||
)
|
||||
|
||||
# Check that submission event was logged, and that it was the last call
|
||||
self.mock_tracker.emit.assert_called_with( # pylint: disable=maybe-no-member
|
||||
'edx.course.enrollment.reverify.submitted',
|
||||
{
|
||||
'user_id': self.user.id,
|
||||
@@ -417,6 +437,9 @@ class TestMidCourseReverifyView(TestCase):
|
||||
'mode': "verified",
|
||||
}
|
||||
)
|
||||
|
||||
self.assertTrue(self.mock_tracker.emit.call_count, 2)
|
||||
|
||||
self.mock_tracker.emit.reset_mock() # pylint: disable=maybe-no-member
|
||||
|
||||
self.assertEquals(response.status_code, 302)
|
||||
|
||||
Reference in New Issue
Block a user