Commit 7f691e4a by cewing

MIT: CCX. Rename POC to CCX

Final official name is Custom Courses for EdX (CCX), rename all code to remove previous name.

Rename the FEATURE constant used to identify this feature

Rename the middleware for the CCX change

rename the constant used for storing the current poc id on the session.

rename the _PocContext threading local

rename the override provider in all places where it is referenced

`PersonalOnlineCoursesOverrideProvider` -> `CustomCoursesForEdxOverrideProvider`

generally rename symbols from overrides.py to replace `poc` with `ccx` where possible without changing model names or attributes

rename more symbols from poc to ccx
rename util functions from app utils module

general symbol renaming poc -> ccx in views.py and related url changes

Rename the coach role wherever it is used.

reword poc_coach to ccx_coach

UI rename

replace POC with CCX globally

template context variable renamed

rename poc_ to ccx_ in urls and all related locations (views, js, scss etc)

remove pocs migrations

Final massive renaming, including models.  Re-built migration.

cleaning up a few tailing references

Fix reference typo in schedule template JS

undo modifications made on the fly in test setup to ensure that our tests are properly isolated from the rest of the system tests.

Fixes jazkarta/edx-platform#38

Clean up some leftover strings and comments

fixing more strings and comments in python files

fix a naming error in the schedule tab that was causing problems in deleting courseware items.

Fixes jazkarta/edx-platform#36

