Commit 6e7f131c by Nimisha Asthagiri Committed by GitHub

Merge pull request #13790 from edx/release

Merge Release to Master
parents 9dc2d117 3ebdec22
...@@ -916,7 +916,6 @@ INSTALLED_APPS = ( ...@@ -916,7 +916,6 @@ INSTALLED_APPS = (
# other apps that are. Django 1.8 wants to have imported models supported # other apps that are. Django 1.8 wants to have imported models supported
# by installed apps. # by installed apps.
'lms.djangoapps.verify_student', 'lms.djangoapps.verify_student',
'lms.djangoapps.grades.apps.GradesConfig',
# Microsite configuration application # Microsite configuration application
'microsite_configuration', 'microsite_configuration',
......
...@@ -360,6 +360,49 @@ class TestCreateAccount(TestCase): ...@@ -360,6 +360,49 @@ class TestCreateAccount(TestCase):
self.assertIsNone(UserAttribute.get_user_attribute(user, REGISTRATION_UTM_PARAMETERS.get('utm_content'))) self.assertIsNone(UserAttribute.get_user_attribute(user, REGISTRATION_UTM_PARAMETERS.get('utm_content')))
self.assertIsNone(UserAttribute.get_user_attribute(user, REGISTRATION_UTM_CREATED_AT)) self.assertIsNone(UserAttribute.get_user_attribute(user, REGISTRATION_UTM_CREATED_AT))
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
def test_incomplete_utm_referral(self):
"""Verify that no referral is recorded when a cookie is not present."""
utm_cookie_name = 'edx.test.utm'
with mock.patch('student.models.RegistrationCookieConfiguration.current') as config:
instance = config.return_value
instance.utm_cookie_name = utm_cookie_name
utm_cookie = {
'utm_source': 'test-source',
'utm_medium': 'test-medium',
# No campaign
'utm_term': 'test-term',
'utm_content': 'test-content',
# No created at
}
self.client.cookies[utm_cookie_name] = json.dumps(utm_cookie)
user = self.create_account_and_fetch_profile().user
self.assertEqual(
UserAttribute.get_user_attribute(user, REGISTRATION_UTM_PARAMETERS.get('utm_source')),
utm_cookie.get('utm_source')
)
self.assertEqual(
UserAttribute.get_user_attribute(user, REGISTRATION_UTM_PARAMETERS.get('utm_medium')),
utm_cookie.get('utm_medium')
)
self.assertEqual(
UserAttribute.get_user_attribute(user, REGISTRATION_UTM_PARAMETERS.get('utm_term')),
utm_cookie.get('utm_term')
)
self.assertEqual(
UserAttribute.get_user_attribute(user, REGISTRATION_UTM_PARAMETERS.get('utm_content')),
utm_cookie.get('utm_content')
)
self.assertIsNone(
UserAttribute.get_user_attribute(user, REGISTRATION_UTM_PARAMETERS.get('utm_campaign'))
)
self.assertIsNone(
UserAttribute.get_user_attribute(user, REGISTRATION_UTM_CREATED_AT)
)
@ddt.ddt @ddt.ddt
class TestCreateAccountValidation(TestCase): class TestCreateAccountValidation(TestCase):
......
...@@ -1903,25 +1903,25 @@ def record_utm_registration_attribution(request, user): ...@@ -1903,25 +1903,25 @@ def record_utm_registration_attribution(request, user):
utm_cookie = request.COOKIES.get(utm_cookie_name) utm_cookie = request.COOKIES.get(utm_cookie_name)
if user and utm_cookie: if user and utm_cookie:
utm = json.loads(utm_cookie) utm = json.loads(utm_cookie)
for utm_parameter in REGISTRATION_UTM_PARAMETERS: for utm_parameter_name in REGISTRATION_UTM_PARAMETERS:
UserAttribute.set_user_attribute( utm_parameter = utm.get(utm_parameter_name)
user, if utm_parameter:
REGISTRATION_UTM_PARAMETERS.get(utm_parameter), UserAttribute.set_user_attribute(
utm.get(utm_parameter) user,
) REGISTRATION_UTM_PARAMETERS.get(utm_parameter_name),
utm_parameter
)
created_at_unixtime = utm.get('created_at') created_at_unixtime = utm.get('created_at')
if created_at_unixtime: if created_at_unixtime:
# We divide by 1000 here because the javascript timestamp generated is in milliseconds not seconds. # We divide by 1000 here because the javascript timestamp generated is in milliseconds not seconds.
# PYTHON: time.time() => 1475590280.823698 # PYTHON: time.time() => 1475590280.823698
# JS: new Date().getTime() => 1475590280823 # JS: new Date().getTime() => 1475590280823
created_at_datetime = datetime.datetime.fromtimestamp(int(created_at_unixtime) / float(1000), tz=UTC) created_at_datetime = datetime.datetime.fromtimestamp(int(created_at_unixtime) / float(1000), tz=UTC)
else: UserAttribute.set_user_attribute(
created_at_datetime = None user,
UserAttribute.set_user_attribute( REGISTRATION_UTM_CREATED_AT,
user, created_at_datetime
REGISTRATION_UTM_CREATED_AT, )
created_at_datetime
)
def record_registration_attributions(request, user): def record_registration_attributions(request, user):
......
...@@ -4,10 +4,11 @@ import logging ...@@ -4,10 +4,11 @@ import logging
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import migrations from django.db import migrations
from django.http import Http404
from ccx_keys.locator import CCXLocator from ccx_keys.locator import CCXLocator
from courseware.courses import get_course_by_id
from instructor.access import allow_access, revoke_access from instructor.access import allow_access, revoke_access
from lms.djangoapps.ccx.utils import ccx_course
log = logging.getLogger("edx.ccx") log = logging.getLogger("edx.ccx")
...@@ -31,7 +32,11 @@ def change_existing_ccx_coaches_to_staff(apps, schema_editor): ...@@ -31,7 +32,11 @@ def change_existing_ccx_coaches_to_staff(apps, schema_editor):
list_ccx = CustomCourseForEdX.objects.using(db_alias).all() list_ccx = CustomCourseForEdX.objects.using(db_alias).all()
for ccx in list_ccx: for ccx in list_ccx:
ccx_locator = CCXLocator.from_course_locator(ccx.course_id, unicode(ccx.id)) ccx_locator = CCXLocator.from_course_locator(ccx.course_id, unicode(ccx.id))
with ccx_course(ccx_locator) as course: try:
course = get_course_by_id(ccx_locator)
except Http404:
log.error('Could not migrate access for CCX course: %s', unicode(ccx_locator))
else:
coach = User.objects.get(id=ccx.coach.id) coach = User.objects.get(id=ccx.coach.id)
allow_access(course, coach, 'staff', send_email=False) allow_access(course, coach, 'staff', send_email=False)
revoke_access(course, coach, 'ccx_coach', send_email=False) revoke_access(course, coach, 'ccx_coach', send_email=False)
...@@ -57,7 +62,11 @@ def revert_ccx_staff_to_coaches(apps, schema_editor): ...@@ -57,7 +62,11 @@ def revert_ccx_staff_to_coaches(apps, schema_editor):
list_ccx = CustomCourseForEdX.objects.using(db_alias).all() list_ccx = CustomCourseForEdX.objects.using(db_alias).all()
for ccx in list_ccx: for ccx in list_ccx:
ccx_locator = CCXLocator.from_course_locator(ccx.course_id, unicode(ccx.id)) ccx_locator = CCXLocator.from_course_locator(ccx.course_id, unicode(ccx.id))
with ccx_course(ccx_locator) as course: try:
course = get_course_by_id(ccx_locator)
except Http404:
log.error('Could not migrate access for CCX course: %s', unicode(ccx_locator))
else:
coach = User.objects.get(id=ccx.coach.id) coach = User.objects.get(id=ccx.coach.id)
allow_access(course, coach, 'ccx_coach', send_email=False) allow_access(course, coach, 'ccx_coach', send_email=False)
revoke_access(course, coach, 'staff', send_email=False) revoke_access(course, coach, 'staff', send_email=False)
......
...@@ -70,7 +70,7 @@ def get_course_by_id(course_key, depth=0): ...@@ -70,7 +70,7 @@ def get_course_by_id(course_key, depth=0):
if course: if course:
return course return course
else: else:
raise Http404("Course not found.") raise Http404("Course not found: {}.".format(unicode(course_key)))
class UserNotEnrolled(Http404): class UserNotEnrolled(Http404):
......
...@@ -3,6 +3,7 @@ This module contains tasks for asynchronous execution of grade updates. ...@@ -3,6 +3,7 @@ This module contains tasks for asynchronous execution of grade updates.
""" """
from celery import task from celery import task
from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from lms.djangoapps.course_blocks.api import get_course_blocks from lms.djangoapps.course_blocks.api import get_course_blocks
...@@ -16,7 +17,7 @@ from .transformer import GradesTransformer ...@@ -16,7 +17,7 @@ from .transformer import GradesTransformer
from .new.subsection_grade import SubsectionGradeFactory from .new.subsection_grade import SubsectionGradeFactory
@task() @task(routing_key=settings.RECALCULATE_GRADES_ROUTING_KEY)
def recalculate_subsection_grade(user_id, course_id, usage_id): def recalculate_subsection_grade(user_id, course_id, usage_id):
""" """
Updates a saved subsection grade. Updates a saved subsection grade.
......
...@@ -259,7 +259,10 @@ BULK_EMAIL_DEFAULT_RETRY_DELAY = ENV_TOKENS.get('BULK_EMAIL_DEFAULT_RETRY_DELAY' ...@@ -259,7 +259,10 @@ BULK_EMAIL_DEFAULT_RETRY_DELAY = ENV_TOKENS.get('BULK_EMAIL_DEFAULT_RETRY_DELAY'
BULK_EMAIL_MAX_RETRIES = ENV_TOKENS.get('BULK_EMAIL_MAX_RETRIES', BULK_EMAIL_MAX_RETRIES) BULK_EMAIL_MAX_RETRIES = ENV_TOKENS.get('BULK_EMAIL_MAX_RETRIES', BULK_EMAIL_MAX_RETRIES)
BULK_EMAIL_INFINITE_RETRY_CAP = ENV_TOKENS.get('BULK_EMAIL_INFINITE_RETRY_CAP', BULK_EMAIL_INFINITE_RETRY_CAP) BULK_EMAIL_INFINITE_RETRY_CAP = ENV_TOKENS.get('BULK_EMAIL_INFINITE_RETRY_CAP', BULK_EMAIL_INFINITE_RETRY_CAP)
BULK_EMAIL_LOG_SENT_EMAILS = ENV_TOKENS.get('BULK_EMAIL_LOG_SENT_EMAILS', BULK_EMAIL_LOG_SENT_EMAILS) BULK_EMAIL_LOG_SENT_EMAILS = ENV_TOKENS.get('BULK_EMAIL_LOG_SENT_EMAILS', BULK_EMAIL_LOG_SENT_EMAILS)
BULK_EMAIL_RETRY_DELAY_BETWEEN_SENDS = ENV_TOKENS.get('BULK_EMAIL_RETRY_DELAY_BETWEEN_SENDS', BULK_EMAIL_RETRY_DELAY_BETWEEN_SENDS) BULK_EMAIL_RETRY_DELAY_BETWEEN_SENDS = ENV_TOKENS.get(
'BULK_EMAIL_RETRY_DELAY_BETWEEN_SENDS',
BULK_EMAIL_RETRY_DELAY_BETWEEN_SENDS
)
# We want Bulk Email running on the high-priority queue, so we define the # We want Bulk Email running on the high-priority queue, so we define the
# routing key that points to it. At the moment, the name is the same. # routing key that points to it. At the moment, the name is the same.
# We have to reset the value here, since we have changed the value of the queue name. # We have to reset the value here, since we have changed the value of the queue name.
...@@ -269,6 +272,9 @@ BULK_EMAIL_ROUTING_KEY = HIGH_PRIORITY_QUEUE ...@@ -269,6 +272,9 @@ BULK_EMAIL_ROUTING_KEY = HIGH_PRIORITY_QUEUE
# we have to reset the value here. # we have to reset the value here.
BULK_EMAIL_ROUTING_KEY_SMALL_JOBS = LOW_PRIORITY_QUEUE BULK_EMAIL_ROUTING_KEY_SMALL_JOBS = LOW_PRIORITY_QUEUE
# Queue to use for updating persistent grades
RECALCULATE_GRADES_ROUTING_KEY = ENV_TOKENS.get('RECALCULATE_GRADES_ROUTING_KEY', LOW_PRIORITY_QUEUE)
# following setting is for backward compatibility # following setting is for backward compatibility
if ENV_TOKENS.get('COMPREHENSIVE_THEME_DIR', None): if ENV_TOKENS.get('COMPREHENSIVE_THEME_DIR', None):
COMPREHENSIVE_THEME_DIR = ENV_TOKENS.get('COMPREHENSIVE_THEME_DIR') COMPREHENSIVE_THEME_DIR = ENV_TOKENS.get('COMPREHENSIVE_THEME_DIR')
......
...@@ -1853,6 +1853,11 @@ BULK_EMAIL_LOG_SENT_EMAILS = False ...@@ -1853,6 +1853,11 @@ BULK_EMAIL_LOG_SENT_EMAILS = False
# parallel, and what the SES rate is. # parallel, and what the SES rate is.
BULK_EMAIL_RETRY_DELAY_BETWEEN_SENDS = 0.02 BULK_EMAIL_RETRY_DELAY_BETWEEN_SENDS = 0.02
############################# Persistent Grades ####################################
# Queue to use for updating persistent grades
RECALCULATE_GRADES_ROUTING_KEY = LOW_PRIORITY_QUEUE
############################# Email Opt In #################################### ############################# Email Opt In ####################################
# Minimum age for organization-wide email opt in # Minimum age for organization-wide email opt in
......
...@@ -16,4 +16,4 @@ ...@@ -16,4 +16,4 @@
# incorporated into the main project. # incorporated into the main project.
# https://github.com/onelogin/python-saml/pull/159 # https://github.com/onelogin/python-saml/pull/159
# https://github.com/onelogin/python-saml/pull/164 # https://github.com/onelogin/python-saml/pull/164
git+https://github.com/open-craft/python-saml.git@7f7dc389abeba44c1dac154d6aafcbe4187ae221#egg=python-saml==2.1.9 git+https://github.com/open-craft/python-saml.git@d50f8d25419c2e981605e5c64e8528a125bcf303#egg=python-saml==2.2.0
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment