Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 33 additions & 10 deletions lms/djangoapps/bulk_enroll/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from rest_framework.test import APIRequestFactory, APITestCase, force_authenticate

from common.djangoapps.student.models import ( # lint-amnesty, pylint: disable=line-too-long
ENROLLED_TO_ENROLLED,
ENROLLED_TO_UNENROLLED,
UNENROLLED_TO_ENROLLED,
CourseEnrollment,
Expand Down Expand Up @@ -150,6 +151,9 @@ def test_invalid_email(self):
{
"identifier": 'percivaloctavius@',
"invalidIdentifier": True,
"success": False,
"error_type": "invalid_identifier",
"error_message": "Invalid email address",
}
]
}
Expand Down Expand Up @@ -182,6 +186,9 @@ def test_invalid_username(self):
{
"identifier": 'percivaloctavius',
"invalidIdentifier": True,
"success": False,
"error_type": "invalid_identifier",
"error_message": "Invalid email address",
}
]
}
Expand Down Expand Up @@ -224,7 +231,9 @@ def test_enroll_with_username(self):
"auto_enroll": False,
"user": True,
"allowed": False,
}
},
"success": True,
"state_transition": UNENROLLED_TO_ENROLLED,
}
]
}
Expand Down Expand Up @@ -274,7 +283,9 @@ def test_enroll_with_email(self, use_json):
"auto_enroll": False,
"user": True,
"allowed": False,
}
},
"success": True,
"state_transition": UNENROLLED_TO_ENROLLED,
}
]
}
Expand Down Expand Up @@ -328,7 +339,9 @@ def test_unenroll(self, use_json):
"auto_enroll": False,
"user": True,
"allowed": False,
}
},
"success": True,
"state_transition": ENROLLED_TO_UNENROLLED,
}
]
}
Expand Down Expand Up @@ -432,7 +445,9 @@ def test_add_to_valid_cohort(self):
"user": True,
"allowed": False,
"cohort": 'cohort1',
}
},
"success": True,
"state_transition": UNENROLLED_TO_ENROLLED,
}
]
}
Expand All @@ -446,7 +461,7 @@ def test_add_to_valid_cohort(self):

assert res_json == expected

def test_readd_to_different_cohort(self):
def test_read_to_different_cohort(self):
config_course_cohorts(self.course, is_cohorted=True, manual_cohorts=["cohort1", "cohort2"])
response = self.request_bulk_enroll({
'identifiers': self.notenrolled_student.username,
Expand Down Expand Up @@ -483,7 +498,9 @@ def test_readd_to_different_cohort(self):
"user": True,
"allowed": False,
"cohort": 'cohort1',
}
},
"success": True,
"state_transition": UNENROLLED_TO_ENROLLED,
}
]
}
Expand Down Expand Up @@ -531,7 +548,9 @@ def test_readd_to_different_cohort(self):
"user": True,
"allowed": False,
"cohort": 'cohort2',
}
},
"success": True,
"state_transition": ENROLLED_TO_ENROLLED,
}
]
}
Expand All @@ -541,7 +560,7 @@ def test_readd_to_different_cohort(self):
assert get_cohort_id(self.notenrolled_student, CourseKey.from_string(self.course_key)) is not None
assert res2_json == expected2

def test_readd_to_same_cohort(self):
def test_read_to_same_cohort(self):
config_course_cohorts(self.course, is_cohorted=True, manual_cohorts=["cohort1", "cohort2"])
response = self.request_bulk_enroll({
'identifiers': self.notenrolled_student.username,
Expand Down Expand Up @@ -578,7 +597,9 @@ def test_readd_to_same_cohort(self):
"user": True,
"allowed": False,
"cohort": 'cohort1',
}
},
"success": True,
"state_transition": UNENROLLED_TO_ENROLLED,
}
]
}
Expand Down Expand Up @@ -627,7 +648,9 @@ def test_readd_to_same_cohort(self):
"user": True,
"allowed": False,
"cohort": 'cohort1',
}
},
"success": True,
"state_transition": ENROLLED_TO_ENROLLED,
}
]
}
Expand Down
2 changes: 1 addition & 1 deletion lms/djangoapps/bulk_enroll/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def post(self, request): # lint-amnesty, pylint: disable=missing-function-docst
# Internal request to DRF view
view = StudentsUpdateEnrollmentView()
response_content = view._process_student_enrollment( # pylint: disable=protected-access
user=request.user,
request=request,
course_id=course_id,
data=request.data,
secure=request.is_secure()
Expand Down
36 changes: 35 additions & 1 deletion lms/djangoapps/instructor/enrollment.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
from datetime import datetime

import pytz
from crum import get_current_request
from django.conf import settings
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.contrib.sites.models import Site
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.translation import override as override_language
Expand Down Expand Up @@ -50,7 +52,9 @@
)
from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.theming.helpers import get_current_site
from openedx.core.djangoapps.user_api.models import UserPreference
from openedx.core.lib.celery.task_utils import emulate_http_request
from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore.exceptions import ItemNotFoundError # lint-amnesty, pylint: disable=wrong-import-order

Expand Down Expand Up @@ -591,7 +595,37 @@ def send_mail_to_student(student, param_dict, language=None):
language=language,
user_context=param_dict,
)
ace.send(message)

current_request = get_current_request()

if current_request is None:
# We're in a Celery task context, need to emulate HTTP request
site = get_current_site()
if not site:
try:
site = Site.objects.get(id=settings.SITE_ID)
except Site.DoesNotExist:
try:
site = Site.objects.first()
except Exception: # pylint: disable=broad-except
site = None

# Get the recipient user for tracking purposes
user = None
if lms_user_id and lms_user_id > 0:
try:
user = User.objects.get(id=lms_user_id)
except User.DoesNotExist:
pass

# Use emulate_http_request to provide the necessary context for template tags
# that require a request object, such as google_analytics_tracking_pixel
with emulate_http_request(site=site, user=user):
ace.send(message)
else:
# We're in a web context, just send the message directly
# The current request already provides the necessary context
ace.send(message)


def render_message_to_string(subject_template, message_template, param_dict, language=None):
Expand Down
12 changes: 12 additions & 0 deletions lms/djangoapps/instructor/message_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,15 @@ class RemoveBetaTester(BaseMessageType):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.options['transactional'] = True


class BatchEnrollment(BaseMessageType):
"""
A message for instructors when they finish the batch enrollment async process.
"""

APP_LABEL = "instructor"

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.options["transactional"] = True
Loading
Loading