updating tests and utility code to match changes in infrastructure from latest rebase
parent fbaab967
......@@ -267,12 +267,12 @@ class LibraryUserRole(CourseRole):
super(LibraryUserRole, self).__init__(self.ROLE, *args, **kwargs)
class CoursePocCoachRole(CourseRole):
"""A POC Coach"""
ROLE = 'poc_coach'
class CourseCcxCoachRole(CourseRole):
"""A CCX Coach"""
ROLE = 'ccx_coach'
def __init__(self, *args, **kwargs):
super(CoursePocCoachRole, self).__init__(self.ROLE, *args, **kwargs)
super(CourseCcxCoachRole, self).__init__(self.ROLE, *args, **kwargs)
class OrgStaffRole(OrgRole):
......
......@@ -660,16 +660,16 @@ def dashboard(request):
if course.pre_requisite_courses)
courses_requirements_not_met = get_pre_requisite_courses_not_completed(user, courses_having_prerequisites)
poc_membership_triplets = []
if settings.FEATURES.get('PERSONAL_ONLINE_COURSES', False):
from pocs import ACTIVE_POC_KEY
from pocs.utils import get_poc_membership_triplets
poc_membership_triplets = get_poc_membership_triplets(
ccx_membership_triplets = []
if settings.FEATURES.get('CUSTOM_COURSES_EDX', False):
from ccx import ACTIVE_CCX_KEY
from ccx.utils import get_ccx_membership_triplets
ccx_membership_triplets = get_ccx_membership_triplets(
user, course_org_filter, org_filter_out_set
)
# should we deselect any active POC at this time so that we don't have
# should we deselect any active CCX at this time so that we don't have
# to change the URL for viewing a course? I think so.
request.session[ACTIVE_POC_KEY] = None
request.session[ACTIVE_CCX_KEY] = None
context = {
'enrollment_message': enrollment_message,
......@@ -702,7 +702,7 @@ def dashboard(request):
'provider_states': [],
'order_history_list': order_history_list,
'courses_requirements_not_met': courses_requirements_not_met,
'poc_membership_triplets': poc_membership_triplets,
'ccx_membership_triplets': ccx_membership_triplets,
}
if third_party_auth.is_enabled():
......@@ -1818,15 +1818,15 @@ def activate_account(request, key):
if cea.auto_enroll:
CourseEnrollment.enroll(student[0], cea.course_id)
# enroll student in any pending POCs he/she may have if auto_enroll flag is set
if settings.FEATURES.get('PERSONAL_ONLINE_COURSES'):
from pocs.models import PocMembership, PocFutureMembership
pfms = PocFutureMembership.objects.filter(
# enroll student in any pending CCXs he/she may have if auto_enroll flag is set
if settings.FEATURES.get('CUSTOM_COURSES_EDX'):
from ccx.models import CcxMembership, CcxFutureMembership
pfms = CcxFutureMembership.objects.filter(
email=student[0].email
)
for pfm in pfms:
if pfm.auto_enroll:
PocMembership.auto_enroll(student[0], pfm)
CcxMembership.auto_enroll(student[0], pfm)
resp = render_to_response(
"registration/activation_complete.html",
......
......@@ -193,7 +193,7 @@ class CourseTab(object):
'edxnotes': EdxNotesTab,
'syllabus': SyllabusTab,
'instructor': InstructorTab, # not persisted
'poc_coach': PocCoachTab, # not persisted
'ccx_coach': CcxCoachTab, # not persisted
}
tab_type = tab_dict.get('type')
......@@ -376,9 +376,9 @@ class DiscussionTab(EnrolledOrStaffTab):
)
def can_display(self, course, settings, is_user_authenticated, is_user_staff, is_user_enrolled):
if settings.FEATURES.get('PERSONAL_ONLINE_COURSES', False):
from pocs.overrides import get_current_poc
if get_current_poc():
if settings.FEATURES.get('CUSTOM_COURSES_EDX', False):
from ccx.overrides import get_current_ccx
if get_current_ccx():
return False
super_can_display = super(DiscussionTab, self).can_display(
course, settings, is_user_authenticated, is_user_staff, is_user_enrolled
......@@ -738,26 +738,26 @@ class InstructorTab(StaffTab):
)
class PocCoachTab(CourseTab):
class CcxCoachTab(CourseTab):
"""
A tab for the personal online course coaches.
A tab for the custom course coaches.
"""
type = 'poc_coach'
type = 'ccx_coach'
def __init__(self, tab_dict=None): # pylint: disable=unused-argument
super(PocCoachTab, self).__init__(
name=_('POC Coach'),
super(CcxCoachTab, self).__init__(
name=_('CCX Coach'),
tab_id=self.type,
link_func=link_reverse_func('poc_coach_dashboard'),
link_func=link_reverse_func('ccx_coach_dashboard'),
)
def can_display(self, course, settings, *args, **kw):
# TODO Check that user actually has 'poc_coach' role on course
# TODO Check that user actually has 'ccx_coach' role on course
# this is difficult to do because the user isn't passed in.
# We need either a hack or an architectural realignment.
return (
settings.FEATURES.get('PERSONAL_ONLINE_COURSES', False) and
super(PocCoachTab, self).can_display(course, settings, *args, **kw))
settings.FEATURES.get('CUSTOM_COURSES_EDX', False) and
super(CcxCoachTab, self).can_display(course, settings, *args, **kw))
class CourseTabList(List):
......@@ -860,9 +860,9 @@ class CourseTabList(List):
instructor_tab = InstructorTab()
if instructor_tab.can_display(course, settings, is_user_authenticated, is_user_staff, is_user_enrolled):
yield instructor_tab
poc_coach_tab = PocCoachTab()
if poc_coach_tab.can_display(course, settings, is_user_authenticated, is_user_staff, is_user_enrolled):
yield poc_coach_tab
ccx_coach_tab = CcxCoachTab()
if ccx_coach_tab.can_display(course, settings, is_user_authenticated, is_user_staff, is_user_enrolled):
yield ccx_coach_tab
@staticmethod
def iterate_displayable_cms(
......
ACTIVE_CCX_KEY = '_ccx_id'
......@@ -8,49 +8,62 @@ from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'PersonalOnlineCourse'
db.create_table('pocs_personalonlinecourse', (
# Adding model 'CustomCourseForEdX'
db.create_table('ccx_customcourseforedx', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('course_id', self.gf('xmodule_django.models.CourseKeyField')(max_length=255, db_index=True)),
('display_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
('coach', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
))
db.send_create_signal('pocs', ['PersonalOnlineCourse'])
db.send_create_signal('ccx', ['CustomCourseForEdX'])
# Adding model 'PocMembership'
db.create_table('pocs_pocmembership', (
# Adding model 'CcxMembership'
db.create_table('ccx_ccxmembership', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('poc', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['pocs.PersonalOnlineCourse'])),
('ccx', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['ccx.CustomCourseForEdX'])),
('student', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
('active', self.gf('django.db.models.fields.BooleanField')(default=False)),
))
db.send_create_signal('pocs', ['PocMembership'])
db.send_create_signal('ccx', ['CcxMembership'])
# Adding model 'PocFieldOverride'
db.create_table('pocs_pocfieldoverride', (
# Adding model 'CcxFutureMembership'
db.create_table('ccx_ccxfuturemembership', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('poc', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['pocs.PersonalOnlineCourse'])),
('ccx', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['ccx.CustomCourseForEdX'])),
('email', self.gf('django.db.models.fields.CharField')(max_length=255)),
('auto_enroll', self.gf('django.db.models.fields.BooleanField')(default=False)),
))
db.send_create_signal('ccx', ['CcxFutureMembership'])
# Adding model 'CcxFieldOverride'
db.create_table('ccx_ccxfieldoverride', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('ccx', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['ccx.CustomCourseForEdX'])),
('location', self.gf('xmodule_django.models.LocationKeyField')(max_length=255, db_index=True)),
('field', self.gf('django.db.models.fields.CharField')(max_length=255)),
('value', self.gf('django.db.models.fields.TextField')(default='null')),
))
db.send_create_signal('pocs', ['PocFieldOverride'])
db.send_create_signal('ccx', ['CcxFieldOverride'])
# Adding unique constraint on 'PocFieldOverride', fields ['poc', 'location', 'field']
db.create_unique('pocs_pocfieldoverride', ['poc_id', 'location', 'field'])
# Adding unique constraint on 'CcxFieldOverride', fields ['ccx', 'location', 'field']
db.create_unique('ccx_ccxfieldoverride', ['ccx_id', 'location', 'field'])
def backwards(self, orm):
# Removing unique constraint on 'PocFieldOverride', fields ['poc', 'location', 'field']
db.delete_unique('pocs_pocfieldoverride', ['poc_id', 'location', 'field'])
# Removing unique constraint on 'CcxFieldOverride', fields ['ccx', 'location', 'field']
db.delete_unique('ccx_ccxfieldoverride', ['ccx_id', 'location', 'field'])
# Deleting model 'CustomCourseForEdX'
db.delete_table('ccx_customcourseforedx')
# Deleting model 'PersonalOnlineCourse'
db.delete_table('pocs_personalonlinecourse')
# Deleting model 'CcxMembership'
db.delete_table('ccx_ccxmembership')
# Deleting model 'PocMembership'
db.delete_table('pocs_pocmembership')
# Deleting model 'CcxFutureMembership'
db.delete_table('ccx_ccxfuturemembership')
# Deleting model 'PocFieldOverride'
db.delete_table('pocs_pocfieldoverride')
# Deleting model 'CcxFieldOverride'
db.delete_table('ccx_ccxfieldoverride')
models = {
......@@ -83,34 +96,42 @@ class Migration(SchemaMigration):
'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'}),
'ccx.ccxfieldoverride': {
'Meta': {'unique_together': "(('ccx', 'location', 'field'),)", 'object_name': 'CcxFieldOverride'},
'ccx': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ccx.CustomCourseForEdX']"}),
'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'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'})
'location': ('xmodule_django.models.LocationKeyField', [], {'max_length': '255', 'db_index': 'True'}),
'value': ('django.db.models.fields.TextField', [], {'default': "'null'"})
},
'ccx.ccxfuturemembership': {
'Meta': {'object_name': 'CcxFutureMembership'},
'auto_enroll': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'ccx': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ccx.CustomCourseForEdX']"}),
'email': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'pocs.personalonlinecourse': {
'Meta': {'object_name': 'PersonalOnlineCourse'},
'ccx.ccxmembership': {
'Meta': {'object_name': 'CcxMembership'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'ccx': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ccx.CustomCourseForEdX']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'ccx.customcourseforedx': {
'Meta': {'object_name': 'CustomCourseForEdX'},
'coach': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
'display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'pocs.pocfieldoverride': {
'Meta': {'unique_together': "(('poc', 'location', 'field'),)", 'object_name': 'PocFieldOverride'},
'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'location': ('xmodule_django.models.LocationKeyField', [], {'max_length': '255', 'db_index': 'True'}),
'poc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pocs.PersonalOnlineCourse']"}),
'value': ('django.db.models.fields.TextField', [], {'default': "'null'"})
},
'pocs.pocmembership': {
'Meta': {'object_name': 'PocMembership'},
'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'}),
'poc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pocs.PersonalOnlineCourse']"}),
'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
}
}
complete_apps = ['pocs']
\ No newline at end of file
complete_apps = ['ccx']
\ No newline at end of file
......@@ -5,20 +5,20 @@ from student.models import CourseEnrollment, AlreadyEnrolledError
from xmodule_django.models import CourseKeyField, LocationKeyField
class PersonalOnlineCourse(models.Model):
class CustomCourseForEdX(models.Model):
"""
A Personal Online Course.
A Custom Course.
"""
course_id = CourseKeyField(max_length=255, db_index=True)
display_name = models.CharField(max_length=255)
coach = models.ForeignKey(User, db_index=True)
class PocMembership(models.Model):
class CcxMembership(models.Model):
"""
Which students are in a POC?
Which students are in a CCX?
"""
poc = models.ForeignKey(PersonalOnlineCourse, db_index=True)
ccx = models.ForeignKey(CustomCourseForEdX, db_index=True)
student = models.ForeignKey(User, db_index=True)
active = models.BooleanField(default=False)
......@@ -30,11 +30,11 @@ class PocMembership(models.Model):
msg = "auto enrollment not allowed for {}"
raise ValueError(msg.format(future_membership))
membership = cls(
poc=future_membership.poc, student=student, active=True
ccx=future_membership.ccx, student=student, active=True
)
try:
CourseEnrollment.enroll(
student, future_membership.poc.course_id, check_access=True
student, future_membership.ccx.course_id, check_access=True
)
except AlreadyEnrolledError:
# if the user is already enrolled in the course, great!
......@@ -48,24 +48,24 @@ class PocMembership(models.Model):
return cls.objects.filter(student=user, active__exact=active)
class PocFutureMembership(models.Model):
class CcxFutureMembership(models.Model):
"""
Which emails for non-users are waiting to be added to POC on registration
Which emails for non-users are waiting to be added to CCX on registration
"""
poc = models.ForeignKey(PersonalOnlineCourse, db_index=True)
ccx = models.ForeignKey(CustomCourseForEdX, db_index=True)
email = models.CharField(max_length=255)
auto_enroll = models.BooleanField(default=0)
class PocFieldOverride(models.Model):
class CcxFieldOverride(models.Model):
"""
Field overrides for personal online courses.
Field overrides for custom courses.
"""
poc = models.ForeignKey(PersonalOnlineCourse, db_index=True)
ccx = models.ForeignKey(CustomCourseForEdX, db_index=True)
location = LocationKeyField(max_length=255, db_index=True)
field = models.CharField(max_length=255)
class Meta:
unique_together = (('poc', 'location', 'field'),)
unique_together = (('ccx', 'location', 'field'),)
value = models.TextField(default='null')
......@@ -8,81 +8,81 @@ import threading
from contextlib import contextmanager
from courseware.field_overrides import FieldOverrideProvider
from pocs import ACTIVE_POC_KEY
from ccx import ACTIVE_CCX_KEY
from .models import PocMembership, PocFieldOverride
from .models import CcxMembership, CcxFieldOverride
class PersonalOnlineCoursesOverrideProvider(FieldOverrideProvider):
class CustomCoursesForEdxOverrideProvider(FieldOverrideProvider):
"""
A concrete implementation of
:class:`~courseware.field_overrides.FieldOverrideProvider` which allows for
overrides to be made on a per user basis.
"""
def get(self, block, name, default):
poc = get_current_poc()
if poc:
return get_override_for_poc(poc, block, name, default)
ccx = get_current_ccx()
if ccx:
return get_override_for_ccx(ccx, block, name, default)
return default
class _PocContext(threading.local):
class _CcxContext(threading.local):
"""
A threading local used to implement the `with_poc` context manager, that
keeps track of the POC currently set as the context.
A threading local used to implement the `with_ccx` context manager, that
keeps track of the CCX currently set as the context.
"""
poc = None
ccx = None
_POC_CONTEXT = _PocContext()
_CCX_CONTEXT = _CcxContext()
@contextmanager
def poc_context(poc):
def ccx_context(ccx):
"""
A context manager which can be used to explicitly set the POC that is in
A context manager which can be used to explicitly set the CCX that is in
play for field overrides. This mechanism overrides the standard mechanism
of looking in the user's session to see if they are enrolled in a POC and
viewing that POC.
of looking in the user's session to see if they are enrolled in a CCX and
viewing that CCX.
"""
prev = _POC_CONTEXT.poc
_POC_CONTEXT.poc = poc
prev = _CCX_CONTEXT.ccx
_CCX_CONTEXT.ccx = ccx
yield
_POC_CONTEXT.poc = prev
_CCX_CONTEXT.ccx = prev
def get_current_poc():
def get_current_ccx():
"""
Return the poc that is active for this request.
Return the ccx that is active for this request.
"""
poc = _POC_CONTEXT.poc
if poc:
return poc
ccx = _CCX_CONTEXT.ccx
if ccx:
return ccx
def get_override_for_poc(poc, block, name, default=None):
def get_override_for_ccx(ccx, block, name, default=None):
"""
Gets the value of the overridden field for the `poc`. `block` and `name`
Gets the value of the overridden field for the `ccx`. `block` and `name`
specify the block and the name of the field. If the field is not
overridden for the given poc, returns `default`.
overridden for the given ccx, returns `default`.
"""
if not hasattr(block, '_poc_overrides'):
block._poc_overrides = {}
overrides = block._poc_overrides.get(poc.id)
if not hasattr(block, '_ccx_overrides'):
block._ccx_overrides = {}
overrides = block._ccx_overrides.get(ccx.id)
if overrides is None:
overrides = _get_overrides_for_poc(poc, block)
block._poc_overrides[poc.id] = overrides
overrides = _get_overrides_for_ccx(ccx, block)
block._ccx_overrides[ccx.id] = overrides
return overrides.get(name, default)
def _get_overrides_for_poc(poc, block):
def _get_overrides_for_ccx(ccx, block):
"""
Returns a dictionary mapping field name to overriden value for any
overrides set on this block for this POC.
overrides set on this block for this CCX.
"""
overrides = {}
query = PocFieldOverride.objects.filter(
poc=poc,
query = CcxFieldOverride.objects.filter(
ccx=ccx,
location=block.location
)
for override in query:
......@@ -92,68 +92,68 @@ def _get_overrides_for_poc(poc, block):
return overrides
def override_field_for_poc(poc, block, name, value):
def override_field_for_ccx(ccx, block, name, value):
"""
Overrides a field for the `poc`. `block` and `name` specify the block
Overrides a field for the `ccx`. `block` and `name` specify the block
and the name of the field on that block to override. `value` is the
value to set for the given field.
"""
override, created = PocFieldOverride.objects.get_or_create(
poc=poc,
override, created = CcxFieldOverride.objects.get_or_create(
ccx=ccx,
location=block.location,
field=name)
field = block.fields[name]
override.value = json.dumps(field.to_json(value))
override.save()
if hasattr(block, '_poc_overrides'):
del block._poc_overrides[poc.id]
if hasattr(block, '_ccx_overrides'):
del block._ccx_overrides[ccx.id]
def clear_override_for_poc(poc, block, name):
def clear_override_for_ccx(ccx, block, name):
"""
Clears a previously set field override for the `poc`. `block` and `name`
Clears a previously set field override for the `ccx`. `block` and `name`
specify the block and the name of the field on that block to clear.
This function is idempotent--if no override is set, nothing action is
performed.
"""
try:
PocFieldOverride.objects.get(
poc=poc,
CcxFieldOverride.objects.get(
ccx=ccx,
location=block.location,
field=name).delete()
if hasattr(block, '_poc_overrides'):
del block._poc_overrides[poc.id]
if hasattr(block, '_ccx_overrides'):
del block._ccx_overrides[ccx.id]
except PocFieldOverride.DoesNotExist:
except CcxFieldOverride.DoesNotExist:
pass
class PocMiddleware(object):
class CcxMiddleware(object):
"""
Checks to see if current session is examining a POC and sets the POC as
the current POC for the override machinery if so.
Checks to see if current session is examining a CCX and sets the CCX as
the current CCX for the override machinery if so.
"""
def process_request(self, request):
"""
Do the check.
"""
poc_id = request.session.get(ACTIVE_POC_KEY, None)
if poc_id is not None:
ccx_id = request.session.get(ACTIVE_CCX_KEY, None)
if ccx_id is not None:
try:
membership = PocMembership.objects.get(
student=request.user, active=True, poc__id__exact=poc_id
membership = CcxMembership.objects.get(
student=request.user, active=True, ccx__id__exact=ccx_id
)
_POC_CONTEXT.poc = membership.poc
except PocMembership.DoesNotExist:
# if there is no membership, be sure to unset the active poc
_POC_CONTEXT.poc = None
request.session.pop(ACTIVE_POC_KEY)
_CCX_CONTEXT.ccx = membership.ccx
except CcxMembership.DoesNotExist:
# if there is no membership, be sure to unset the active ccx
_CCX_CONTEXT.ccx = None
request.session.pop(ACTIVE_CCX_KEY)
def process_response(self, request, response):
"""
Clean up afterwards.
"""
_POC_CONTEXT.poc = None
_CCX_CONTEXT.ccx = None
return response
from factory.django import DjangoModelFactory
from ccx.models import CustomCourseForEdX
from ccx.models import CcxMembership
from ccx.models import CcxFutureMembership
class CcxFactory(DjangoModelFactory):
FACTORY_FOR = CustomCourseForEdX
display_name = "Test CCX"
class CcxMembershipFactory(DjangoModelFactory):
FACTORY_FOR = CcxMembership
active = False
class CcxFutureMembershipFactory(DjangoModelFactory):
FACTORY_FOR = CcxFutureMembership
from student.models import CourseEnrollment
from student.roles import CoursePocCoachRole
from student.roles import CourseCcxCoachRole
from student.tests.factories import (
AdminFactory,
CourseEnrollmentFactory,
......@@ -9,34 +9,35 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from .factories import (
PocFactory,
PocMembershipFactory,
PocFutureMembershipFactory,
CcxFactory,
CcxMembershipFactory,
CcxFutureMembershipFactory,
)
from ..models import (
PocMembership,
PocFutureMembership,
CcxMembership,
CcxFutureMembership,
)
class TestPocMembership(ModuleStoreTestCase):
"""Unit tests for the PocMembership model
class TestCcxMembership(ModuleStoreTestCase):
"""Unit tests for the CcxMembership model
"""
def setUp(self):
"""common setup for all tests"""
super(TestCcxMembership, self).setUp()
self.course = course = CourseFactory.create()
coach = AdminFactory.create()
role = CoursePocCoachRole(course.id)
role = CourseCcxCoachRole(course.id)
role.add_users(coach)
self.poc = PocFactory(course_id=course.id, coach=coach)
self.ccx = CcxFactory(course_id=course.id, coach=coach)
enrollment = CourseEnrollmentFactory.create(course_id=course.id)
self.enrolled_user = enrollment.user
self.unenrolled_user = UserFactory.create()
def create_future_enrollment(self, user, auto_enroll=True):
pfm = PocFutureMembershipFactory.create(
poc=self.poc,
pfm = CcxFutureMembershipFactory.create(
ccx=self.ccx,
email=user.email,
auto_enroll=auto_enroll
)
......@@ -48,62 +49,62 @@ class TestPocMembership(ModuleStoreTestCase):
)
return enrollment.exists()
def has_poc_membership(self, user):
membership = PocMembership.objects.filter(
student=user, poc=self.poc, active=True
def has_ccx_membership(self, user):
membership = CcxMembership.objects.filter(
student=user, ccx=self.ccx, active=True
)
return membership.exists()
def has_poc_future_membership(self, user):
future_membership = PocFutureMembership.objects.filter(
email=user.email, poc=self.poc
def has_ccx_future_membership(self, user):
future_membership = CcxFutureMembership.objects.filter(
email=user.email, ccx=self.ccx
)
return future_membership.exists()
def call_MUT(self, student, future_membership):
PocMembership.auto_enroll(student, future_membership)
CcxMembership.auto_enroll(student, future_membership)
def test_poc_auto_enroll_unregistered_user(self):
def test_ccx_auto_enroll_unregistered_user(self):
"""verify auto_enroll works when user is not enrolled in the MOOC
n.b. After auto_enroll, user will have both a MOOC enrollment and a
POC membership
CCX membership
"""
user = self.unenrolled_user
pfm = self.create_future_enrollment(user)
self.assertTrue(self.has_poc_future_membership(user))
self.assertTrue(self.has_ccx_future_membership(user))
self.assertFalse(self.has_course_enrollment(user))
# auto_enroll user
self.call_MUT(user, pfm)
self.assertTrue(self.has_course_enrollment(user))
self.assertTrue(self.has_poc_membership(user))
self.assertFalse(self.has_poc_future_membership(user))
self.assertTrue(self.has_ccx_membership(user))
self.assertFalse(self.has_ccx_future_membership(user))
def test_poc_auto_enroll_registered_user(self):
def test_ccx_auto_enroll_registered_user(self):
"""verify auto_enroll works when user is enrolled in the MOOC
"""
user = self.enrolled_user
pfm = self.create_future_enrollment(user)
self.assertTrue(self.has_poc_future_membership(user))
self.assertTrue(self.has_ccx_future_membership(user))
self.assertTrue(self.has_course_enrollment(user))
self.call_MUT(user, pfm)
self.assertTrue(self.has_course_enrollment(user))
self.assertTrue(self.has_poc_membership(user))
self.assertFalse(self.has_poc_future_membership(user))
self.assertTrue(self.has_ccx_membership(user))
self.assertFalse(self.has_ccx_future_membership(user))
def test_future_membership_disallows_auto_enroll(self):
"""verify that the PocFutureMembership can veto auto_enroll
"""verify that the CcxFutureMembership can veto auto_enroll
"""
user = self.unenrolled_user
pfm = self.create_future_enrollment(user, auto_enroll=False)
self.assertTrue(self.has_poc_future_membership(user))
self.assertTrue(self.has_ccx_future_membership(user))
self.assertFalse(self.has_course_enrollment(user))
self.assertRaises(ValueError, self.call_MUT, user, pfm)
self.assertFalse(self.has_course_enrollment(user))
self.assertFalse(self.has_poc_membership(user))
self.assertTrue(self.has_poc_future_membership(user))
self.assertFalse(self.has_ccx_membership(user))
self.assertTrue(self.has_ccx_future_membership(user))
......@@ -8,12 +8,12 @@ from student.tests.factories import AdminFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
from ..models import PersonalOnlineCourse
from ..overrides import override_field_for_poc
from ..models import CustomCourseForEdX
from ..overrides import override_field_for_ccx
@override_settings(FIELD_OVERRIDE_PROVIDERS=(
'pocs.overrides.PersonalOnlineCoursesOverrideProvider',))
'ccx.overrides.CustomCoursesForEdxOverrideProvider',))
class TestFieldOverrides(ModuleStoreTestCase):
"""
Make sure field overrides behave in the expected manner.
......@@ -22,6 +22,7 @@ class TestFieldOverrides(ModuleStoreTestCase):
"""
Set up tests
"""
super(TestFieldOverrides, self).setUp()
self.course = course = CourseFactory.create()
# Create a course outline
......@@ -41,15 +42,15 @@ class TestFieldOverrides(ModuleStoreTestCase):
[ItemFactory.create(parent=vertical) for _ in xrange(2)]
for vertical in verticals])
self.poc = poc = PersonalOnlineCourse(
self.ccx = ccx = CustomCourseForEdX(
course_id=course.id,
display_name='Test POC',
display_name='Test CCX',
coach=AdminFactory.create())
poc.save()
ccx.save()
patch = mock.patch('pocs.overrides.get_current_poc')
self.get_poc = get_poc = patch.start()
get_poc.return_value = poc
patch = mock.patch('ccx.overrides.get_current_ccx')
self.get_ccx = get_ccx = patch.start()
get_ccx.return_value = ccx
self.addCleanup(patch.stop)
# Apparently the test harness doesn't use LmsFieldStorage, and I'm not
......@@ -60,24 +61,30 @@ class TestFieldOverrides(ModuleStoreTestCase):
block._field_data = OverrideFieldData.wrap( # pylint: disable=protected-access
AdminFactory.create(), block._field_data) # pylint: disable=protected-access
# and after everything is done, clean up by un-doing the change to the
# OverrideFieldData object that is done during the wrap method.
def cleanup_provider_classes():
OverrideFieldData.provider_classes = None
self.addCleanup(cleanup_provider_classes)
def test_override_start(self):
"""
Test that overriding start date on a chapter works.
"""
poc_start = datetime.datetime(2014, 12, 25, 00, 00, tzinfo=pytz.UTC)
ccx_start = datetime.datetime(2014, 12, 25, 00, 00, tzinfo=pytz.UTC)
chapter = self.course.get_children()[0]
override_field_for_poc(self.poc, chapter, 'start', poc_start)
self.assertEquals(chapter.start, poc_start)
override_field_for_ccx(self.ccx, chapter, 'start', ccx_start)
self.assertEquals(chapter.start, ccx_start)
def test_override_is_inherited(self):
"""
Test that sequentials inherit overridden start date from chapter.
"""
poc_start = datetime.datetime(2014, 12, 25, 00, 00, tzinfo=pytz.UTC)
ccx_start = datetime.datetime(2014, 12, 25, 00, 00, tzinfo=pytz.UTC)
chapter = self.course.get_children()[0]
override_field_for_poc(self.poc, chapter, 'start', poc_start)
self.assertEquals(chapter.get_children()[0].start, poc_start)
self.assertEquals(chapter.get_children()[1].start, poc_start)
override_field_for_ccx(self.ccx, chapter, 'start', ccx_start)
self.assertEquals(chapter.get_children()[0].start, ccx_start)
self.assertEquals(chapter.get_children()[1].start, ccx_start)
def test_override_is_inherited_even_if_set_in_mooc(self):
"""
......@@ -85,12 +92,12 @@ class TestFieldOverrides(ModuleStoreTestCase):
(verticals) even if a due date is set explicitly on grandchildren in
the mooc.
"""
poc_due = datetime.datetime(2015, 1, 1, 00, 00, tzinfo=pytz.UTC)
ccx_due = datetime.datetime(2015, 1, 1, 00, 00, tzinfo=pytz.UTC)
chapter = self.course.get_children()[0]
chapter.display_name = 'itsme!'
override_field_for_poc(self.poc, chapter, 'due', poc_due)
override_field_for_ccx(self.ccx, chapter, 'due', ccx_due)
vertical = chapter.get_children()[0].get_children()[0]
self.assertEqual(vertical.due, poc_due)
self.assertEqual(vertical.due, ccx_due)
def flatten(seq):
......
from pocs.models import (
PocMembership,
PocFutureMembership,
from ccx.models import (
CcxMembership,
CcxFutureMembership,
)
from pocs.tests.factories import (
PocFactory,
PocMembershipFactory,
PocFutureMembershipFactory,
from ccx.tests.factories import (
CcxFactory,
CcxMembershipFactory,
CcxFutureMembershipFactory,
)
from student.roles import CoursePocCoachRole
from student.roles import CourseCcxCoachRole
from student.tests.factories import (
AdminFactory,
UserFactory,
......@@ -26,11 +26,15 @@ class TestEmailEnrollmentState(ModuleStoreTestCase):
"""
Set up tests
"""
super(TestEmailEnrollmentState, self).setUp()
# remove user provided by the parent test case so we can make our own
# when needed.
self.user = None
course = CourseFactory.create()
coach = AdminFactory.create()
role = CoursePocCoachRole(course.id)
role = CourseCcxCoachRole(course.id)
role.add_users(coach)
self.poc = PocFactory(course_id=course.id, coach=coach)
self.ccx = CcxFactory(course_id=course.id, coach=coach)
def create_user(self):
"""provide a legitimate django user for testing
......@@ -38,47 +42,47 @@ class TestEmailEnrollmentState(ModuleStoreTestCase):
if getattr(self, 'user', None) is None:
self.user = UserFactory()
def register_user_in_poc(self):
"""create registration of self.user in self.poc
def register_user_in_ccx(self):
"""create registration of self.user in self.ccx
registration will be inactive
"""
self.create_user()
PocMembershipFactory(poc=self.poc, student=self.user)
CcxMembershipFactory(ccx=self.ccx, student=self.user)
def create_one(self, email=None):
"""Create a single EmailEnrollmentState object and return it
"""
from pocs.utils import EmailEnrollmentState
from ccx.utils import EmailEnrollmentState
if email is None:
email = self.user.email
return EmailEnrollmentState(self.poc, email)
return EmailEnrollmentState(self.ccx, email)
def test_enrollment_state_for_non_user(self):
"""verify behavior for non-user email address
"""
ee_state = self.create_one(email='nobody@nowhere.com')
for attr in ['user', 'member', 'full_name', 'in_poc']:
for attr in ['user', 'member', 'full_name', 'in_ccx']:
value = getattr(ee_state, attr, 'missing attribute')
self.assertFalse(value, "{}: {}".format(value, attr))
def test_enrollment_state_for_non_member_user(self):
"""verify behavior for email address of user who is not a poc memeber
"""verify behavior for email address of user who is not a ccx memeber
"""
self.create_user()
ee_state = self.create_one()
self.assertTrue(ee_state.user)
self.assertFalse(ee_state.in_poc)
self.assertFalse(ee_state.in_ccx)
self.assertEqual(ee_state.member, self.user)
self.assertEqual(ee_state.full_name, self.user.profile.name)
def test_enrollment_state_for_member_user(self):
"""verify behavior for email address of user who is a poc member
"""verify behavior for email address of user who is a ccx member
"""
self.create_user()
self.register_user_in_poc()
self.register_user_in_ccx()
ee_state = self.create_one()
for attr in ['user', 'in_poc']:
for attr in ['user', 'in_ccx']:
self.assertTrue(
getattr(ee_state, attr, False),
"attribute {} is missing or False".format(attr)
......@@ -90,13 +94,13 @@ class TestEmailEnrollmentState(ModuleStoreTestCase):
"""verify dict representation of EmailEnrollmentState
"""
self.create_user()
self.register_user_in_poc()
self.register_user_in_ccx()
ee_state = self.create_one()
ee_dict = ee_state.to_dict()
expected = {
'user': True,
'member': self.user,
'in_poc': True,
'in_ccx': True,
}
for expected_key, expected_value in expected.iteritems():
self.assertTrue(expected_key in ee_dict)
......@@ -104,28 +108,29 @@ class TestEmailEnrollmentState(ModuleStoreTestCase):
def test_enrollment_state_repr(self):
self.create_user()
self.register_user_in_poc()
self.register_user_in_ccx()
ee_state = self.create_one()
representation = repr(ee_state)
self.assertTrue('user=True' in representation)
self.assertTrue('in_poc=True' in representation)
self.assertTrue('in_ccx=True' in representation)
member = 'member={}'.format(self.user)
self.assertTrue(member in representation)
# TODO: deal with changes in behavior for auto_enroll
class TestGetEmailParams(ModuleStoreTestCase):
"""tests for pocs.utils.get_email_params
"""tests for ccx.utils.get_email_params
"""
def setUp(self):
"""
Set up tests
"""
super(TestGetEmailParams, self).setUp()
course = CourseFactory.create()
coach = AdminFactory.create()
role = CoursePocCoachRole(course.id)
role = CourseCcxCoachRole(course.id)
role.add_users(coach)
self.poc = PocFactory(course_id=course.id, coach=coach)
self.ccx = CcxFactory(course_id=course.id, coach=coach)
self.all_keys = [
'site_name', 'course', 'course_url', 'registration_url',
'course_about_url', 'auto_enroll'
......@@ -134,17 +139,17 @@ class TestGetEmailParams(ModuleStoreTestCase):
self.course_keys = [k for k in self.url_keys if 'course' in k]
def call_FUT(self, auto_enroll=False, secure=False):
from pocs.utils import get_email_params
return get_email_params(self.poc, auto_enroll, secure)
from ccx.utils import get_email_params
return get_email_params(self.ccx, auto_enroll, secure)
def test_params_have_expected_keys(self):
params = self.call_FUT()
self.assertFalse(set(params.keys()) - set(self.all_keys))
def test_poc_id_in_params(self):
expected_course_id = self.poc.course_id.to_deprecated_string()
def test_ccx_id_in_params(self):
expected_course_id = self.ccx.course_id.to_deprecated_string()
params = self.call_FUT()
self.assertEqual(params['course'], self.poc)
self.assertEqual(params['course'], self.ccx)
for url_key in self.url_keys:
self.assertTrue('http://' in params[url_key])
for url_key in self.course_keys:
......@@ -167,14 +172,18 @@ class TestGetEmailParams(ModuleStoreTestCase):
# TODO: deal with changes in behavior for auto_enroll
class TestEnrollEmail(ModuleStoreTestCase):
"""tests for the enroll_email function from pocs.utils
"""tests for the enroll_email function from ccx.utils
"""
def setUp(self):
super(TestEnrollEmail, self).setUp()
# unbind the user created by the parent, so we can create our own when
# needed.
self.user = None
course = CourseFactory.create()
coach = AdminFactory.create()
role = CoursePocCoachRole(course.id)
role = CourseCcxCoachRole(course.id)
role.add_users(coach)
self.poc = PocFactory(course_id=course.id, coach=coach)
self.ccx = CcxFactory(course_id=course.id, coach=coach)
self.outbox = self.get_outbox()
def create_user(self):
......@@ -183,13 +192,13 @@ class TestEnrollEmail(ModuleStoreTestCase):
if getattr(self, 'user', None) is None:
self.user = UserFactory()
def register_user_in_poc(self):
"""create registration of self.user in self.poc
def register_user_in_ccx(self):
"""create registration of self.user in self.ccx
registration will be inactive
"""
self.create_user()
PocMembershipFactory(poc=self.poc, student=self.user)
CcxMembershipFactory(ccx=self.ccx, student=self.user)
def get_outbox(self):
"""Return the django mail outbox"""
......@@ -197,31 +206,31 @@ class TestEnrollEmail(ModuleStoreTestCase):
return mail.outbox
def check_membership(self, email=None, user=None, future=False):
"""Verify tjat an appropriate Poc Membership exists"""
"""Verify tjat an appropriate CCX Membership exists"""
if not email and not user:
self.fail(
"must provide user or email address to check Poc Membership"
"must provide user or email address to check CCX Membership"
)
if future and email:
membership = PocFutureMembership.objects.filter(
poc=self.poc, email=email
membership = CcxFutureMembership.objects.filter(
ccx=self.ccx, email=email
)
elif not future:
if not user:
user = self.user
membership = PocMembership.objects.filter(
poc=self.poc, student=user
membership = CcxMembership.objects.filter(
ccx=self.ccx, student=user
)
self.assertTrue(membership.exists())
def check_enrollment_state(self, state, in_poc, member, user):
def check_enrollment_state(self, state, in_ccx, member, user):
"""Verify an enrollment state object against provided arguments
state.in_poc will always be a boolean
state.in_ccx will always be a boolean
state.user will always be a boolean
state.member will be a Django user object or None
"""
self.assertEqual(in_poc, state.in_poc)
self.assertEqual(in_ccx, state.in_ccx)
self.assertEqual(member, state.member)
self.assertEqual(user, state.user)
......@@ -232,11 +241,11 @@ class TestEnrollEmail(ModuleStoreTestCase):
email_students=False,
email_params=None
):
from pocs.utils import enroll_email
from ccx.utils import enroll_email
if student_email is None:
student_email = self.user.email
before, after = enroll_email(
self.poc, student_email, auto_enroll, email_students, email_params
self.ccx, student_email, auto_enroll, email_students, email_params
)
return before, after
......@@ -279,7 +288,7 @@ class TestEnrollEmail(ModuleStoreTestCase):
def test_enroll_member_sending_email(self):
"""register a member and send an enrollment email to them
"""
self.register_user_in_poc()
self.register_user_in_ccx()
# ensure no emails are in the outbox now
self.assertEqual(len(self.outbox), 0)
before, after = self.call_FUT(email_students=True)
......@@ -327,7 +336,7 @@ class TestEnrollEmail(ModuleStoreTestCase):
def test_enroll_member_no_email(self):
"""enroll a member but send no email
"""
self.register_user_in_poc()
self.register_user_in_ccx()
# ensure no emails are in the outbox now
self.assertEqual(len(self.outbox), 0)
before, after = self.call_FUT(email_students=False)
......@@ -342,13 +351,17 @@ class TestEnrollEmail(ModuleStoreTestCase):
# TODO: deal with changes in behavior for auto_enroll
class TestUnenrollEmail(ModuleStoreTestCase):
"""Tests for the unenroll_email function from pocs.utils"""
"""Tests for the unenroll_email function from ccx.utils"""
def setUp(self):
super(TestUnenrollEmail, self).setUp()
# unbind the user created by the parent, so we can create our own when
# needed.
self.user = None
course = CourseFactory.create()
coach = AdminFactory.create()
role = CoursePocCoachRole(course.id)
role = CourseCcxCoachRole(course.id)
role.add_users(coach)
self.poc = PocFactory(course_id=course.id, coach=coach)
self.ccx = CcxFactory(course_id=course.id, coach=coach)
self.outbox = self.get_outbox()
def tearDown(self):
......@@ -367,52 +380,52 @@ class TestUnenrollEmail(ModuleStoreTestCase):
if getattr(self, 'user', None) is None:
self.user = UserFactory()
def make_poc_membership(self):
"""create registration of self.user in self.poc
def make_ccx_membership(self):
"""create registration of self.user in self.ccx
registration will be inactive
"""
self.create_user()
PocMembershipFactory.create(poc=self.poc, student=self.user)
CcxMembershipFactory.create(ccx=self.ccx, student=self.user)
def make_poc_future_membership(self):
"""create future registration for email in self.poc"""
def make_ccx_future_membership(self):
"""create future registration for email in self.ccx"""
self.email = "nobody@nowhere.com"
PocFutureMembershipFactory.create(
poc=self.poc, email=self.email
CcxFutureMembershipFactory.create(
ccx=self.ccx, email=self.email
)
def check_enrollment_state(self, state, in_poc, member, user):
def check_enrollment_state(self, state, in_ccx, member, user):
"""Verify an enrollment state object against provided arguments
state.in_poc will always be a boolean
state.in_ccx will always be a boolean
state.user will always be a boolean
state.member will be a Django user object or None
"""
self.assertEqual(in_poc, state.in_poc)
self.assertEqual(in_ccx, state.in_ccx)
self.assertEqual(member, state.member)
self.assertEqual(user, state.user)
def check_membership(self, future=False):
if future:
membership = PocFutureMembership.objects.filter(
poc=self.poc, email=self.email
membership = CcxFutureMembership.objects.filter(
ccx=self.ccx, email=self.email
)
else:
membership = PocMembership.objects.filter(
poc=self.poc, student=self.user
membership = CcxMembership.objects.filter(
ccx=self.ccx, student=self.user
)
return membership.exists()
def call_FUT(self, email_students=False):
from pocs.utils import unenroll_email
email = hasattr(self, 'user') and self.user.email or self.email
return unenroll_email(self.poc, email, email_students=email_students)
from ccx.utils import unenroll_email
email = getattr(self, 'user', None) and self.user.email or self.email
return unenroll_email(self.ccx, email, email_students=email_students)
def test_unenroll_future_member_with_email(self):
"""unenroll a future member and send an email
"""
self.make_poc_future_membership()
self.make_ccx_future_membership()
# assert that a membership exists and that no emails have been sent
self.assertTrue(self.check_membership(future=True))
self.assertEqual(len(self.outbox), 0)
......@@ -431,7 +444,7 @@ class TestUnenrollEmail(ModuleStoreTestCase):
def test_unenroll_member_with_email(self):
"""unenroll a current member and send an email"""
self.make_poc_membership()
self.make_ccx_membership()
# assert that a membership exists and that no emails have been sent
self.assertTrue(self.check_membership())
self.assertEqual(len(self.outbox), 0)
......@@ -451,7 +464,7 @@ class TestUnenrollEmail(ModuleStoreTestCase):
def test_unenroll_future_member_no_email(self):
"""unenroll a future member but send no email
"""
self.make_poc_future_membership()
self.make_ccx_future_membership()
# assert that a membership exists and that no emails have been sent
self.assertTrue(self.check_membership(future=True))
self.assertEqual(len(self.outbox), 0)
......@@ -469,7 +482,7 @@ class TestUnenrollEmail(ModuleStoreTestCase):
def test_unenroll_member_no_email(self):
"""unenroll a current member but send no email
"""
self.make_poc_membership()
self.make_ccx_membership()
# assert that a membership exists and that no emails have been sent
self.assertTrue(self.check_membership())
self.assertEqual(len(self.outbox), 0)
......@@ -485,64 +498,65 @@ class TestUnenrollEmail(ModuleStoreTestCase):
self.assertEqual(len(self.outbox), 0)
class TestUserPocList(ModuleStoreTestCase):
"""Unit tests for poc.utils.get_all_pocs_for_user"""
class TestUserCCXList(ModuleStoreTestCase):
"""Unit tests for ccx.utils.get_all_ccx_for_user"""
def setUp(self):
"""Create required infrastructure for tests"""
super(TestUserCCXList, self).setUp()
self.course = CourseFactory.create()
coach = AdminFactory.create()
role = CoursePocCoachRole(self.course.id)
role = CourseCcxCoachRole(self.course.id)
role.add_users(coach)
self.poc = PocFactory(course_id=self.course.id, coach=coach)
self.ccx = CcxFactory(course_id=self.course.id, coach=coach)
enrollment = CourseEnrollmentFactory.create(course_id=self.course.id)
self.user = enrollment.user
self.anonymous = AnonymousUserFactory.create()
def register_user_in_poc(self, active=False):
"""create registration of self.user in self.poc
def register_user_in_ccx(self, active=False):
"""create registration of self.user in self.ccx
registration will be inactive unless active=True
"""
PocMembershipFactory(poc=self.poc, student=self.user, active=active)
CcxMembershipFactory(ccx=self.ccx, student=self.user, active=active)
def get_course_title(self):
from courseware.courses import get_course_about_section
return get_course_about_section(self.course, 'title')
def call_FUT(self, user):
from pocs.utils import get_all_pocs_for_user
return get_all_pocs_for_user(user)
from ccx.utils import get_all_ccx_for_user
return get_all_ccx_for_user(user)
def test_anonymous_sees_no_pocs(self):
def test_anonymous_sees_no_ccx(self):
memberships = self.call_FUT(self.anonymous)
self.assertEqual(len(memberships), 0)
def test_unenrolled_sees_no_pocs(self):
def test_unenrolled_sees_no_ccx(self):
memberships = self.call_FUT(self.user)
self.assertEqual(len(memberships), 0)
def test_enrolled_inactive_sees_no_pocs(self):
self.register_user_in_poc()
def test_enrolled_inactive_sees_no_ccx(self):
self.register_user_in_ccx()
memberships = self.call_FUT(self.user)
self.assertEqual(len(memberships), 0)
def test_enrolled_sees_a_poc(self):
self.register_user_in_poc(active=True)
def test_enrolled_sees_a_ccx(self):
self.register_user_in_ccx(active=True)
memberships = self.call_FUT(self.user)
self.assertEqual(len(memberships), 1)
def test_data_structure(self):
self.register_user_in_poc(active=True)
self.register_user_in_ccx(active=True)
memberships = self.call_FUT(self.user)
this_membership = memberships[0]
self.assertTrue(this_membership)
# structure contains the expected keys
for key in ['poc_name', 'poc_url']:
for key in ['ccx_name', 'ccx_url']:
self.assertTrue(key in this_membership.keys())
url_parts = [self.course.id.to_deprecated_string(), str(self.poc.id)]
# all parts of the poc url are present
url_parts = [self.course.id.to_deprecated_string(), str(self.ccx.id)]
# all parts of the ccx url are present
for part in url_parts:
self.assertTrue(part in this_membership['poc_url'])
actual_name = self.poc.display_name
self.assertEqual(actual_name, this_membership['poc_name'])
self.assertTrue(part in this_membership['ccx_url'])
actual_name = self.ccx.display_name
self.assertEqual(actual_name, this_membership['ccx_name'])
......@@ -11,7 +11,7 @@ from courseware.tests.helpers import LoginEnrollmentTestCase
from django.core.urlresolvers import reverse
from django.test.utils import override_settings
from edxmako.shortcuts import render_to_response
from student.roles import CoursePocCoachRole
from student.roles import CourseCcxCoachRole
from student.tests.factories import (
AdminFactory,
CourseEnrollmentFactory,
......@@ -23,18 +23,18 @@ from xmodule.modulestore.tests.factories import (
CourseFactory,
ItemFactory,
)
from pocs import ACTIVE_POC_KEY
from ccx import ACTIVE_CCX_KEY
from ..models import (
PersonalOnlineCourse,
PocMembership,
PocFutureMembership,
CustomCourseForEdX,
CcxMembership,
CcxFutureMembership,
)
from ..overrides import get_override_for_poc, override_field_for_poc
from .. import ACTIVE_POC_KEY
from ..overrides import get_override_for_ccx, override_field_for_ccx
from .. import ACTIVE_CCX_KEY
from .factories import (
PocFactory,
PocMembershipFactory,
PocFutureMembershipFactory,
CcxFactory,
CcxMembershipFactory,
CcxFutureMembershipFactory,
)
......@@ -54,12 +54,13 @@ def intercept_renderer(path, context):
class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""
Tests for Personal Online Courses views.
Tests for Custom Courses views.
"""
def setUp(self):
"""
Set up tests
"""
super(TestCoachDashboard, self).setUp()
self.course = course = CourseFactory.create()
# Create instructor account
......@@ -89,12 +90,12 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
)
def make_coach(self):
role = CoursePocCoachRole(self.course.id)
role = CourseCcxCoachRole(self.course.id)
role.add_users(self.coach)
def make_poc(self):
poc = PocFactory(course_id=self.course.id, coach=self.coach)
return poc
def make_ccx(self):
ccx = CcxFactory(course_id=self.course.id, coach=self.coach)
return ccx
def get_outbox(self):
from django.core import mail
......@@ -111,51 +112,51 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
User is not a coach, should get Forbidden response.
"""
url = reverse(
'poc_coach_dashboard',
'ccx_coach_dashboard',
kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url)
self.assertEqual(response.status_code, 403)
def test_no_poc_created(self):
def test_no_ccx_created(self):
"""
No POC is created, coach should see form to add a POC.
No CCX is created, coach should see form to add a CCX.
"""
self.make_coach()
url = reverse(
'poc_coach_dashboard',
'ccx_coach_dashboard',
kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertTrue(re.search(
'<form action=".+create_poc"',
'<form action=".+create_ccx"',
response.content))
def test_create_poc(self):
def test_create_ccx(self):
"""
Create POC. Follow redirect to coach dashboard, confirm we see
the coach dashboard for the new POC.
Create CCX. Follow redirect to coach dashboard, confirm we see
the coach dashboard for the new CCX.
"""
self.make_coach()
url = reverse(
'create_poc',
'create_ccx',
kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.post(url, {'name': 'New POC'})
response = self.client.post(url, {'name': 'New CCX'})
self.assertEqual(response.status_code, 302)
url = response.get('location')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertTrue(re.search('id="poc-schedule"', response.content))
self.assertTrue(re.search('id="ccx-schedule"', response.content))
@patch('pocs.views.render_to_response', intercept_renderer)
@patch('pocs.views.TODAY')
@patch('ccx.views.render_to_response', intercept_renderer)
@patch('ccx.views.TODAY')
def test_edit_schedule(self, today):
"""
Get POC schedule, modify it, save it.
Get CCX schedule, modify it, save it.
"""
today.return_value = datetime.datetime(2014, 11, 25, tzinfo=pytz.UTC)
self.test_create_poc()
self.test_create_ccx()
url = reverse(
'poc_coach_dashboard',
'ccx_coach_dashboard',
kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url)
schedule = json.loads(response.mako_context['schedule'])
......@@ -170,12 +171,12 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
)
url = reverse(
'save_poc',
'save_ccx',
kwargs={'course_id': self.course.id.to_deprecated_string()})
def unhide(unit):
"""
Recursively unhide a unit and all of its children in the POC
Recursively unhide a unit and all of its children in the CCX
schedule.
"""
unit['hidden'] = False
......@@ -198,12 +199,12 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
# Make sure start date set on course, follows start date of earliest
# scheduled chapter
poc = PersonalOnlineCourse.objects.get()
course_start = get_override_for_poc(poc, self.course, 'start')
ccx = CustomCourseForEdX.objects.get()
course_start = get_override_for_ccx(ccx, self.course, 'start')
self.assertEqual(str(course_start)[:-9], u'2014-11-20 00:00')
# Make sure grading policy adjusted
policy = get_override_for_poc(poc, self.course, 'grading_policy',
policy = get_override_for_ccx(ccx, self.course, 'grading_policy',
self.course.grading_policy)
self.assertEqual(policy['GRADER'][0]['type'], 'Homework')
self.assertEqual(policy['GRADER'][0]['min_count'], 4)
......@@ -218,14 +219,14 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""enroll a list of students who are members of the class
"""
self.make_coach()
poc = self.make_poc()
ccx = self.make_ccx()
enrollment = CourseEnrollmentFactory(course_id=self.course.id)
student = enrollment.user
outbox = self.get_outbox()
self.assertEqual(len(outbox), 0)
url = reverse(
'poc_invite',
'ccx_invite',
kwargs={'course_id': self.course.id.to_deprecated_string()}
)
data = {
......@@ -240,25 +241,25 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.assertTrue(302 in response.redirect_chain[0])
self.assertEqual(len(outbox), 1)
self.assertTrue(student.email in outbox[0].recipients())
# a PocMembership exists for this student
# a CcxMembership exists for this student
self.assertTrue(
PocMembership.objects.filter(poc=poc, student=student).exists()
CcxMembership.objects.filter(ccx=ccx, student=student).exists()
)
def test_unenroll_member_student(self):
"""unenroll a list of students who are members of the class
"""
self.make_coach()
poc = self.make_poc()
ccx = self.make_ccx()
enrollment = CourseEnrollmentFactory(course_id=self.course.id)
student = enrollment.user
outbox = self.get_outbox()
self.assertEqual(len(outbox), 0)
# student is member of POC:
PocMembershipFactory(poc=poc, student=student)
# student is member of CCX:
CcxMembershipFactory(ccx=ccx, student=student)
url = reverse(
'poc_invite',
'ccx_invite',
kwargs={'course_id': self.course.id.to_deprecated_string()}
)
data = {
......@@ -275,7 +276,7 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.assertTrue(student.email in outbox[0].recipients())
# the membership for this student is gone
self.assertFalse(
PocMembership.objects.filter(poc=poc, student=student).exists()
CcxMembership.objects.filter(ccx=ccx, student=student).exists()
)
def test_enroll_non_user_student(self):
......@@ -283,12 +284,12 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""
test_email = "nobody@nowhere.com"
self.make_coach()
poc = self.make_poc()
ccx = self.make_ccx()
outbox = self.get_outbox()
self.assertEqual(len(outbox), 0)
url = reverse(
'poc_invite',
'ccx_invite',
kwargs={'course_id': self.course.id.to_deprecated_string()}
)
data = {
......@@ -304,8 +305,8 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.assertEqual(len(outbox), 1)
self.assertTrue(test_email in outbox[0].recipients())
self.assertTrue(
PocFutureMembership.objects.filter(
poc=poc, email=test_email
CcxFutureMembership.objects.filter(
ccx=ccx, email=test_email
).exists()
)
......@@ -314,13 +315,13 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""
test_email = "nobody@nowhere.com"
self.make_coach()
poc = self.make_poc()
ccx = self.make_ccx()
outbox = self.get_outbox()
PocFutureMembershipFactory(poc=poc, email=test_email)
CcxFutureMembershipFactory(ccx=ccx, email=test_email)
self.assertEqual(len(outbox), 0)
url = reverse(
'poc_invite',
'ccx_invite',
kwargs={'course_id': self.course.id.to_deprecated_string()}
)
data = {
......@@ -336,8 +337,8 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.assertEqual(len(outbox), 1)
self.assertTrue(test_email in outbox[0].recipients())
self.assertFalse(
PocFutureMembership.objects.filter(
poc=poc, email=test_email
CcxFutureMembership.objects.filter(
ccx=ccx, email=test_email
).exists()
)
......@@ -345,7 +346,7 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""enroll a single student who is a member of the class already
"""
self.make_coach()
poc = self.make_poc()
ccx = self.make_ccx()
enrollment = CourseEnrollmentFactory(course_id=self.course.id)
student = enrollment.user
# no emails have been sent so far
......@@ -353,7 +354,7 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.assertEqual(len(outbox), 0)
url = reverse(
'poc_manage_student',
'ccx_manage_student',
kwargs={'course_id': self.course.id.to_deprecated_string()}
)
data = {
......@@ -366,25 +367,25 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.assertEqual(len(response.redirect_chain), 1)
self.assertTrue(302 in response.redirect_chain[0])
self.assertEqual(len(outbox), 0)
# a PocMembership exists for this student
# a CcxMembership exists for this student
self.assertTrue(
PocMembership.objects.filter(poc=poc, student=student).exists()
CcxMembership.objects.filter(ccx=ccx, student=student).exists()
)
def test_manage_remove_single_student(self):
"""unenroll a single student who is a member of the class already
"""
self.make_coach()
poc = self.make_poc()
ccx = self.make_ccx()
enrollment = CourseEnrollmentFactory(course_id=self.course.id)
student = enrollment.user
PocMembershipFactory(poc=poc, student=student)
CcxMembershipFactory(ccx=ccx, student=student)
# no emails have been sent so far
outbox = self.get_outbox()
self.assertEqual(len(outbox), 0)
url = reverse(
'poc_manage_student',
'ccx_manage_student',
kwargs={'course_id': self.course.id.to_deprecated_string()}
)
data = {
......@@ -397,22 +398,23 @@ class TestCoachDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.assertEqual(len(response.redirect_chain), 1)
self.assertTrue(302 in response.redirect_chain[0])
self.assertEqual(len(outbox), 0)
# a PocMembership exists for this student
# a CcxMembership exists for this student
self.assertFalse(
PocMembership.objects.filter(poc=poc, student=student).exists()
CcxMembership.objects.filter(ccx=ccx, student=student).exists()
)
@override_settings(FIELD_OVERRIDE_PROVIDERS=(
'pocs.overrides.PersonalOnlineCoursesOverrideProvider',))
class TestPocGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
'ccx.overrides.CustomCoursesForEdxOverrideProvider',))
class TestCCXGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""
Tests for Personal Online Courses views.
Tests for Custom Courses views.
"""
def setUp(self):
"""
Set up tests
"""
super(TestCCXGrades, self).setUp()
self.course = course = CourseFactory.create()
# Create instructor account
......@@ -431,13 +433,13 @@ class TestPocGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
metadata={'graded': True, 'format': 'Homework'})
for _ in xrange(4)]
role = CoursePocCoachRole(self.course.id)
role = CourseCcxCoachRole(self.course.id)
role.add_users(coach)
self.poc = poc = PocFactory(course_id=self.course.id, coach=self.coach)
self.ccx = ccx = CcxFactory(course_id=self.course.id, coach=self.coach)
self.student = student = UserFactory.create()
CourseEnrollmentFactory.create(user=student, course_id=self.course.id)
PocMembershipFactory(poc=poc, student=student, active=True)
CcxMembershipFactory(ccx=ccx, student=student, active=True)
for i, section in enumerate(sections):
for j in xrange(4):
......@@ -466,12 +468,18 @@ class TestPocGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
block._field_data_cache = {}
visible_children(block)
patch_context = patch('pocs.views.get_course_by_id')
# and after everything is done, clean up by un-doing the change to the
# OverrideFieldData object that is done during the wrap method.
def cleanup_provider_classes():
OverrideFieldData.provider_classes = None
self.addCleanup(cleanup_provider_classes)
patch_context = patch('ccx.views.get_course_by_id')
get_course = patch_context.start()
get_course.return_value = course
self.addCleanup(patch_context.stop)
override_field_for_poc(poc, course, 'grading_policy', {
override_field_for_ccx(ccx, course, 'grading_policy', {
'GRADER': [
{'drop_count': 0,
'min_count': 2,
......@@ -481,13 +489,13 @@ class TestPocGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
],
'GRADE_CUTOFFS': {'Pass': 0.75},
})
override_field_for_poc(
poc, sections[-1], 'visible_to_staff_only', True)
override_field_for_ccx(
ccx, sections[-1], 'visible_to_staff_only', True)
@patch('pocs.views.render_to_response', intercept_renderer)
@patch('ccx.views.render_to_response', intercept_renderer)
def test_gradebook(self):
url = reverse(
'poc_gradebook',
'ccx_gradebook',
kwargs={'course_id': self.course.id.to_deprecated_string()}
)
response = self.client.get(url)
......@@ -502,7 +510,7 @@ class TestPocGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
def test_grades_csv(self):
url = reverse(
'poc_grades_csv',
'ccx_grades_csv',
kwargs={'course_id': self.course.id.to_deprecated_string()}
)
response = self.client.get(url)
......@@ -527,9 +535,9 @@ class TestPocGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.client.login(username=self.student.username, password="test")
session = self.client.session
session[ACTIVE_POC_KEY] = self.poc.id
session[ACTIVE_CCX_KEY] = self.ccx.id
session.save()
self.client.session.get(ACTIVE_POC_KEY)
self.client.session.get(ACTIVE_CCX_KEY)
url = reverse(
'progress',
kwargs={'course_id': self.course.id.to_deprecated_string()}
......@@ -542,175 +550,176 @@ class TestPocGrades(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.assertEqual(len(grades['section_breakdown']), 4)
class TestSwitchActivePoc(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""Verify the view for switching which POC is active, if any
class TestSwitchActiveCCX(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""Verify the view for switching which CCX is active, if any
"""
def setUp(self):
super(TestSwitchActiveCCX, self).setUp()
self.course = course = CourseFactory.create()
coach = AdminFactory.create()
role = CoursePocCoachRole(course.id)
role = CourseCcxCoachRole(course.id)
role.add_users(coach)
self.poc = PocFactory(course_id=course.id, coach=coach)
self.ccx = CcxFactory(course_id=course.id, coach=coach)
enrollment = CourseEnrollmentFactory.create(course_id=course.id)
self.user = enrollment.user
self.target_url = reverse(
'course_root', args=[course.id.to_deprecated_string()]
)
def register_user_in_poc(self, active=False):
"""create registration of self.user in self.poc
def register_user_in_ccx(self, active=False):
"""create registration of self.user in self.ccx
registration will be inactive unless active=True
"""
PocMembershipFactory(poc=self.poc, student=self.user, active=active)
CcxMembershipFactory(ccx=self.ccx, student=self.user, active=active)
def revoke_poc_registration(self):
from ..models import PocMembership
membership = PocMembership.objects.filter(
poc=self.poc, student=self.user
def revoke_ccx_registration(self):
from ..models import CcxMembership
membership = CcxMembership.objects.filter(
ccx=self.ccx, student=self.user
)
membership.delete()
def verify_active_poc(self, request, id=None):
def verify_active_ccx(self, request, id=None):
if id:
id = str(id)
self.assertEqual(id, request.session.get(ACTIVE_POC_KEY, None))
self.assertEqual(id, request.session.get(ACTIVE_CCX_KEY, None))
def test_unauthorized_cannot_switch_to_poc(self):
def test_unauthorized_cannot_switch_to_ccx(self):
switch_url = reverse(
'switch_active_poc',
args=[self.course.id.to_deprecated_string(), self.poc.id]
'switch_active_ccx',
args=[self.course.id.to_deprecated_string(), self.ccx.id]
)
response = self.client.get(switch_url)
self.assertEqual(response.status_code, 302)
def test_unauthorized_cannot_switch_to_mooc(self):
switch_url = reverse(
'switch_active_poc',
'switch_active_ccx',
args=[self.course.id.to_deprecated_string()]
)
response = self.client.get(switch_url)
self.assertEqual(response.status_code, 302)
def test_enrolled_inactive_user_cannot_select_poc(self):
self.register_user_in_poc(active=False)
def test_enrolled_inactive_user_cannot_select_ccx(self):
self.register_user_in_ccx(active=False)
self.client.login(username=self.user.username, password="test")
switch_url = reverse(
'switch_active_poc',
args=[self.course.id.to_deprecated_string(), self.poc.id]
'switch_active_ccx',
args=[self.course.id.to_deprecated_string(), self.ccx.id]
)
response = self.client.get(switch_url)
self.assertEqual(response.status_code, 302)
self.assertTrue(response.get('Location', '').endswith(self.target_url))
# if the poc were active, we'd need to pass the ID of the poc here.
self.verify_active_poc(self.client)
# if the ccx were active, we'd need to pass the ID of the ccx here.
self.verify_active_ccx(self.client)
def test_enrolled_user_can_select_poc(self):
self.register_user_in_poc(active=True)
def test_enrolled_user_can_select_ccx(self):
self.register_user_in_ccx(active=True)
self.client.login(username=self.user.username, password="test")
switch_url = reverse(
'switch_active_poc',
args=[self.course.id.to_deprecated_string(), self.poc.id]
'switch_active_ccx',
args=[self.course.id.to_deprecated_string(), self.ccx.id]
)
response = self.client.get(switch_url)
self.assertEqual(response.status_code, 302)
self.assertTrue(response.get('Location', '').endswith(self.target_url))
self.verify_active_poc(self.client, self.poc.id)
self.verify_active_ccx(self.client, self.ccx.id)
def test_enrolled_user_can_select_mooc(self):
self.register_user_in_poc(active=True)
self.register_user_in_ccx(active=True)
self.client.login(username=self.user.username, password="test")
# pre-seed the session with the poc id
# pre-seed the session with the ccx id
session = self.client.session
session[ACTIVE_POC_KEY] = str(self.poc.id)
session[ACTIVE_CCX_KEY] = str(self.ccx.id)
session.save()
switch_url = reverse(
'switch_active_poc',
'switch_active_ccx',
args=[self.course.id.to_deprecated_string()]
)
response = self.client.get(switch_url)
self.assertEqual(response.status_code, 302)
self.assertTrue(response.get('Location', '').endswith(self.target_url))
self.verify_active_poc(self.client)
self.verify_active_ccx(self.client)
def test_unenrolled_user_cannot_select_poc(self):
def test_unenrolled_user_cannot_select_ccx(self):
self.client.login(username=self.user.username, password="test")
switch_url = reverse(
'switch_active_poc',
args=[self.course.id.to_deprecated_string(), self.poc.id]
'switch_active_ccx',
args=[self.course.id.to_deprecated_string(), self.ccx.id]
)
response = self.client.get(switch_url)
self.assertEqual(response.status_code, 302)
self.assertTrue(response.get('Location', '').endswith(self.target_url))
# if the poc were active, we'd need to pass the ID of the poc here.
self.verify_active_poc(self.client)
# if the ccx were active, we'd need to pass the ID of the ccx here.
self.verify_active_ccx(self.client)
def test_unenrolled_user_switched_to_mooc(self):
self.client.login(username=self.user.username, password="test")
# pre-seed the session with the poc id
# pre-seed the session with the ccx id
session = self.client.session
session[ACTIVE_POC_KEY] = str(self.poc.id)
session[ACTIVE_CCX_KEY] = str(self.ccx.id)
session.save()
switch_url = reverse(
'switch_active_poc',
args=[self.course.id.to_deprecated_string(), self.poc.id]
'switch_active_ccx',
args=[self.course.id.to_deprecated_string(), self.ccx.id]
)
response = self.client.get(switch_url)
self.assertEqual(response.status_code, 302)
self.assertTrue(response.get('Location', '').endswith(self.target_url))
# we tried to select the poc but are not registered, so we are switched
# we tried to select the ccx but are not registered, so we are switched
# back to the mooc view
self.verify_active_poc(self.client)
self.verify_active_ccx(self.client)
def test_unassociated_course_and_poc_not_selected(self):
def test_unassociated_course_and_ccx_not_selected(self):
new_course = CourseFactory.create()
self.client.login(username=self.user.username, password="test")
expected_url = reverse(
'course_root', args=[new_course.id.to_deprecated_string()]
)
# the poc and the course are not related.
# the ccx and the course are not related.
switch_url = reverse(
'switch_active_poc',
args=[new_course.id.to_deprecated_string(), self.poc.id]
'switch_active_ccx',
args=[new_course.id.to_deprecated_string(), self.ccx.id]
)
response = self.client.get(switch_url)
self.assertEqual(response.status_code, 302)
self.assertTrue(response.get('Location', '').endswith(expected_url))
# the mooc should be active
self.verify_active_poc(self.client)
self.verify_active_ccx(self.client)
def test_missing_poc_cannot_be_selected(self):
self.register_user_in_poc()
def test_missing_ccx_cannot_be_selected(self):
self.register_user_in_ccx()
self.client.login(username=self.user.username, password="test")
switch_url = reverse(
'switch_active_poc',
args=[self.course.id.to_deprecated_string(), self.poc.id]
'switch_active_ccx',
args=[self.course.id.to_deprecated_string(), self.ccx.id]
)
# delete the poc
self.poc.delete()
# delete the ccx
self.ccx.delete()
response = self.client.get(switch_url)
self.assertEqual(response.status_code, 302)
self.assertTrue(response.get('Location', '').endswith(self.target_url))
# we tried to select the poc it doesn't exist anymore, so we are
# we tried to select the ccx it doesn't exist anymore, so we are
# switched back to the mooc view
self.verify_active_poc(self.client)
self.verify_active_ccx(self.client)
def test_revoking_poc_membership_revokes_active_poc(self):
self.register_user_in_poc(active=True)
def test_revoking_ccx_membership_revokes_active_ccx(self):
self.register_user_in_ccx(active=True)
self.client.login(username=self.user.username, password="test")
# ensure poc is active in the request session
# ensure ccx is active in the request session
switch_url = reverse(
'switch_active_poc',
args=[self.course.id.to_deprecated_string(), self.poc.id]
'switch_active_ccx',
args=[self.course.id.to_deprecated_string(), self.ccx.id]
)
self.client.get(switch_url)
self.verify_active_poc(self.client, self.poc.id)
# unenroll the user from the poc
self.revoke_poc_registration()
# request the course root and verify that the poc is not active
self.verify_active_ccx(self.client, self.ccx.id)
# unenroll the user from the ccx
self.revoke_ccx_registration()
# request the course root and verify that the ccx is not active
self.client.get(self.target_url)
self.verify_active_poc(self.client)
self.verify_active_ccx(self.client)
def flatten(seq):
......
"""
POC Enrollment operations for use by Coach APIs.
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
from courseware.courses import get_course_by_id
from django.contrib.auth.models import User
......@@ -16,61 +16,64 @@ from xmodule.modulestore.django import modulestore
from xmodule.error_module import ErrorDescriptor
from .models import (
PocMembership,
PocFutureMembership,
CcxMembership,
CcxFutureMembership,
)
from .overrides import get_current_poc
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, poc, email):
def __init__(self, ccx, email):
exists_user = User.objects.filter(email=email).exists()
if exists_user:
user = User.objects.get(email=email)
poc_member = PocMembership.objects.filter(poc=poc, student=user)
in_poc = poc_member.exists()
ccx_member = CcxMembership.objects.filter(ccx=ccx, student=user)
in_ccx = ccx_member.exists()
full_name = user.profile.name
else:
user = None
in_poc = False
in_ccx = False
full_name = None
self.user = exists_user
self.member = user
self.full_name = full_name
self.in_poc = in_poc
self.in_ccx = in_ccx
def __repr__(self):
return "{}(user={}, member={}, in_poc={}".format(
return "{}(user={}, member={}, in_ccx={}".format(
self.__class__.__name__,
self.user,
self.member,
self.in_poc,
self.in_ccx,
)
def to_dict(self):
return {
'user': self.user,
'member': self.member,
'in_poc': self.in_poc,
'in_ccx': self.in_ccx,
}
def enroll_email(poc, student_email, auto_enroll=False, email_students=False, email_params=None):
def enroll_email(ccx, student_email, auto_enroll=False, email_students=False, email_params=None):
if email_params is None:
email_params = get_email_params(poc, True)
previous_state = EmailEnrollmentState(poc, student_email)
email_params = get_email_params(ccx, True)
previous_state = EmailEnrollmentState(ccx, student_email)
if previous_state.user:
if not previous_state.in_poc:
if not previous_state.in_ccx:
user = User.objects.get(email=student_email)
membership = PocMembership(
poc=poc, student=user, active=True
membership = CcxMembership(
ccx=ccx, student=user, active=True
)
membership.save()
elif auto_enroll:
# activate existing memberships
membership = PocMembership.objects.get(student=user, poc=poc)
membership = CcxMembership.objects.get(student=user, ccx=ccx)
membership.active = True
membership.save()
if email_students:
......@@ -79,8 +82,8 @@ def enroll_email(poc, student_email, auto_enroll=False, email_students=False, em
email_params['full_name'] = previous_state.full_name
send_mail_to_student(student_email, email_params)
else:
membership = PocFutureMembership(
poc=poc, auto_enroll=auto_enroll, email=student_email
membership = CcxFutureMembership(
ccx=ccx, auto_enroll=auto_enroll, email=student_email
)
membership.save()
if email_students:
......@@ -88,19 +91,19 @@ def enroll_email(poc, student_email, auto_enroll=False, email_students=False, em
email_params['email_address'] = student_email
send_mail_to_student(student_email, email_params)
after_state = EmailEnrollmentState(poc, student_email)
after_state = EmailEnrollmentState(ccx, student_email)
return previous_state, after_state
def unenroll_email(poc, student_email, email_students=False, email_params=None):
def unenroll_email(ccx, student_email, email_students=False, email_params=None):
if email_params is None:
email_params = get_email_params(poc, True)
previous_state = EmailEnrollmentState(poc, student_email)
email_params = get_email_params(ccx, True)
previous_state = EmailEnrollmentState(ccx, student_email)
if previous_state.in_poc:
PocMembership.objects.get(
poc=poc, student=previous_state.member
if previous_state.in_ccx:
CcxMembership.objects.get(
ccx=ccx, student=previous_state.member
).delete()
if email_students:
email_params['message'] = 'enrolled_unenroll'
......@@ -108,25 +111,25 @@ def unenroll_email(poc, student_email, email_students=False, email_params=None):
email_params['full_name'] = previous_state.full_name
send_mail_to_student(student_email, email_params)
else:
if PocFutureMembership.objects.filter(
poc=poc, email=student_email
if CcxFutureMembership.objects.filter(
ccx=ccx, email=student_email
).exists():
PocFutureMembership.objects.get(
poc=poc, email=student_email
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(poc, student_email)
after_state = EmailEnrollmentState(ccx, student_email)
return previous_state, after_state
def get_email_params(poc, auto_enroll, secure=True):
def get_email_params(ccx, auto_enroll, secure=True):
protocol = 'https' if secure else 'http'
course_id = poc.course_id
course_id = ccx.course_id
stripped_site_name = microsite.get_value(
'SITE_NAME',
......@@ -135,7 +138,7 @@ def get_email_params(poc, auto_enroll, secure=True):
registration_url = u'{proto}://{site}{path}'.format(
proto=protocol,
site=stripped_site_name,
path=reverse('student.views.register_user')
path=reverse('register_user')
)
course_url = u'{proto}://{site}{path}'.format(
proto=protocol,
......@@ -160,7 +163,7 @@ def get_email_params(poc, auto_enroll, secure=True):
email_params = {
'site_name': stripped_site_name,
'registration_url': registration_url,
'course': poc,
'course': ccx,
'auto_enroll': auto_enroll,
'course_url': course_url,
'course_about_url': course_about_url,
......@@ -184,20 +187,20 @@ def send_mail_to_student(student, param_dict):
email_template_dict = {
'allowed_enroll': (
'pocs/enroll_email_allowedsubject.txt',
'pocs/enroll_email_allowedmessage.txt'
'ccx/enroll_email_allowedsubject.txt',
'ccx/enroll_email_allowedmessage.txt'
),
'enrolled_enroll': (
'pocs/enroll_email_enrolledsubject.txt',
'pocs/enroll_email_enrolledmessage.txt'
'ccx/enroll_email_enrolledsubject.txt',
'ccx/enroll_email_enrolledmessage.txt'
),
'allowed_unenroll': (
'pocs/unenroll_email_subject.txt',
'pocs/unenroll_email_allowedmessage.txt'
'ccx/unenroll_email_subject.txt',
'ccx/unenroll_email_allowedmessage.txt'
),
'enrolled_unenroll': (
'pocs/unenroll_email_subject.txt',
'pocs/unenroll_email_enrolledmessage.txt'
'ccx/unenroll_email_subject.txt',
'ccx/unenroll_email_enrolledmessage.txt'
),
}
......@@ -226,54 +229,54 @@ def send_mail_to_student(student, param_dict):
)
def get_all_pocs_for_user(user):
"""return all POCS to which the user is registered
def get_all_ccx_for_user(user):
"""return all CCXS to which the user is registered
Returns a list of dicts: {
poc_name: <formatted title of POC course>
poc_url: <url to view this POC>
poc_active: True if this poc is currently the 'active' one
mooc_name: <formatted title of the MOOC course for this POC>
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_poc = get_current_poc()
current_active_ccx = get_current_ccx()
memberships = []
for membership in PocMembership.memberships_for_user(user):
course = get_course_by_id(membership.poc.course_id)
poc = membership.poc
poc_title = poc.display_name
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_poc',
args=[course.id.to_deprecated_string(), membership.poc.id]
'switch_active_ccx',
args=[course.id.to_deprecated_string(), membership.ccx.id]
)
mooc_url = reverse(
'switch_active_poc',
args=[course.id.to_deprecated_string(),]
'switch_active_ccx',
args=[course.id.to_deprecated_string(), ]
)
memberships.append({
'poc_name': poc_title,
'poc_url': url,
'active': membership.poc == current_active_poc,
'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_poc_membership_triplets(user, course_org_filter, org_filter_out_set):
def get_ccx_membership_triplets(user, course_org_filter, org_filter_out_set):
"""
Get the relevant set of (PersonalOnlineCourse, PocMembership, Course)
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 PocMembership.memberships_for_user(user):
poc = membership.poc
for membership in CcxMembership.memberships_for_user(user):
ccx = membership.ccx
store = modulestore()
with store.bulk_operations(poc.course_id):
course = store.get_course(poc.course_id)
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
......@@ -284,8 +287,8 @@ def get_poc_membership_triplets(user, course_org_filter, org_filter_out_set):
elif course.location.org in org_filter_out_set:
continue
yield (poc, membership, course)
yield (ccx, membership, course)
else:
log.error("User {0} enrolled in {2} course {1}".format(
user.username, poc.course_id, "broken" if course else "non-existent"
user.username, ccx.course_id, "broken" if course else "non-existent"
))
"""
Views related to the Personal Online Courses feature.
Views related to the Custom Courses feature.
"""
import csv
import datetime
......@@ -33,24 +33,24 @@ from courseware.model_data import FieldDataCache
from courseware.module_render import get_module_for_descriptor
from edxmako.shortcuts import render_to_response
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from student.roles import CoursePocCoachRole
from student.roles import CourseCcxCoachRole
from instructor.offline_gradecalc import student_grades
from instructor.views.api import _split_input_list
from instructor.views.tools import get_student_from_identifier
from .models import PersonalOnlineCourse, PocMembership
from .models import CustomCourseForEdX, CcxMembership
from .overrides import (
clear_override_for_poc,
get_override_for_poc,
override_field_for_poc,
poc_context,
clear_override_for_ccx,
get_override_for_ccx,
override_field_for_ccx,
ccx_context,
)
from .utils import (
enroll_email,
unenroll_email,
)
from pocs import ACTIVE_POC_KEY
from ccx import ACTIVE_CCX_KEY
log = logging.getLogger(__name__)
......@@ -59,7 +59,7 @@ TODAY = datetime.datetime.today # for patching in tests
def coach_dashboard(view):
"""
View decorator which enforces that the user have the POC coach role on the
View decorator which enforces that the user have the CCX coach role on the
given course and goes ahead and translates the course_id from the Django
route into a course object.
"""
......@@ -70,10 +70,10 @@ def coach_dashboard(view):
and modifying the view's call signature.
"""
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
role = CoursePocCoachRole(course_key)
role = CourseCcxCoachRole(course_key)
if not role.has_user(request.user):
return HttpResponseForbidden(
_('You must be a POC Coach to access this view.'))
_('You must be a CCX Coach to access this view.'))
course = get_course_by_id(course_key, depth=None)
return view(request, course)
return wrapper
......@@ -84,79 +84,79 @@ def coach_dashboard(view):
@coach_dashboard
def dashboard(request, course):
"""
Display the POC Coach Dashboard.
Display the CCX Coach Dashboard.
"""
poc = get_poc_for_coach(course, request.user)
ccx = get_ccx_for_coach(course, request.user)
context = {
'course': course,
'poc': poc,
'ccx': ccx,
}
if poc:
schedule = get_poc_schedule(course, poc)
grading_policy = get_override_for_poc(
poc, course, 'grading_policy', course.grading_policy)
if ccx:
schedule = get_ccx_schedule(course, ccx)
grading_policy = get_override_for_ccx(
ccx, course, 'grading_policy', course.grading_policy)
context['schedule'] = json.dumps(schedule, indent=4)
context['save_url'] = reverse(
'save_poc', kwargs={'course_id': course.id})
context['poc_members'] = PocMembership.objects.filter(poc=poc)
'save_ccx', kwargs={'course_id': course.id})
context['ccx_members'] = CcxMembership.objects.filter(ccx=ccx)
context['gradebook_url'] = reverse(
'poc_gradebook', kwargs={'course_id': course.id})
'ccx_gradebook', kwargs={'course_id': course.id})
context['grades_csv_url'] = reverse(
'poc_grades_csv', kwargs={'course_id': course.id})
'ccx_grades_csv', kwargs={'course_id': course.id})
context['grading_policy'] = json.dumps(grading_policy, indent=4)
context['grading_policy_url'] = reverse(
'poc_set_grading_policy', kwargs={'course_id': course.id})
'ccx_set_grading_policy', kwargs={'course_id': course.id})
else:
context['create_poc_url'] = reverse(
'create_poc', kwargs={'course_id': course.id})
return render_to_response('pocs/coach_dashboard.html', context)
context['create_ccx_url'] = reverse(
'create_ccx', kwargs={'course_id': course.id})
return render_to_response('ccx/coach_dashboard.html', context)
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@coach_dashboard
def create_poc(request, course):
def create_ccx(request, course):
"""
Create a new POC
Create a new CCX
"""
name = request.POST.get('name')
poc = PersonalOnlineCourse(
ccx = CustomCourseForEdX(
course_id=course.id,
coach=request.user,
display_name=name)
poc.save()
ccx.save()
# Make sure start/due are overridden for entire course
start = TODAY().replace(tzinfo=pytz.UTC)
override_field_for_poc(poc, course, 'start', start)
override_field_for_poc(poc, course, 'due', None)
override_field_for_ccx(ccx, course, 'start', start)
override_field_for_ccx(ccx, course, 'due', None)
# Hide anything that can show up in the schedule
hidden = 'visible_to_staff_only'
for chapter in course.get_children():
override_field_for_poc(poc, chapter, hidden, True)
override_field_for_ccx(ccx, chapter, hidden, True)
for sequential in chapter.get_children():
override_field_for_poc(poc, sequential, hidden, True)
override_field_for_ccx(ccx, sequential, hidden, True)
for vertical in sequential.get_children():
override_field_for_poc(poc, vertical, hidden, True)
override_field_for_ccx(ccx, vertical, hidden, True)
url = reverse('poc_coach_dashboard', kwargs={'course_id': course.id})
url = reverse('ccx_coach_dashboard', kwargs={'course_id': course.id})
return redirect(url)
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@coach_dashboard
def save_poc(request, course):
def save_ccx(request, course):
"""
Save changes to POC.
Save changes to CCX.
"""
poc = get_poc_for_coach(course, request.user)
ccx = get_ccx_for_coach(course, request.user)
def override_fields(parent, data, graded, earliest=None):
"""
Recursively apply POC schedule data to POC by overriding the
Recursively apply CCX schedule data to CCX by overriding the
`visible_to_staff_only`, `start` and `due` fields for units in the
course.
"""
......@@ -165,20 +165,20 @@ def save_poc(request, course):
for child in parent.get_children()}
for unit in data:
block = blocks[unit['location']]
override_field_for_poc(
poc, block, 'visible_to_staff_only', unit['hidden'])
override_field_for_ccx(
ccx, block, 'visible_to_staff_only', unit['hidden'])
start = parse_date(unit['start'])
if start:
if not earliest or start < earliest:
earliest = start
override_field_for_poc(poc, block, 'start', start)
override_field_for_ccx(ccx, block, 'start', start)
else:
clear_override_for_poc(poc, block, 'start')
clear_override_for_ccx(ccx, block, 'start')
due = parse_date(unit['due'])
if due:
override_field_for_poc(poc, block, 'due', due)
override_field_for_ccx(ccx, block, 'due', due)
else:
clear_override_for_poc(poc, block, 'due')
clear_override_for_ccx(ccx, block, 'due')
if not unit['hidden'] and block.graded:
graded[block.format] = graded.get(block.format, 0) + 1
......@@ -191,12 +191,12 @@ def save_poc(request, course):
graded = {}
earliest = override_fields(course, json.loads(request.body), graded)
if earliest:
override_field_for_poc(poc, course, 'start', earliest)
override_field_for_ccx(ccx, course, 'start', earliest)
# Attempt to automatically adjust grading policy
changed = False
policy = get_override_for_poc(
poc, course, 'grading_policy', course.grading_policy
policy = get_override_for_ccx(
ccx, course, 'grading_policy', course.grading_policy
)
policy = deepcopy(policy)
grader = policy['GRADER']
......@@ -206,11 +206,11 @@ def save_poc(request, course):
changed = True
section['min_count'] = count
if changed:
override_field_for_poc(poc, course, 'grading_policy', policy)
override_field_for_ccx(ccx, course, 'grading_policy', policy)
return HttpResponse(
json.dumps({
'schedule': get_poc_schedule(course, poc),
'schedule': get_ccx_schedule(course, ccx),
'grading_policy': json.dumps(policy, indent=4)}),
content_type='application/json',
)
......@@ -221,13 +221,13 @@ def save_poc(request, course):
@coach_dashboard
def set_grading_policy(request, course):
"""
Set grading policy for the POC.
Set grading policy for the CCX.
"""
poc = get_poc_for_coach(course, request.user)
override_field_for_poc(
poc, course, 'grading_policy', json.loads(request.POST['policy']))
ccx = get_ccx_for_coach(course, request.user)
override_field_for_ccx(
ccx, course, 'grading_policy', json.loads(request.POST['policy']))
url = reverse('poc_coach_dashboard', kwargs={'course_id': course.id})
url = reverse('ccx_coach_dashboard', kwargs={'course_id': course.id})
return redirect(url)
......@@ -263,36 +263,36 @@ def parse_date(datestring):
return None
def get_poc_for_coach(course, coach):
def get_ccx_for_coach(course, coach):
"""
Looks to see if user is coach of a POC for this course. Returns the POC or
Looks to see if user is coach of a CCX for this course. Returns the CCX or
None.
"""
try:
return PersonalOnlineCourse.objects.get(
return CustomCourseForEdX.objects.get(
course_id=course.id,
coach=coach)
except PersonalOnlineCourse.DoesNotExist:
except CustomCourseForEdX.DoesNotExist:
return None
def get_poc_schedule(course, poc):
def get_ccx_schedule(course, ccx):
"""
Generate a JSON serializable POC schedule.
Generate a JSON serializable CCX schedule.
"""
def visit(node, depth=1):
"""
Recursive generator function which yields POC schedule nodes.
Recursive generator function which yields CCX schedule nodes.
"""
for child in node.get_children():
start = get_override_for_poc(poc, child, 'start', None)
start = get_override_for_ccx(ccx, child, 'start', None)
if start:
start = str(start)[:-9]
due = get_override_for_poc(poc, child, 'due', None)
due = get_override_for_ccx(ccx, child, 'due', None)
if due:
due = str(due)[:-9]
hidden = get_override_for_poc(
poc, child, 'visible_to_staff_only',
hidden = get_override_for_ccx(
ccx, child, 'visible_to_staff_only',
child.visible_to_staff_only)
visited = {
'location': str(child.location),
......@@ -317,9 +317,9 @@ def get_poc_schedule(course, poc):
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@coach_dashboard
def poc_schedule(request, course):
poc = get_poc_for_coach(course, request.user)
schedule = get_poc_schedule(course, poc)
def ccx_schedule(request, course):
ccx = get_ccx_for_coach(course, request.user)
schedule = get_ccx_schedule(course, ccx)
json_schedule = json.dumps(schedule, indent=4)
return HttpResponse(json_schedule, mimetype='application/json')
......@@ -327,11 +327,11 @@ def poc_schedule(request, course):
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@coach_dashboard
def poc_invite(request, course):
def ccx_invite(request, course):
"""
Invite users to new poc
Invite users to new ccx
"""
poc = get_poc_for_coach(course, request.user)
ccx = get_ccx_for_coach(course, request.user)
action = request.POST.get('enrollment-button')
identifiers_raw = request.POST.get('student-ids')
identifiers = _split_input_list(identifiers_raw)
......@@ -350,26 +350,26 @@ def poc_invite(request, course):
validate_email(email)
if action == 'Enroll':
enroll_email(
poc,
ccx,
email,
auto_enroll=auto_enroll,
email_students=email_students
)
if action == "Unenroll":
unenroll_email(poc, email, email_students=email_students)
unenroll_email(ccx, email, email_students=email_students)
except ValidationError:
pass # maybe log this?
url = reverse('poc_coach_dashboard', kwargs={'course_id': course.id})
url = reverse('ccx_coach_dashboard', kwargs={'course_id': course.id})
return redirect(url)
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@coach_dashboard
def poc_student_management(request, course):
"""Manage the enrollment of individual students in a POC
def ccx_student_management(request, course):
"""Manage the enrollment of individual students in a CCX
"""
poc = get_poc_for_coach(course, request.user)
ccx = get_ccx_for_coach(course, request.user)
action = request.POST.get('student-action', None)
student_id = request.POST.get('student-id', '')
user = email = None
......@@ -385,21 +385,21 @@ def poc_student_management(request, course):
if action == 'add':
# by decree, no emails sent to students added this way
# by decree, any students added this way are auto_enrolled
enroll_email(poc, email, auto_enroll=True, email_students=False)
enroll_email(ccx, email, auto_enroll=True, email_students=False)
elif action == 'revoke':
unenroll_email(poc, email, email_students=False)
unenroll_email(ccx, email, email_students=False)
except ValidationError:
pass # XXX: log, report?
url = reverse('poc_coach_dashboard', kwargs={'course_id': course.id})
url = reverse('ccx_coach_dashboard', kwargs={'course_id': course.id})
return redirect(url)
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@coach_dashboard
def poc_gradebook(request, course):
def ccx_gradebook(request, course):
"""
Show the gradebook for this POC.
Show the gradebook for this CCX.
"""
# Need course module for overrides to function properly
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
......@@ -407,16 +407,16 @@ def poc_gradebook(request, course):
course = get_module_for_descriptor(
request.user, request, course, field_data_cache, course.id)
poc = get_poc_for_coach(course, request.user)
with poc_context(poc):
ccx = get_ccx_for_coach(course, request.user)
with ccx_context(ccx):
# The grading policy for the MOOC is probably already cached. We need
# to make sure we have the POC grading policy loaded.
# to make sure we have the CCX grading policy loaded.
course._field_data_cache = {} # pylint: disable=protected-access
course.set_grading_policy(course.grading_policy)
enrolled_students = User.objects.filter(
pocmembership__poc=poc,
pocmembership__active=1
ccxmembership__ccx=ccx,
ccxmembership__active=1
).order_by('username').select_related("profile")
student_info = [
......@@ -442,7 +442,7 @@ def poc_gradebook(request, course):
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@coach_dashboard
def poc_grades_csv(request, course):
def ccx_grades_csv(request, course):
"""
Download grades as CSV.
"""
......@@ -451,16 +451,16 @@ def poc_grades_csv(request, course):
course.id, request.user, course, depth=2)
course = get_module_for_descriptor(
request.user, request, course, field_data_cache, course.id)
poc = get_poc_for_coach(course, request.user)
with poc_context(poc):
ccx = get_ccx_for_coach(course, request.user)
with ccx_context(ccx):
# The grading policy for the MOOC is probably already cached. We need
# to make sure we have the POC grading policy loaded.
# to make sure we have the CCX grading policy loaded.
course._field_data_cache = {} # pylint: disable=protected-access
course.set_grading_policy(course.grading_policy)
enrolled_students = User.objects.filter(
pocmembership__poc=poc,
pocmembership__active=1
ccxmembership__ccx=ccx,
ccxmembership__active=1
).order_by('username').select_related("profile")
grades = iterate_grades_for(course, enrolled_students)
......@@ -496,8 +496,8 @@ def poc_grades_csv(request, course):
@login_required
def swich_active_poc(request, course_id, poc_id=None):
"""set the active POC for the logged-in user
def switch_active_ccx(request, course_id, ccx_id=None):
"""set the active CCX for the logged-in user
"""
user = request.user
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
......@@ -506,21 +506,21 @@ def swich_active_poc(request, course_id, poc_id=None):
course_url = reverse(
'course_root', args=[course.id.to_deprecated_string()]
)
if poc_id is not None:
if ccx_id is not None:
try:
requested_poc = PersonalOnlineCourse.objects.get(pk=poc_id)
assert requested_poc.course_id.to_deprecated_string() == course_id
if not PocMembership.objects.filter(
poc=requested_poc, student=request.user, active=True
requested_ccx = CustomCourseForEdX.objects.get(pk=ccx_id)
assert requested_ccx.course_id.to_deprecated_string() == course_id
if not CcxMembership.objects.filter(
ccx=requested_ccx, student=request.user, active=True
).exists():
poc_id = None
except PersonalOnlineCourse.DoesNotExist:
ccx_id = None
except CustomCourseForEdX.DoesNotExist:
# what to do here? Log the failure? Do we care?
poc_id = None
ccx_id = None
except AssertionError:
# what to do here? Log the failure? Do we care?
poc_id = None
ccx_id = None
request.session[ACTIVE_POC_KEY] = poc_id
request.session[ACTIVE_CCX_KEY] = ccx_id
return HttpResponseRedirect(course_url)
......@@ -248,7 +248,7 @@ def _grade(student, request, course, keep_raw_scores):
totaled_scores[section_format] = format_scores
# Grading policy might be overriden by a POC, need to reset it
# Grading policy might be overriden by a CCX, need to reset it
course.set_grading_policy(course.grading_policy)
grade_summary = course.grader.grade(totaled_scores, generate_random_scores=settings.GENERATE_PROFILE_SCORES)
......
......@@ -15,7 +15,7 @@ from django_comment_common.models import Role
from student.roles import (
CourseBetaTesterRole,
CourseInstructorRole,
CoursePocCoachRole,
CourseCcxCoachRole,
CourseStaffRole,
)
......@@ -26,7 +26,7 @@ ROLES = {
'beta': CourseBetaTesterRole,
'instructor': CourseInstructorRole,
'staff': CourseStaffRole,
'poc_coach': CoursePocCoachRole,
'ccx_coach': CourseCcxCoachRole,
}
......
......@@ -679,7 +679,7 @@ def bulk_beta_modify_access(request, course_id):
@common_exceptions_400
@require_query_params(
unique_student_identifier="email or username of user to change access",
rolename="'instructor', 'staff', 'beta', or 'poc_coach'",
rolename="'instructor', 'staff', 'beta', or 'ccx_coach'",
action="'allow' or 'revoke'"
)
def modify_access(request, course_id):
......@@ -691,7 +691,7 @@ def modify_access(request, course_id):
Query parameters:
unique_student_identifer is the target user's username or email
rolename is one of ['instructor', 'staff', 'beta', 'poc_coach']
rolename is one of ['instructor', 'staff', 'beta', 'ccx_coach']
action is one of ['allow', 'revoke']
"""
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
......@@ -762,7 +762,7 @@ def list_course_role_members(request, course_id):
List instructors and staff.
Requires instructor access.
rolename is one of ['instructor', 'staff', 'beta', 'poc_coach']
rolename is one of ['instructor', 'staff', 'beta', 'ccx_coach']
Returns JSON of the form {
"course_id": "some/course/id",
......
# -*- 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 'PocFutureMembership'
db.create_table('pocs_pocfuturemembership', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('poc', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['pocs.PersonalOnlineCourse'])),
('email', self.gf('django.db.models.fields.CharField')(max_length=255)),
))
db.send_create_signal('pocs', ['PocFutureMembership'])
# Adding field 'PocMembership.active'
db.add_column('pocs_pocmembership', 'active',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=False)
def backwards(self, orm):
# Deleting model 'PocFutureMembership'
db.delete_table('pocs_pocfuturemembership')
# Deleting field 'PocMembership.active'
db.delete_column('pocs_pocmembership', 'active')
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'})
},
'pocs.personalonlinecourse': {
'Meta': {'object_name': 'PersonalOnlineCourse'},
'coach': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
'display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'pocs.pocfieldoverride': {
'Meta': {'unique_together': "(('poc', 'location', 'field'),)", 'object_name': 'PocFieldOverride'},
'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'location': ('xmodule_django.models.LocationKeyField', [], {'max_length': '255', 'db_index': 'True'}),
'poc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pocs.PersonalOnlineCourse']"}),
'value': ('django.db.models.fields.TextField', [], {'default': "'null'"})
},
'pocs.pocfuturemembership': {
'Meta': {'object_name': 'PocFutureMembership'},
'email': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'poc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pocs.PersonalOnlineCourse']"})
},
'pocs.pocmembership': {
'Meta': {'object_name': 'PocMembership'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'poc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pocs.PersonalOnlineCourse']"}),
'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
}
}
complete_apps = ['pocs']
\ No newline at end of file
# -*- 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 field 'PocFutureMembership.auto_enroll'
db.add_column('pocs_pocfuturemembership', 'auto_enroll',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=False)
def backwards(self, orm):
# Deleting field 'PocFutureMembership.auto_enroll'
db.delete_column('pocs_pocfuturemembership', 'auto_enroll')
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'})
},
'pocs.personalonlinecourse': {
'Meta': {'object_name': 'PersonalOnlineCourse'},
'coach': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
'display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'pocs.pocfieldoverride': {
'Meta': {'unique_together': "(('poc', 'location', 'field'),)", 'object_name': 'PocFieldOverride'},
'field': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'location': ('xmodule_django.models.LocationKeyField', [], {'max_length': '255', 'db_index': 'True'}),
'poc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pocs.PersonalOnlineCourse']"}),
'value': ('django.db.models.fields.TextField', [], {'default': "'null'"})
},
'pocs.pocfuturemembership': {
'Meta': {'object_name': 'PocFutureMembership'},
'auto_enroll': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'email': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'poc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pocs.PersonalOnlineCourse']"})
},
'pocs.pocmembership': {
'Meta': {'object_name': 'PocMembership'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'poc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['pocs.PersonalOnlineCourse']"}),
'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
}
}
complete_apps = ['pocs']
\ No newline at end of file
from factory.django import DjangoModelFactory
from pocs.models import PersonalOnlineCourse
from pocs.models import PocMembership
from pocs.models import PocFutureMembership
class PocFactory(DjangoModelFactory):
FACTORY_FOR = PersonalOnlineCourse
display_name = "Test POC"
class PocMembershipFactory(DjangoModelFactory):
FACTORY_FOR = PocMembership
active = False
class PocFutureMembershipFactory(DjangoModelFactory):
FACTORY_FOR = PocFutureMembership
......@@ -577,12 +577,12 @@ ECOMMERCE_API_URL = ENV_TOKENS.get('ECOMMERCE_API_URL', ECOMMERCE_API_URL)
ECOMMERCE_API_SIGNING_KEY = AUTH_TOKENS.get('ECOMMERCE_API_SIGNING_KEY', ECOMMERCE_API_SIGNING_KEY)
ECOMMERCE_API_TIMEOUT = ENV_TOKENS.get('ECOMMERCE_API_TIMEOUT', ECOMMERCE_API_TIMEOUT)
##### Personal Online Courses #####
if FEATURES.get('PERSONAL_ONLINE_COURSES'):
INSTALLED_APPS += ('pocs',)
MIDDLEWARE_CLASSES += ('pocs.overrides.PocMiddleware',)
##### Custom Courses for EdX #####
if FEATURES.get('CUSTOM_COURSES_EDX'):
INSTALLED_APPS += ('ccx',)
MIDDLEWARE_CLASSES += ('ccx.overrides.CcxMiddleware',)
FIELD_OVERRIDE_PROVIDERS += (
'pocs.overrides.PersonalOnlineCoursesOverrideProvider',
'ccx.overrides.CustomCoursesForEdxOverrideProvider',
)
##### Individual Due Date Extensions #####
......
......@@ -214,8 +214,8 @@ FEATURES = {
# True.
'INDIVIDUAL_DUE_DATES': False,
# Enable Personal Online Courses
'PERSONAL_ONLINE_COURSES': False,
# Enable Custom Courses for EdX
'CUSTOM_COURSES_EDX': False,
# Enable legacy instructor dashboard
'ENABLE_INSTRUCTOR_LEGACY_DASHBOARD': True,
......@@ -1203,7 +1203,8 @@ reverify_js = [
'js/verify_student/incourse_reverify.js',
]
pocs_js = sorted(rooted_glob(PROJECT_ROOT / 'static', 'js/pocs/**/*.js'))
ccx_js = sorted(rooted_glob(PROJECT_ROOT / 'static', 'js/ccx/**/*.js'))
PIPELINE_CSS = {
'style-vendor': {
......@@ -1399,9 +1400,9 @@ PIPELINE_JS = {
'source_filenames': reverify_js,
'output_filename': 'js/reverify.js'
},
'pocs': {
'source_filenames': pocs_js,
'output_filename': 'js/pocs.js'
'ccx': {
'source_filenames': ccx_js,
'output_filename': 'js/ccx.js'
}
}
......
......@@ -469,6 +469,6 @@ FACEBOOK_API_VERSION = "v2.2"
# Certificates Views
FEATURES['CERTIFICATES_HTML_VIEW'] = True
######### personal online courses #########
INSTALLED_APPS += ('pocs',)
MIDDLEWARE_CLASSES += ('pocs.overrides.PocMiddleware',)
######### custom courses #########
INSTALLED_APPS += ('ccx',)
MIDDLEWARE_CLASSES += ('ccx.overrides.CcxMiddleware',)
......@@ -3,18 +3,18 @@ var edx = edx || {};
(function($, _, Backbone, gettext) {
'use strict';
edx.pocs = edx.pocs || {};
edx.pocs.schedule = edx.pocs.schedule || {};
edx.ccx = edx.ccx || {};
edx.ccx.schedule = edx.ccx.schedule || {};
var syncErrorMessage = gettext("The data could not be saved.");
var self;
edx.pocs.schedule.reloadPage = function() {
edx.ccx.schedule.reloadPage = function() {
location.reload();
};
edx.pocs.schedule.UnitModel = Backbone.Model.extend({
edx.ccx.schedule.UnitModel = Backbone.Model.extend({
defaults: {
location: '',
display_name: '',
......@@ -27,18 +27,18 @@ var edx = edx || {};
});
edx.pocs.schedule.Schedule = Backbone.Collection.extend({
edx.ccx.schedule.Schedule = Backbone.Collection.extend({
model: edx.pocs.schedule.UnitModel,
url: 'poc_schedule'
model: edx.ccx.schedule.UnitModel,
url: 'ccx_schedule'
});
edx.pocs.schedule.ScheduleView = Backbone.View.extend({
edx.ccx.schedule.ScheduleView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'render');
this.schedule_collection = new edx.pocs.schedule.Schedule();
this.schedule_collection = new edx.ccx.schedule.Schedule();
this.schedule = {};
this.schedule_collection.bind('reset', this.render);
this.schedule_collection.fetch({reset: true});
......@@ -63,23 +63,23 @@ var edx = edx || {};
this.showing = this.pruned(self.schedule, function(node) {
return !node.hidden});
this.$el.html(schedule_template({chapters: this.showing}));
$('table.poc-schedule .sequential,.vertical').hide();
$('table.poc-schedule .toggle-collapse').on('click', this.toggle_collapse);
$('table.ccx-schedule .sequential,.vertical').hide();
$('table.ccx-schedule .toggle-collapse').on('click', this.toggle_collapse);
//
// Hidden hover fields for empty date fields
$('table.poc-schedule .date a').each(function() {
$('table.ccx-schedule .date a').each(function() {
if (! $(this).text()) {
$(this).text('Set date').addClass('empty');
}
});
// Handle date edit clicks
$('table.poc-schedule .date a').attr('href', '#enter-date-modal')
$('table.ccx-schedule .date a').attr('href', '#enter-date-modal')
.leanModal({closeButton: '.close-modal'});
$('table.poc-schedule .due-date a').on('click', this.enterNewDate('due'));
$('table.poc-schedule .start-date a').on('click', this.enterNewDate('start'));
$('table.ccx-schedule .due-date a').on('click', this.enterNewDate('due'));
$('table.ccx-schedule .start-date a').on('click', this.enterNewDate('start'));
// Click handler for remove all
$('table.poc-schedule a#remove-all').on('click', function(event) {
$('table.ccx-schedule a#remove-all').on('click', function(event) {
event.preventDefault();
self.schedule_apply(self.schedule, self.hide);
self.dirty = true;
......@@ -175,7 +175,7 @@ var edx = edx || {};
});
// Remove unit handler
$('table.poc-schedule a.remove-unit').on('click', function(event) {
$('table.ccx-schedule a.remove-unit').on('click', function(event) {
var row = $(this).closest('tr'),
path = row.data('location').split(' '),
unit = self.find_unit(self.schedule, path[0], path[1], path[2]);
......
......@@ -77,8 +77,8 @@
@import "course/instructor/email";
@import "xmodule/descriptors/css/module-styles.scss";
// course - poc_coach
@import "course/poc_coach/dashboard";
// course - ccx_coach
@import "course/ccx_coach/dashboard";
// discussion
@import "course/discussion/form-wmd-toolbar";
.poc-schedule-container {
.ccx-schedule-container {
float: left;
width: 750px;
}
table.poc-schedule {
table.ccx-schedule {
width: 100%;
thead {
......@@ -34,19 +34,19 @@ table.poc-schedule {
}
}
.poc-schedule-sidebar {
.ccx-schedule-sidebar {
float: left;
width: 295px;
margin-left: 20px;
}
.poc-sidebar-panel {
.ccx-sidebar-panel {
border: 1px solid #cbcbcb;
padding: 15px;
margin-bottom: 20px;
}
form.poc-form {
form.ccx-form {
line-height: 1.5;
select {
width: 100%;
......
<%page args="poc, membership, course" />
<%page args="ccx, membership, course" />
<%! from django.utils.translation import ugettext as _ %>
<%!
......@@ -6,24 +6,24 @@
from courseware.courses import course_image_url, get_course_about_section
%>
<%
poc_switch_target = reverse('switch_active_poc', args=[course.id.to_deprecated_string(), poc.id])
ccx_switch_target = reverse('switch_active_ccx', args=[course.id.to_deprecated_string(), ccx.id])
%>
<li class="course-item">
<article class="course">
<a href="${poc_switch_target}" class="cover">
<img src="${course_image_url(course)}" alt="${_('{course_number} {poc_name} Cover Image').format(course_number=course.number, poc_name=poc.display_name) |h}" />
<a href="${ccx_switch_target}" class="cover">
<img src="${course_image_url(course)}" alt="${_('{course_number} {ccx_name} Cover Image').format(course_number=course.number, ccx_name=ccx.display_name) |h}" />
</a>
<section class="info">
<hgroup>
<p class="date-block">
Personal Online Course
Custom Course
</p>
<h2 class="university">${get_course_about_section(course, 'university')}</h2>
<h3>
<a href="${poc_switch_target}">${course.display_number_with_default | h} ${poc.display_name}</a>
<a href="${ccx_switch_target}">${course.display_number_with_default | h} ${ccx.display_name}</a>
</h3>
</hgroup>
<a href="${poc_switch_target}" class="enter-course">${_('View Course')}</a>
<a href="${ccx_switch_target}" class="enter-course">${_('View Course')}</a>
</section>
</article>
</li>
......@@ -4,8 +4,8 @@
<%inherit file="/main.html" />
<%namespace name='static' file='/static_content.html'/>
<%block name="pagetitle">${_("POC Coach Dashboard")}</%block>
<%block name="nav_skip">#poc-coach-dashboard-content</%block>
<%block name="pagetitle">${_("CCX Coach Dashboard")}</%block>
<%block name="nav_skip">#ccx-coach-dashboard-content</%block>
<%block name="headextra">
<%static:css group='style-course-vendor'/>
......@@ -14,24 +14,24 @@
<%static:css group='style-course'/>
</%block>
<%include file="/courseware/course_navigation.html" args="active_page='poc_coach'" />
<%include file="/courseware/course_navigation.html" args="active_page='ccx_coach'" />
<section class="container">
<div class="instructor-dashboard-wrapper-2">
<section class="instructor-dashboard-content-2" id="poc-coach-dashboard-content">
<h1>${_("POC Coach Dashboard")}</h1>
<section class="instructor-dashboard-content-2" id="ccx-coach-dashboard-content">
<h1>${_("CCX Coach Dashboard")}</h1>
%if not poc:
%if not ccx:
<section>
<form action="${create_poc_url}" method="POST">
<form action="${create_ccx_url}" method="POST">
<input type="hidden" name="csrfmiddlewaretoken" value="${csrf_token}"/>
<input name="name" placeholder="Name your POC"/><br/>
<button id="create-poc">Coach a new Personal Online Course</button>
<input name="name" placeholder="Name your CCX"/><br/>
<button id="create-ccx">Coach a new Custom Course for EdX</button>
</form>
</section>
%endif
%if poc:
%if ccx:
<ul class="instructor-nav">
<li class="nav-item">
<a href="#" data-section="membership">${_("Enrollment")}</a>
......
<%! from django.utils.translation import ugettext as _ %>
<div class="batch-enrollment" style="float:left;width:50%">
<form method="POST" action="poc_invite">
<form method="POST" action="ccx_invite">
<input type="hidden" name="csrfmiddlewaretoken" value="${ csrf_token }">
<h2> ${_("Batch Enrollment")} </h2>
<p>
......@@ -46,7 +46,7 @@
</div>
<div class="member-lists-management" style="float:left;width:50%">
<form method="POST" action="poc_manage_student">
<form method="POST" action="ccx_manage_student">
<input type="hidden" name="csrfmiddlewaretoken" value="${ csrf_token }">
<div class="auth-list-container active">
<div class="member-list-widget">
......@@ -61,7 +61,7 @@
</tr>
</thead>
<tbody>
%for member in poc_members:
%for member in ccx_members:
<tr>
<td>${member.student}</td>
<td>${member.student.email}</td>
......
......@@ -15,18 +15,18 @@
.ui-datepicker { z-index: 100000 !important; }
input.date, input.time { width: auto !important; display: inline !important; }
</style>
<%static:js group='pocs'/>
<%static:js group='ccx'/>
</%block>
%for template_name in ["schedule"]:
<script type="text/template" id="poc-${template_name}-template">
<%static:include path="pocs/${template_name}.underscore" />
<script type="text/template" id="ccx-${template_name}-template">
<%static:include path="ccx/${template_name}.underscore" />
</script>
%endfor
<div class="poc-schedule-container">
<div id="poc-schedule"></div>
<div id="new-poc-schedule"></div>
<div class="ccx-schedule-container">
<div id="ccx-schedule"></div>
<div id="new-ccx-schedule"></div>
</div>
<section id="enter-date-modal" class="modal" aria-hidden="true">
......@@ -53,8 +53,8 @@
</div>
</section>
<div class="poc-schedule-sidebar">
<div class="poc-sidebar-panel" id="dirty-schedule">
<div class="ccx-schedule-sidebar">
<div class="ccx-sidebar-panel" id="dirty-schedule">
<h2>${_('Save changes')}</h2>
<form role="form">
<p>${_("You have unsaved changes.")}</p>
......@@ -64,13 +64,13 @@
</div>
</form>
</div>
<div class="poc-sidebar-panel" id="ajax-error">
<div class="ccx-sidebar-panel" id="ajax-error">
<h2>${_('Error')}</h2>
<p>${_("There was an error saving changes.")}</p>
</div>
<div class="poc-sidebar-panel">
<div class="ccx-sidebar-panel">
<h2>${_('Schedule a Unit')}</h2>
<form role="form" id="add-unit" name="add-unit" class="poc-form">
<form role="form" id="add-unit" name="add-unit" class="ccx-form">
<div class="field">
<b>${_('Section')}</b><br/>
<select name="chapter"></select>
......@@ -110,12 +110,12 @@
<script>
$(function() {
schedule_template = _.template($('#poc-schedule-template').html());
var view = new edx.pocs.schedule.ScheduleView({
el: $('#new-poc-schedule')
schedule_template = _.template($('#ccx-schedule-template').html());
var view = new edx.ccx.schedule.ScheduleView({
el: $('#new-ccx-schedule')
});
view.render();
//poc_schedule.render();
//ccx_schedule.render();
$('.datepair .time').timepicker({
'showDuration': true,
'timeFormat': 'G:i'
......
<table class="poc-schedule">
<table class="ccx-schedule">
<thead>
<tr>
<th><%- gettext('Unit') %></th>
......@@ -13,7 +13,7 @@
<% _.each(chapters, function(chapter) { %>
<tr class="chapter collapsed" data-location="<%= chapter.location %>" data-depth="1">
<td class="unit">
<a href="#"><i class="icon-caret-right icon toggle-collapse"></i></a>
<a href="#"><i class="icon-caret-right icon toggle-collapse"></i></a>
<%= chapter.display_name %>
</td>
<td class="date start-date"><a><%= chapter.start %></a></td>
......@@ -23,10 +23,10 @@
</a></td>
</tr>
<% _.each(chapter.children, function(child) { %>
<tr class="sequential collapsed" data-depth="2"
<tr class="sequential collapsed" data-depth="2"
data-location="<%= chapter.location %> <%= child.location %>">
<td class="unit">
<a href="#"><i class="icon-caret-right icon toggle-collapse"></i></a>
<a href="#"><i class="icon-caret-right icon toggle-collapse"></i></a>
<%= child.display_name %>
</td>
<td class="date start-date"><a><%= child.start %></a></td>
......
......@@ -243,9 +243,9 @@
<%include file='dashboard/_dashboard_course_listing.html' args="course=course, enrollment=enrollment, show_courseware_link=show_courseware_link, cert_status=cert_status, show_email_settings=show_email_settings, course_mode_info=course_mode_info, show_refund_option = show_refund_option, is_paid_course = is_paid_course, is_course_blocked = is_course_blocked, verification_status=course_verification_status, course_requirements=course_requirements" />
% endfor
% if settings.FEATURES.get('PERSONAL_ONLINE_COURSES', False):
% for poc, membership, course in poc_membership_triplets:
<%include file='pocs/_dashboard_poc_listing.html' args="poc=poc, membership=membership, course=course" />
% if settings.FEATURES.get('CUSTOM_COURSES_EDX', False):
% for ccx, membership, course in ccx_membership_triplets:
<%include file='ccx/_dashboard_ccx_listing.html' args="ccx=ccx, membership=membership, course=course" />
% endfor
% endif
......
......@@ -244,17 +244,17 @@
></div>
%endif
%if section_data['access']['instructor'] and settings.FEATURES.get('PERSONAL_ONLINE_COURSES', False):
%if section_data['access']['instructor'] and settings.FEATURES.get('CUSTOM_COURSES_EDX', False):
<div class="auth-list-container"
data-rolename="poc_coach"
data-display-name="${_("POC Coaches")}"
data-rolename="ccx_coach"
data-display-name="${_("CCX Coaches")}"
data-info-text="
${_("POC Coaches are able to create their own Personal Online Courses "
${_("CCX Coaches are able to create their own Custom Courses "
"based on this course, which they can use to provide personalized "
"instruction to their own students based in this course material.")}"
data-list-endpoint="${section_data['list_course_role_members_url']}"
data-modify-endpoint="${section_data['modify_access_url']}"
data-add-button-label="${_("Add POC Coach")}"
data-add-button-label="${_("Add CCX Coach")}"
></div>
%endif
</div>
......@@ -13,7 +13,7 @@ from status.status import get_site_status_msg
<%! from microsite_configuration import microsite %>
<%! from microsite_configuration.templatetags.microsite import platform_name %>
<%! from pocs.overrides import get_current_poc %>
<%! from ccx.overrides import get_current_ccx %>
## Provide a hook for themes to inject branding on top.
<%block name="navigation_top" />
......@@ -52,10 +52,10 @@ site_status_msg = get_site_status_msg(course_id)
${course.display_number_with_default | h}
<%
display_name = course.display_name_with_default
if settings.FEATURES.get('PERSONAL_ONLINE_COURSES', False):
poc = get_current_poc()
if poc:
display_name = poc.display_name
if settings.FEATURES.get('CUSTOM_COURSES_EDX', False):
ccx = get_current_ccx()
if ccx:
display_name = ccx.display_name
%>
${display_name}</h2>
% endif
......
......@@ -343,26 +343,26 @@ if settings.COURSEWARE_ENABLED:
# For the instructor
url(r'^courses/{}/instructor$'.format(settings.COURSE_ID_PATTERN),
'instructor.views.instructor_dashboard.instructor_dashboard_2', name="instructor_dashboard"),
url(r'^courses/{}/poc_coach$'.format(settings.COURSE_ID_PATTERN),
'pocs.views.dashboard', name='poc_coach_dashboard'),
url(r'^courses/{}/create_poc$'.format(settings.COURSE_ID_PATTERN),
'pocs.views.create_poc', name='create_poc'),
url(r'^courses/{}/save_poc$'.format(settings.COURSE_ID_PATTERN),
'pocs.views.save_poc', name='save_poc'),
url(r'^courses/{}/poc_invite$'.format(settings.COURSE_ID_PATTERN),
'pocs.views.poc_invite', name='poc_invite'),
url(r'^courses/{}/poc_schedule$'.format(settings.COURSE_ID_PATTERN),
'pocs.views.poc_schedule', name='poc_schedule'),
url(r'^courses/{}/poc_manage_student$'.format(settings.COURSE_ID_PATTERN),
'pocs.views.poc_student_management', name='poc_manage_student'),
url(r'^courses/{}/poc_gradebook$'.format(settings.COURSE_ID_PATTERN),
'pocs.views.poc_gradebook', name='poc_gradebook'),
url(r'^courses/{}/poc_grades.csv$'.format(settings.COURSE_ID_PATTERN),
'pocs.views.poc_grades_csv', name='poc_grades_csv'),
url(r'^courses/{}/poc_set_grading_policy$'.format(settings.COURSE_ID_PATTERN),
'pocs.views.set_grading_policy', name='poc_set_grading_policy'),
url(r'^courses/{}/swich_poc(?:/(?P<poc_id>[\d]+))?$'.format(settings.COURSE_ID_PATTERN),
'pocs.views.swich_active_poc', name='switch_active_poc'),
url(r'^courses/{}/ccx_coach$'.format(settings.COURSE_ID_PATTERN),
'ccx.views.dashboard', name='ccx_coach_dashboard'),
url(r'^courses/{}/create_ccx$'.format(settings.COURSE_ID_PATTERN),
'ccx.views.create_ccx', name='create_ccx'),
url(r'^courses/{}/save_ccx$'.format(settings.COURSE_ID_PATTERN),
'ccx.views.save_ccx', name='save_ccx'),
url(r'^courses/{}/ccx_invite$'.format(settings.COURSE_ID_PATTERN),
'ccx.views.ccx_invite', name='ccx_invite'),
url(r'^courses/{}/ccx_schedule$'.format(settings.COURSE_ID_PATTERN),
'ccx.views.ccx_schedule', name='ccx_schedule'),
url(r'^courses/{}/ccx_manage_student$'.format(settings.COURSE_ID_PATTERN),
'ccx.views.ccx_student_management', name='ccx_manage_student'),
url(r'^courses/{}/ccx_gradebook$'.format(settings.COURSE_ID_PATTERN),
'ccx.views.ccx_gradebook', name='ccx_gradebook'),
url(r'^courses/{}/ccx_grades.csv$'.format(settings.COURSE_ID_PATTERN),
'ccx.views.ccx_grades_csv', name='ccx_grades_csv'),
url(r'^courses/{}/ccx_set_grading_policy$'.format(settings.COURSE_ID_PATTERN),
'ccx.views.set_grading_policy', name='ccx_set_grading_policy'),
url(r'^courses/{}/switch_ccx(?:/(?P<ccx_id>[\d]+))?$'.format(settings.COURSE_ID_PATTERN),
'ccx.views.switch_active_ccx', name='switch_active_ccx'),
url(r'^courses/{}/set_course_mode_price$'.format(settings.COURSE_ID_PATTERN),
'instructor.views.instructor_dashboard.set_course_mode_price', name="set_course_mode_price"),
url(r'^courses/{}/instructor/api/'.format(settings.COURSE_ID_PATTERN),
......
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