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):
......
......@@ -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