Commit 0b184881 by cewing

MIT: CCX. Implement display of CCXs in user-facing templates such as dashboard

Show the display name for the POC in the menu for switching.

Remove the MOOC: prefix from the MOOC title to avoid truncation issues.

Incorporate the display name of the POC into the display of course org/number/name at the top of the page in the default theme.

Provide warning on the grading policy page in system warning style.

Provide a PocMembership model api that returns PocMemberships for a user.  Default to active memberships.

Build generator that yields triplets of poc, membership and course for each poc membership a user has.

This is an analog of the `get_course_enrollment_pairs` in common/djangoapps/students/views.py and is used in the student dashboard to provide pocs as options for a student to view.

refactor to use model api instead of inline code.

provide pocs as possible targets for a student to visit from their dashboard.

Unset the current active poc when visiting the dashboard so as to avoid needing to switch active pocs when viewing a plain course.  This ensures we can leave template code for the display of courses unchanged.

Update templates to show pocs in a course-like view on a student's dashboard.

Revert a portion of the template change so that the poc title will still render properly when a poc is being viewed.
parent bb7eed58
...@@ -660,6 +660,17 @@ def dashboard(request): ...@@ -660,6 +660,17 @@ def dashboard(request):
if course.pre_requisite_courses) if course.pre_requisite_courses)
courses_requirements_not_met = get_pre_requisite_courses_not_completed(user, courses_having_prerequisites) 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(
user, course_org_filter, org_filter_out_set
)
# should we deselect any active POC 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
context = { context = {
'enrollment_message': enrollment_message, 'enrollment_message': enrollment_message,
'course_enrollment_pairs': course_enrollment_pairs, 'course_enrollment_pairs': course_enrollment_pairs,
...@@ -691,6 +702,7 @@ def dashboard(request): ...@@ -691,6 +702,7 @@ def dashboard(request):
'provider_states': [], 'provider_states': [],
'order_history_list': order_history_list, 'order_history_list': order_history_list,
'courses_requirements_not_met': courses_requirements_not_met, 'courses_requirements_not_met': courses_requirements_not_met,
'poc_membership_triplets': poc_membership_triplets,
} }
if third_party_auth.is_enabled(): if third_party_auth.is_enabled():
......
...@@ -43,6 +43,10 @@ class PocMembership(models.Model): ...@@ -43,6 +43,10 @@ class PocMembership(models.Model):
membership.save() membership.save()
future_membership.delete() future_membership.delete()
@classmethod
def memberships_for_user(cls, user, active=True):
return cls.objects.filter(student=user, active__exact=active)
class PocFutureMembership(models.Model): class PocFutureMembership(models.Model):
""" """
......
...@@ -4,19 +4,18 @@ POC Enrollment operations for use by Coach APIs. ...@@ -4,19 +4,18 @@ POC Enrollment operations for use by Coach APIs.
Does not include any access control, be sure to check access before calling. Does not include any access control, be sure to check access before calling.
""" """
from courseware.courses import get_course_about_section
from courseware.courses import get_course_by_id
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.conf import settings from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.core.mail import send_mail from django.core.mail import send_mail
from courseware.courses import get_course_about_section
from courseware.courses import get_course_by_id
from edxmako.shortcuts import render_to_string from edxmako.shortcuts import render_to_string
from microsite_configuration import microsite from microsite_configuration import microsite
from xmodule.modulestore.django import modulestore
from xmodule.error_module import ErrorDescriptor
from .models import ( from .models import (
PersonalOnlineCourse,
PocMembership, PocMembership,
PocFutureMembership, PocFutureMembership,
) )
...@@ -241,15 +240,12 @@ def get_all_pocs_for_user(user): ...@@ -241,15 +240,12 @@ def get_all_pocs_for_user(user):
current_active_poc = get_current_poc(user) current_active_poc = get_current_poc(user)
if user.is_anonymous(): if user.is_anonymous():
return [] return []
active_poc_memberships = PocMembership.objects.filter(
student=user, active__exact=True
)
memberships = [] memberships = []
for membership in active_poc_memberships: for membership in PocMembership.memberships_for_user(user):
course = get_course_by_id(membership.poc.course_id) course = get_course_by_id(membership.poc.course_id)
course_title = get_course_about_section(course, 'title') poc = membership.poc
poc_title = 'POC: {}'.format(course_title) poc_title = poc.display_name
mooc_title = 'MOOC: {}'.format(course_title) mooc_title = get_course_about_section(course, 'title')
url = reverse( url = reverse(
'switch_active_poc', 'switch_active_poc',
args=[course.id.to_deprecated_string(), membership.poc.id] args=[course.id.to_deprecated_string(), membership.poc.id]
...@@ -266,3 +262,30 @@ def get_all_pocs_for_user(user): ...@@ -266,3 +262,30 @@ def get_all_pocs_for_user(user):
'mooc_url': mooc_url, 'mooc_url': mooc_url,
}) })
return memberships return memberships
def get_poc_membership_triplets(user, course_org_filter, org_filter_out_set):
"""
Get the relevant set of (PersonalOnlineCourse, PocMembership, 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
store = modulestore()
with store.bulk_operations(poc.course_id):
course = store.get_course(poc.course_id)
if course and not isinstance(course, ErrorDescriptor):
# if we are in a Microsite, then filter out anything that is not
# attributed (by ORG) to that Microsite
if course_org_filter and course_org_filter != course.location.org:
continue
# Conversely, if we are not in a Microsite, then let's filter out any enrollments
# with courses attributed (by ORG) to Microsites
elif course.location.org in org_filter_out_set:
continue
yield (poc, membership, course)
else:
log.error("User {0} enrolled in {2} course {1}".format(
user.username, poc.course_id, "broken" if course else "non-existent"
))
...@@ -222,6 +222,63 @@ ...@@ -222,6 +222,63 @@
</ul> </ul>
</section> </section>
</section> </section>
<section id="my-courses" class="my-courses" role="main" aria-label="Content">
<header>
<h2>${_("Current Courses")}</h2>
</header>
% if len(course_enrollment_pairs) > 0:
<ul class="listing-courses">
% for course, enrollment in course_enrollment_pairs:
<% show_courseware_link = (course.id in show_courseware_links_for) %>
<% cert_status = cert_statuses.get(course.id) %>
<% show_email_settings = (course.id in show_email_settings_for) %>
<% course_mode_info = all_course_modes.get(course.id) %>
<% show_refund_option = (course.id in show_refund_option_for) %>
<% is_paid_course = (course.id in enrolled_courses_either_paid) %>
<% is_course_blocked = (course.id in block_courses) %>
<% course_verification_status = verification_status_by_course.get(course.id, {}) %>
<% course_requirements = courses_requirements_not_met.get(course.id) %>
<%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" />
% endfor
% endif
</ul>
% else:
<section class="empty-dashboard-message">
% if settings.FEATURES.get('COURSES_ARE_BROWSABLE'):
<p>${_("Looks like you haven't enrolled in any courses yet.")}</p>
<a href="${marketing_link('COURSES')}">
${_("Find courses now!")}
</a>
% else:
<p>${_("Looks like you haven't enrolled in any courses yet.")}</p>
%endif
</section>
% endif
% if staff_access and len(errored_courses) > 0:
<div id="course-errors">
<h2>${_("Course-loading errors")}</h2>
% for course_dir, errors in errored_courses.items():
<h3>${course_dir | h}</h3>
<ul>
% for (msg, err) in errors:
<li>${msg}
<ul><li><pre>${err}</pre></li></ul>
</li>
% endfor
</ul>
% endfor
% endif
</section>
</section> </section>
<section id="email-settings-modal" class="modal" aria-hidden="true"> <section id="email-settings-modal" class="modal" aria-hidden="true">
......
...@@ -13,7 +13,7 @@ from status.status import get_site_status_msg ...@@ -13,7 +13,7 @@ from status.status import get_site_status_msg
<%! from microsite_configuration import microsite %> <%! from microsite_configuration import microsite %>
<%! from microsite_configuration.templatetags.microsite import platform_name %> <%! from microsite_configuration.templatetags.microsite import platform_name %>
<%! from pocs.utils import get_all_pocs_for_user %> <%! from pocs.overrides import get_current_poc %>
## Provide a hook for themes to inject branding on top. ## Provide a hook for themes to inject branding on top.
<%block name="navigation_top" /> <%block name="navigation_top" />
...@@ -48,7 +48,16 @@ site_status_msg = get_site_status_msg(course_id) ...@@ -48,7 +48,16 @@ site_status_msg = get_site_status_msg(course_id)
</h1> </h1>
% if course: % if course:
<h2><span class="provider">${course.display_org_with_default | h}:</span> ${course.display_number_with_default | h} ${course.display_name_with_default}</h2> <h2><span class="provider">${course.display_org_with_default | h}:</span>
${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(user)
if poc:
display_name = poc.display_name
%>
${display_name}</h2>
% endif % endif
% if user.is_authenticated(): % if user.is_authenticated():
...@@ -83,15 +92,6 @@ site_status_msg = get_site_status_msg(course_id) ...@@ -83,15 +92,6 @@ site_status_msg = get_site_status_msg(course_id)
% if settings.MKTG_URL_LINK_MAP.get('FAQ'): % if settings.MKTG_URL_LINK_MAP.get('FAQ'):
<li><a href="${marketing_link('FAQ')}">${_("Help")}</a></li> <li><a href="${marketing_link('FAQ')}">${_("Help")}</a></li>
% endif % endif
% if settings.FEATURES.get('PERSONAL_ONLINE_COURSES', False):
%for poc in get_all_pocs_for_user(user):
% if poc['active']:
<li><a href="${poc['mooc_url']}" role="menuitem">${poc['mooc_name']}</a></li>
% else:
<li><a href="${poc['poc_url']}" role="menuitem">${poc['poc_name']}</a></li>
% endif
%endfor
% endif
</%block> </%block>
<li><a href="${reverse('logout')}" role="menuitem">${_("Sign out")}</a></li> <li><a href="${reverse('logout')}" role="menuitem">${_("Sign out")}</a></li>
</ul> </ul>
......
<%page args="poc, membership, course" />
<%! from django.utils.translation import ugettext as _ %>
<%!
from django.core.urlresolvers import reverse
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])
%>
<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>
<section class="info">
<hgroup>
<p class="date-block">
Personal Online 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>
</h3>
</hgroup>
<a href="${poc_switch_target}" class="enter-course">${_('View Course')}</a>
</section>
</article>
</li>
<%! from django.utils.translation import ugettext as _ %> <%! from django.utils.translation import ugettext as _ %>
<div id="warn-coach" class="wrapper-msg urgency-high warning">
<div class="msg">
<i class="msg-icon icon-warning-sign"></i>
<div class="msg-content">
<h3 class="title">${_("WARNING")}</h3>
<div class="copy">
<p>${_("For advanced users only. Errors in the grading policy can lead to the course failing to display. This form does not check the validity of the policy before saving.")}</p>
<p>${_("Most coaches should not need to make changes to the grading policy.")}</p>
</div>
</div>
</div>
</div>
<h2>${_("Grading Policy")}</h2> <h2>${_("Grading Policy")}</h2>
<form action="${grading_policy_url}" method="POST"> <form action="${grading_policy_url}" method="POST">
......
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