add doc strings; fix pep8 warning address some minor issues brought up by the code review process
308 lines
10 KiB
Python
308 lines
10 KiB
Python
"""
|
|
CCX Enrollment operations for use by Coach APIs.
|
|
|
|
Does not include any access control, be sure to check access before calling.
|
|
"""
|
|
import logging
|
|
from courseware.courses import get_course_about_section # pylint: disable=import-error
|
|
from courseware.courses import get_course_by_id # pylint: disable=import-error
|
|
from django.contrib.auth.models import User
|
|
from django.conf import settings
|
|
from django.core.urlresolvers import reverse
|
|
from django.core.mail import send_mail
|
|
from edxmako.shortcuts import render_to_string # pylint: disable=import-error
|
|
from microsite_configuration import microsite # pylint: disable=import-error
|
|
from xmodule.modulestore.django import modulestore
|
|
from xmodule.error_module import ErrorDescriptor
|
|
|
|
from .models import (
|
|
CcxMembership,
|
|
CcxFutureMembership,
|
|
)
|
|
from .overrides import get_current_ccx
|
|
|
|
|
|
log = logging.getLogger("edx.ccx")
|
|
|
|
|
|
class EmailEnrollmentState(object):
|
|
""" Store the complete enrollment state of an email in a class """
|
|
def __init__(self, ccx, email):
|
|
exists_user = User.objects.filter(email=email).exists()
|
|
if exists_user:
|
|
user = User.objects.get(email=email)
|
|
ccx_member = CcxMembership.objects.filter(ccx=ccx, student=user)
|
|
in_ccx = ccx_member.exists()
|
|
full_name = user.profile.name
|
|
else:
|
|
user = None
|
|
in_ccx = False
|
|
full_name = None
|
|
self.user = exists_user
|
|
self.member = user
|
|
self.full_name = full_name
|
|
self.in_ccx = in_ccx
|
|
|
|
def __repr__(self):
|
|
return "{}(user={}, member={}, in_ccx={})".format(
|
|
self.__class__.__name__,
|
|
self.user,
|
|
self.member,
|
|
self.in_ccx,
|
|
)
|
|
|
|
def to_dict(self):
|
|
""" return dict with membership and ccx info """
|
|
return {
|
|
'user': self.user,
|
|
'member': self.member,
|
|
'in_ccx': self.in_ccx,
|
|
}
|
|
|
|
|
|
def enroll_email(ccx, student_email, auto_enroll=False, email_students=False, email_params=None):
|
|
"""
|
|
Send email to newly enrolled student
|
|
"""
|
|
if email_params is None:
|
|
email_params = get_email_params(ccx, True)
|
|
previous_state = EmailEnrollmentState(ccx, student_email)
|
|
|
|
if previous_state.user:
|
|
user = User.objects.get(email=student_email)
|
|
if not previous_state.in_ccx:
|
|
membership = CcxMembership(
|
|
ccx=ccx, student=user, active=True
|
|
)
|
|
membership.save()
|
|
elif auto_enroll:
|
|
# activate existing memberships
|
|
membership = CcxMembership.objects.get(student=user, ccx=ccx)
|
|
membership.active = True
|
|
membership.save()
|
|
if email_students:
|
|
email_params['message'] = 'enrolled_enroll'
|
|
email_params['email_address'] = student_email
|
|
email_params['full_name'] = previous_state.full_name
|
|
send_mail_to_student(student_email, email_params)
|
|
else:
|
|
membership = CcxFutureMembership(
|
|
ccx=ccx, auto_enroll=auto_enroll, email=student_email
|
|
)
|
|
membership.save()
|
|
if email_students:
|
|
email_params['message'] = 'allowed_enroll'
|
|
email_params['email_address'] = student_email
|
|
send_mail_to_student(student_email, email_params)
|
|
|
|
after_state = EmailEnrollmentState(ccx, student_email)
|
|
|
|
return previous_state, after_state
|
|
|
|
|
|
def unenroll_email(ccx, student_email, email_students=False, email_params=None):
|
|
"""
|
|
send email to unenrolled students
|
|
"""
|
|
if email_params is None:
|
|
email_params = get_email_params(ccx, True)
|
|
previous_state = EmailEnrollmentState(ccx, student_email)
|
|
|
|
if previous_state.in_ccx:
|
|
CcxMembership.objects.get(
|
|
ccx=ccx, student=previous_state.member
|
|
).delete()
|
|
if email_students:
|
|
email_params['message'] = 'enrolled_unenroll'
|
|
email_params['email_address'] = student_email
|
|
email_params['full_name'] = previous_state.full_name
|
|
send_mail_to_student(student_email, email_params)
|
|
else:
|
|
if CcxFutureMembership.objects.filter(
|
|
ccx=ccx, email=student_email).exists():
|
|
CcxFutureMembership.objects.get(
|
|
ccx=ccx, email=student_email
|
|
).delete()
|
|
if email_students:
|
|
email_params['message'] = 'allowed_unenroll'
|
|
email_params['email_address'] = student_email
|
|
send_mail_to_student(student_email, email_params)
|
|
|
|
after_state = EmailEnrollmentState(ccx, student_email)
|
|
|
|
return previous_state, after_state
|
|
|
|
|
|
def get_email_params(ccx, auto_enroll, secure=True):
|
|
"""
|
|
get parameters for enrollment emails
|
|
"""
|
|
protocol = 'https' if secure else 'http'
|
|
course_id = ccx.course_id
|
|
|
|
stripped_site_name = microsite.get_value(
|
|
'SITE_NAME',
|
|
settings.SITE_NAME
|
|
)
|
|
registration_url = u'{proto}://{site}{path}'.format(
|
|
proto=protocol,
|
|
site=stripped_site_name,
|
|
path=reverse('register_user')
|
|
)
|
|
course_url = u'{proto}://{site}{path}'.format(
|
|
proto=protocol,
|
|
site=stripped_site_name,
|
|
path=reverse(
|
|
'course_root',
|
|
kwargs={'course_id': course_id.to_deprecated_string()}
|
|
)
|
|
)
|
|
|
|
course_about_url = None
|
|
if not settings.FEATURES.get('ENABLE_MKTG_SITE', False):
|
|
course_about_url = u'{proto}://{site}{path}'.format(
|
|
proto=protocol,
|
|
site=stripped_site_name,
|
|
path=reverse(
|
|
'about_course',
|
|
kwargs={'course_id': course_id.to_deprecated_string()}
|
|
)
|
|
)
|
|
|
|
email_params = {
|
|
'site_name': stripped_site_name,
|
|
'registration_url': registration_url,
|
|
'course': ccx,
|
|
'auto_enroll': auto_enroll,
|
|
'course_url': course_url,
|
|
'course_about_url': course_about_url,
|
|
}
|
|
return email_params
|
|
|
|
|
|
def send_mail_to_student(student, param_dict):
|
|
"""
|
|
Check parameters, set text template and send email to student
|
|
"""
|
|
if 'course' in param_dict:
|
|
param_dict['course_name'] = param_dict['course'].display_name
|
|
|
|
param_dict['site_name'] = microsite.get_value(
|
|
'SITE_NAME',
|
|
param_dict['site_name']
|
|
)
|
|
|
|
subject = None
|
|
message = None
|
|
|
|
message_type = param_dict['message']
|
|
|
|
email_template_dict = {
|
|
'allowed_enroll': (
|
|
'ccx/enroll_email_allowedsubject.txt',
|
|
'ccx/enroll_email_allowedmessage.txt'
|
|
),
|
|
'enrolled_enroll': (
|
|
'ccx/enroll_email_enrolledsubject.txt',
|
|
'ccx/enroll_email_enrolledmessage.txt'
|
|
),
|
|
'allowed_unenroll': (
|
|
'ccx/unenroll_email_subject.txt',
|
|
'ccx/unenroll_email_allowedmessage.txt'
|
|
),
|
|
'enrolled_unenroll': (
|
|
'ccx/unenroll_email_subject.txt',
|
|
'ccx/unenroll_email_enrolledmessage.txt'
|
|
),
|
|
}
|
|
|
|
subject_template, message_template = email_template_dict.get(
|
|
message_type, (None, None)
|
|
)
|
|
if subject_template is not None and message_template is not None:
|
|
subject = render_to_string(subject_template, param_dict)
|
|
message = render_to_string(message_template, param_dict)
|
|
|
|
if subject and message:
|
|
message = message.strip()
|
|
|
|
subject = ''.join(subject.splitlines())
|
|
from_address = microsite.get_value(
|
|
'email_from_address',
|
|
settings.DEFAULT_FROM_EMAIL
|
|
)
|
|
|
|
send_mail(
|
|
subject,
|
|
message,
|
|
from_address,
|
|
[student],
|
|
fail_silently=False
|
|
)
|
|
|
|
|
|
def get_all_ccx_for_user(user):
|
|
"""return all CCXS to which the user is registered
|
|
|
|
Returns a list of dicts: {
|
|
ccx_name: <formatted title of CCX course>
|
|
ccx_url: <url to view this CCX>
|
|
ccx_active: True if this ccx is currently the 'active' one
|
|
mooc_name: <formatted title of the MOOC course for this CCX>
|
|
mooc_url: <url to view this MOOC>
|
|
}
|
|
"""
|
|
if user.is_anonymous():
|
|
return []
|
|
current_active_ccx = get_current_ccx()
|
|
memberships = []
|
|
for membership in CcxMembership.memberships_for_user(user):
|
|
course = get_course_by_id(membership.ccx.course_id)
|
|
ccx = membership.ccx
|
|
ccx_title = ccx.display_name
|
|
mooc_title = get_course_about_section(course, 'title')
|
|
url = reverse(
|
|
'switch_active_ccx',
|
|
args=[course.id.to_deprecated_string(), membership.ccx.id]
|
|
)
|
|
mooc_url = reverse(
|
|
'switch_active_ccx',
|
|
args=[course.id.to_deprecated_string(), ]
|
|
)
|
|
memberships.append({
|
|
'ccx_name': ccx_title,
|
|
'ccx_url': url,
|
|
'active': membership.ccx == current_active_ccx,
|
|
'mooc_name': mooc_title,
|
|
'mooc_url': mooc_url,
|
|
})
|
|
return memberships
|
|
|
|
|
|
def get_ccx_membership_triplets(user, course_org_filter, org_filter_out_set):
|
|
"""
|
|
Get the relevant set of (CustomCourseForEdX, CcxMembership, Course)
|
|
triplets to be displayed on a student's dashboard.
|
|
"""
|
|
# only active memberships for now
|
|
for membership in CcxMembership.memberships_for_user(user):
|
|
ccx = membership.ccx
|
|
store = modulestore()
|
|
with store.bulk_operations(ccx.course_id):
|
|
course = store.get_course(ccx.course_id)
|
|
if course and not isinstance(course, ErrorDescriptor):
|
|
# if we are in a Microsite, then filter out anything that is not
|
|
# attributed (by ORG) to that Microsite
|
|
if course_org_filter and course_org_filter != course.location.org:
|
|
continue
|
|
# Conversely, if we are not in a Microsite, then let's filter out any enrollments
|
|
# with courses attributed (by ORG) to Microsites
|
|
elif course.location.org in org_filter_out_set:
|
|
continue
|
|
|
|
yield (ccx, membership, course)
|
|
else:
|
|
log.error("User {0} enrolled in {2} course {1}".format( # pylint: disable=logging-format-interpolation
|
|
user.username, ccx.course_id, "broken" if course else "non-existent"
|
|
))
|