def edit_course_view(request, course, ccx, **kwargs): context = { 'course': course, 'ccx': ccx, 'delivery_mode_choices': CustomCourseForEdX.DELIVERY_MODE_CHOICES, 'enrollment_choices': CustomCourseForEdX.ENROLLMENT_TYPE_CHOICES, } context.update(get_ccx_creation_dict(course)) context.update(edit_ccx_context(course, ccx, request.user)) context['edit_current'] = True return render_to_response('ccx/coach_dashboard.html', context)
def dashboard(request, course, ccx=None): """ Display the CCX Coach Dashboard. """ # right now, we can only have one ccx per user and course # so, if no ccx is passed in, we can sefely redirect to that if ccx is None: ccx = get_ccx_for_coach(course, request.user) if ccx: url = reverse('ccx_coach_dashboard', kwargs={ 'course_id': CCXLocator.from_course_locator( course.id, ccx.id) }) return redirect(url) context = { 'course': course, 'ccx': ccx, } context.update(get_ccx_creation_dict(course)) if ccx: ccx_locator = CCXLocator.from_course_locator(course.id, unicode(ccx.id)) # At this point we are done with verification that current user is ccx coach. assign_coach_role_to_ccx(ccx_locator, request.user, course.id) 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_ccx', kwargs={'course_id': ccx_locator}) context['ccx_members'] = CourseEnrollment.objects.filter( course_id=ccx_locator, is_active=True) context['gradebook_url'] = reverse('ccx_gradebook', kwargs={'course_id': ccx_locator}) context['grades_csv_url'] = reverse('ccx_grades_csv', kwargs={'course_id': ccx_locator}) context['grading_policy'] = json.dumps(grading_policy, indent=4) context['grading_policy_url'] = reverse( 'ccx_set_grading_policy', kwargs={'course_id': ccx_locator}) with ccx_course(ccx_locator) as course: context['course'] = course else: context['create_ccx_url'] = reverse('create_ccx', kwargs={'course_id': course.id}) return render_to_response('ccx/coach_dashboard.html', context)
def dashboard(request, course, ccx=None): """ Display the CCX Coach Dashboard. """ # right now, we can only have one ccx per user and course # so, if no ccx is passed in, we can sefely redirect to that if ccx is None: ccx = get_ccx_for_coach(course, request.user) if ccx: url = reverse( 'ccx_coach_dashboard', kwargs={ 'course_id': CCXLocator.from_course_locator(course.id, unicode(ccx.id)) }) return redirect(url) context = { 'course': course, 'ccx': ccx, } context.update(get_ccx_creation_dict(course)) if ccx: ccx_locator = CCXLocator.from_course_locator(course.id, unicode(ccx.id)) # At this point we are done with verification that current user is ccx coach. assign_staff_role_to_ccx(ccx_locator, request.user, course.id) 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_ccx', kwargs={'course_id': ccx_locator}) context['ccx_members'] = CourseEnrollment.objects.filter( course_id=ccx_locator, is_active=True) context['gradebook_url'] = reverse( 'ccx_gradebook', kwargs={'course_id': ccx_locator}) context['grades_csv_url'] = reverse( 'ccx_grades_csv', kwargs={'course_id': ccx_locator}) context['grading_policy'] = json.dumps(grading_policy, indent=4) context['grading_policy_url'] = reverse( 'ccx_set_grading_policy', kwargs={'course_id': ccx_locator}) with ccx_course(ccx_locator) as course: context['course'] = course else: context['create_ccx_url'] = reverse( 'create_ccx', kwargs={'course_id': course.id}) return render_to_response('ccx/coach_dashboard.html', context)
def create_ccx(request, course, ccx=None): """ Create a new CCX """ name = request.POST.get('name') if hasattr(course, 'ccx_connector') and course.ccx_connector: # if ccx connector url is set in course settings then inform user that he can # only create ccx by using ccx connector url. context = get_ccx_creation_dict(course) messages.error(request, context['use_ccx_con_error_message']) return render_to_response('ccx/coach_dashboard.html', context) # prevent CCX objects from being created for deprecated course ids. if course.id.deprecated: messages.error( request, _("You cannot create a CCX from a course using a deprecated id. " "Please create a rerun of this course in the studio to allow " "this action.")) url = reverse('ccx_coach_dashboard', kwargs={'course_id': course.id}) return redirect(url) ccx = CustomCourseForEdX(course_id=course.id, coach=request.user, display_name=name) ccx.save() # Make sure start/due are overridden for entire course start = TODAY().replace(tzinfo=pytz.UTC) override_field_for_ccx(ccx, course, 'start', start) override_field_for_ccx(ccx, course, 'due', None) # Enforce a static limit for the maximum amount of students that can be enrolled override_field_for_ccx(ccx, course, 'max_student_enrollments_allowed', settings.CCX_MAX_STUDENTS_ALLOWED) # Save display name explicitly override_field_for_ccx(ccx, course, 'display_name', name) # Hide anything that can show up in the schedule hidden = 'visible_to_staff_only' for chapter in course.get_children(): override_field_for_ccx(ccx, chapter, hidden, True) for sequential in chapter.get_children(): override_field_for_ccx(ccx, sequential, hidden, True) for vertical in sequential.get_children(): override_field_for_ccx(ccx, vertical, hidden, True) ccx_id = CCXLocator.from_course_locator(course.id, str(ccx.id)) # Create forum roles seed_permissions_roles(ccx_id) # Assign administrator forum role to CCX coach assign_role(ccx_id, request.user, FORUM_ROLE_ADMINISTRATOR) url = reverse('ccx_coach_dashboard', kwargs={'course_id': ccx_id}) # Enroll the coach in the course email_params = get_email_params(course, auto_enroll=True, course_key=ccx_id, display_name=ccx.display_name) enroll_email( course_id=ccx_id, student_email=request.user.email, auto_enroll=True, email_students=True, email_params=email_params, ) assign_staff_role_to_ccx(ccx_id, request.user, course.id) add_master_course_staff_to_ccx(course, ccx_id, ccx.display_name) # using CCX object as sender here. responses = SignalHandler.course_published.send( sender=ccx, course_key=CCXLocator.from_course_locator(course.id, str(ccx.id))) for rec, response in responses: log.info( 'Signal fired when course is published. Receiver: %s. Response: %s', rec, response) return redirect(url)
def create_ccx(request, course, ccx=None): """ Create a new CCX """ name = request.POST.get('name') if hasattr(course, 'ccx_connector') and course.ccx_connector: # if ccx connector url is set in course settings then inform user that he can # only create ccx by using ccx connector url. context = get_ccx_creation_dict(course) messages.error(request, context['use_ccx_con_error_message']) return render_to_response('ccx/coach_dashboard.html', context) # prevent CCX objects from being created for deprecated course ids. if course.id.deprecated: messages.error(request, _( "You cannot create a CCX from a course using a deprecated id. " "Please create a rerun of this course in the studio to allow " "this action.")) url = reverse('ccx_coach_dashboard', kwargs={'course_id': course.id}) return redirect(url) ccx = CustomCourseForEdX( course_id=course.id, coach=request.user, display_name=name) ccx.save() # Make sure start/due are overridden for entire course start = TODAY().replace(tzinfo=pytz.UTC) override_field_for_ccx(ccx, course, 'start', start) override_field_for_ccx(ccx, course, 'due', None) # Enforce a static limit for the maximum amount of students that can be enrolled override_field_for_ccx(ccx, course, 'max_student_enrollments_allowed', settings.CCX_MAX_STUDENTS_ALLOWED) # Hide anything that can show up in the schedule hidden = 'visible_to_staff_only' for chapter in course.get_children(): override_field_for_ccx(ccx, chapter, hidden, True) for sequential in chapter.get_children(): override_field_for_ccx(ccx, sequential, hidden, True) for vertical in sequential.get_children(): override_field_for_ccx(ccx, vertical, hidden, True) ccx_id = CCXLocator.from_course_locator(course.id, unicode(ccx.id)) # Create forum roles seed_permissions_roles(ccx_id) # Assign administrator forum role to CCX coach assign_role(ccx_id, request.user, FORUM_ROLE_ADMINISTRATOR) url = reverse('ccx_coach_dashboard', kwargs={'course_id': ccx_id}) # Enroll the coach in the course email_params = get_email_params(course, auto_enroll=True, course_key=ccx_id, display_name=ccx.display_name) enroll_email( course_id=ccx_id, student_email=request.user.email, auto_enroll=True, email_students=True, email_params=email_params, ) assign_staff_role_to_ccx(ccx_id, request.user, course.id) add_master_course_staff_to_ccx(course, ccx_id, ccx.display_name) # using CCX object as sender here. responses = SignalHandler.course_published.send( sender=ccx, course_key=CCXLocator.from_course_locator(course.id, unicode(ccx.id)) ) for rec, response in responses: log.info('Signal fired when course is published. Receiver: %s. Response: %s', rec, response) return redirect(url)
def create_ccx(request, course, ccx=None, **kwargs): """ Create a new CCX """ if not is_ccx_coach_on_master_course( request.user, course) or not request.user.profile.affiliate: return HttpResponseForbidden() affiliate_slug = request.POST.get('affiliate') name = request.POST.get('name') delivery_mode = request.POST.get('delivery_mode') location_city = request.POST.get('city') location_state = request.POST.get('state') location_postal_code = request.POST.get('postal_code') time = '{} {}Z'.format(request.POST.get('date'), request.POST.get('time')) enrollment_end_date = '{} {}Z'.format( request.POST.get('enrollment_end_date'), request.POST.get('enrollment_end_time')) end_date = '{} {}Z'.format(request.POST.get('end_date'), request.POST.get('end_time')) fee = request.POST.get('fee') course_description = request.POST.get('course_description') enrollment_type = request.POST.get('enrollment_type') facilitators = dict(request.POST).get('facilitators') context = get_ccx_creation_dict(course) if not affiliate_slug: messages.error(request, 'Affiliate not selected.') return render_to_response('ccx/coach_dashboard.html', context) if not facilitators: messages.error(request, 'No facilitators added.') return render_to_response('ccx/coach_dashboard.html', context) if hasattr(course, 'ccx_connector') and course.ccx_connector: # if ccx connector url is set in course settings then inform user that he can # only create ccx by using ccx connector url. messages.error(request, context['use_ccx_con_error_message']) return render_to_response('ccx/coach_dashboard.html', context) # prevent CCX objects from being created for deprecated course ids. if course.id.deprecated: messages.error( request, _("You cannot create a CCX from a course using a deprecated id. " "Please create a rerun of this course in the studio to allow " "this action.")) url = reverse('ccx_coach_dashboard', kwargs={'course_id': course.id}) return redirect(url) affiliate = AffiliateEntity.objects.get(slug=affiliate_slug) if not request.user.is_staff and not AffiliateMembership.objects.filter( member=request.user, affiliate=affiliate, role__in=AffiliateMembership.STAFF_ROLES).exists(): return HttpResponseForbidden() ccx = CustomCourseForEdX(affiliate=affiliate, course_id=course.id, coach=request.user, display_name=name, delivery_mode=delivery_mode, location_city=location_city, location_state=location_state, location_postal_code=location_postal_code, time=time, enrollment_end_date=enrollment_end_date, end_date=end_date, fee=ast.literal_eval(fee), enrollment_type=enrollment_type, course_description=course_description) ccx.save() # we need this for authorization ccx.save() # Make sure start/due are overridden for entire course start = TODAY().replace(tzinfo=pytz.UTC) override_field_for_ccx(ccx, course, 'start', start) override_field_for_ccx(ccx, course, 'due', None) # Enforce a static limit for the maximum amount of students that can be enrolled override_field_for_ccx(ccx, course, 'max_student_enrollments_allowed', settings.CCX_MAX_STUDENTS_ALLOWED) # Hide anything that can show up in the schedule hidden = 'visible_to_staff_only' for chapter in course.get_children(): override_field_for_ccx(ccx, chapter, hidden, True) for sequential in chapter.get_children(): override_field_for_ccx(ccx, sequential, hidden, True) for vertical in sequential.get_children(): override_field_for_ccx(ccx, vertical, hidden, True) ccx_id = CCXLocator.from_course_locator(course.id, ccx.pk) url = reverse('ccx_coach_dashboard', kwargs={'course_id': ccx_id}) # Enroll the coach in the course email_params = get_email_params(course, auto_enroll=True, course_key=ccx_id, display_name=ccx.display_name) enroll_email( course_id=ccx_id, student_email=request.user.email, auto_enroll=True, email_students=True, email_params=email_params, ) # Add facilitators if facilitators: course_obj = get_course_by_id(ccx.ccx_course_id, depth=None) facilitator_ids = [int(i) for i in facilitators] for user_id in facilitator_ids: user = User.objects.get(id=user_id) enroll_email(course_id=ccx_id, student_email=user.email, auto_enroll=True, email_students=True, email_params=email_params) allow_access(course_obj, user, AffiliateMembership.CCX_COACH, False) return redirect(url)
def dashboard(request, course, ccx=None, **kwargs): """ Display the CCX Coach Dashboard """ partial_course_key = settings.FASTTRAC_COURSE_KEY.split(':')[1] affiliate_slug = request.GET.get('affiliate') affiliate = None facilitators = None if affiliate_slug: affiliate = AffiliateEntity.objects.get(slug=affiliate_slug) facilitators = get_facilitators(affiliate) context = { 'course': course, 'ccx': ccx, 'STATE_CHOICES': STATE_CHOICES, 'delivery_mode_choices': CustomCourseForEdX.DELIVERY_MODE_CHOICES, 'enrollment_choices': CustomCourseForEdX.ENROLLMENT_TYPE_CHOICES, 'is_instructor': False, 'is_ccx_coach': False, 'is_staff': False, 'is_from_fasttrac_course': partial_course_key in unicode(course.id), 'facilitators': facilitators, 'affiliate': affiliate } context.update(get_ccx_creation_dict(course)) if ccx: context.update(edit_ccx_context(course, ccx, request.user)) ccx_locator = ccx.ccx_course_id context['affiliate_entity'] = ccx.affiliate context['is_ccx_coach'] = ccx.coach == request.user context['is_instructor'] = ccx.is_instructor(request.user) context['is_staff'] = ccx.is_staff(request.user) context['ccx_coach_permissions'] = CourseAccessRole.objects.filter( course_id=ccx_locator, role='ccx_coach') context['edit_current'] = False non_student_user_ids = CourseAccessRole.objects.filter( course_id=ccx_locator).values_list('user_id', flat=True) ccx_student_enrollments = CourseEnrollment.objects.filter( course_id=ccx_locator).exclude(user_id__in=non_student_user_ids) # show students on Student Admin tab context['ccx_student_enrollments'] = ccx_student_enrollments # show enrolled not existing students on Enrollments tab context[ 'ccx_student_invitations'] = CourseEnrollmentAllowed.objects.filter( course_id=ccx_locator) # facilitator dropdown choices facilitator_ids = [ ccx_coach_permissions.user.id for ccx_coach_permissions in context['ccx_coach_permissions'] ] facilitator_choices_ids = AffiliateMembership.objects.filter( affiliate=ccx.affiliate, role=AffiliateMembership.CCX_COACH).exclude( member_id__in=facilitator_ids).values_list('member_id', flat=True) context['facilitator_dropdown_choices'] = User.objects.filter( id__in=facilitator_choices_ids) else: context['create_ccx_url'] = reverse('create_ccx', kwargs={'course_id': course.id}) return render_to_response('ccx/coach_dashboard.html', context)