Unverified Commit e63cf7ff by Nimisha Asthagiri Committed by GitHub

Merge pull request #16446 from edx/naa/course-highlights-4

Course Week Highlights accessor
parents 535d39d5 4f2fdde2
from courseware.module_render import get_module_for_descriptor
from courseware.model_data import FieldDataCache
from openedx.core.djangoapps.schedules.config import COURSE_UPDATE_WAFFLE_FLAG
from openedx.core.djangoapps.schedules.exceptions import CourseUpdateDoesNotExist
from request_cache import get_request_or_stub
from xmodule.modulestore.django import modulestore
def course_has_highlights(course_key):
"""
Does the course have any highlights for any section/week in it?
This ignores access checks, since highlights may be lurking in currently
inaccessible content.
"""
if not COURSE_UPDATE_WAFFLE_FLAG.is_enabled(course_key):
return False
course = modulestore().get_course(course_key, depth=1)
return any(
section.highlights
for section in course.get_children()
if not section.hide_from_toc
)
def get_week_highlights(user, course_key, week_num):
"""
Get highlights (list of unicode strings) for a given week.
"""
if not COURSE_UPDATE_WAFFLE_FLAG.is_enabled(course_key):
raise CourseUpdateDoesNotExist(
"%s does not have Course Updates enabled.",
course_key
)
course_descriptor = _get_course_descriptor(course_key)
course_module = _get_course_module(course_descriptor, user)
sections_with_highlights = _get_sections_with_highlights(course_module)
highlights = _get_highlights_for_week(sections_with_highlights, week_num, course_key)
return highlights
def _get_course_descriptor(course_key):
course_descriptor = modulestore().get_course(course_key, depth=1)
if course_descriptor is None:
raise CourseUpdateDoesNotExist(
"Course {} not found.".format(course_key)
)
return course_descriptor
def _get_course_module(course_descriptor, user):
# Fake a request to fool parts of the courseware that want to inspect it.
request = get_request_or_stub()
request.user = user
# Now evil modulestore magic to inflate our descriptor with user state and
# permissions checks.
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
course_descriptor.id, user, course_descriptor, depth=1, read_only=True,
)
return get_module_for_descriptor(
user, request, course_descriptor, field_data_cache, course_descriptor.id, course=course_descriptor,
)
def _get_sections_with_highlights(course_module):
return [
section for section in course_module.get_children()
if section.highlights and not section.hide_from_toc
]
def _get_highlights_for_week(sections, week_num, course_key):
# assume each provided section maps to a single week
num_sections = len(sections)
if not (0 <= week_num < num_sections):
raise CourseUpdateDoesNotExist(
"Requested week {} but {} has only {} sections.".format(
week_num + 1, course_key, num_sections
)
)
section = sections[week_num]
return section.highlights
...@@ -16,7 +16,7 @@ from edx_ace.recipient import Recipient ...@@ -16,7 +16,7 @@ from edx_ace.recipient import Recipient
from courseware.date_summary import verified_upgrade_deadline_link, verified_upgrade_link_is_valid from courseware.date_summary import verified_upgrade_deadline_link, verified_upgrade_link_is_valid
from openedx.core.djangoapps.monitoring_utils import function_trace, set_custom_metric from openedx.core.djangoapps.monitoring_utils import function_trace, set_custom_metric
from openedx.core.djangoapps.schedules.config import COURSE_UPDATE_WAFFLE_FLAG from openedx.core.djangoapps.schedules.content_highlights import get_week_highlights
from openedx.core.djangoapps.schedules.exceptions import CourseUpdateDoesNotExist from openedx.core.djangoapps.schedules.exceptions import CourseUpdateDoesNotExist
from openedx.core.djangoapps.schedules.models import Schedule, ScheduleExperience from openedx.core.djangoapps.schedules.models import Schedule, ScheduleExperience
from openedx.core.djangoapps.schedules.utils import PrefixedDebugLoggerMixin from openedx.core.djangoapps.schedules.utils import PrefixedDebugLoggerMixin
...@@ -26,9 +26,6 @@ from openedx.core.djangoapps.schedules.template_context import ( ...@@ -26,9 +26,6 @@ from openedx.core.djangoapps.schedules.template_context import (
) )
from openedx.core.djangoapps.site_configuration.models import SiteConfiguration from openedx.core.djangoapps.site_configuration.models import SiteConfiguration
from request_cache.middleware import request_cached
from xmodule.modulestore.django import modulestore
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
...@@ -358,14 +355,14 @@ class CourseUpdateResolver(BinnedSchedulesBaseResolver): ...@@ -358,14 +355,14 @@ class CourseUpdateResolver(BinnedSchedulesBaseResolver):
template_context = get_base_template_context(self.site) template_context = get_base_template_context(self.site)
for schedule in schedules: for schedule in schedules:
enrollment = schedule.enrollment enrollment = schedule.enrollment
user = enrollment.user
try: try:
week_highlights = get_week_highlights(enrollment.course_id, week_num) week_highlights = get_week_highlights(user, enrollment.course_id, week_num)
except CourseUpdateDoesNotExist: except CourseUpdateDoesNotExist:
continue continue
user = enrollment.user
course_id_str = str(enrollment.course_id) course_id_str = str(enrollment.course_id)
template_context.update({ template_context.update({
'course_name': schedule.enrollment.course.display_name, 'course_name': schedule.enrollment.course.display_name,
'course_url': absolute_url( 'course_url': absolute_url(
...@@ -380,12 +377,3 @@ class CourseUpdateResolver(BinnedSchedulesBaseResolver): ...@@ -380,12 +377,3 @@ class CourseUpdateResolver(BinnedSchedulesBaseResolver):
template_context.update(_get_upsell_information_for_schedule(user, schedule)) template_context.update(_get_upsell_information_for_schedule(user, schedule))
yield (user, schedule.enrollment.course.language, template_context) yield (user, schedule.enrollment.course.language, template_context)
@request_cached
def get_week_highlights(course_id, week_num):
if COURSE_UPDATE_WAFFLE_FLAG.is_enabled(course_id):
course = modulestore().get_course(course_id)
return course.highlights_for_week(week_num)
else:
raise CourseUpdateDoesNotExist()
...@@ -12,9 +12,8 @@ from courseware.models import ( ...@@ -12,9 +12,8 @@ from courseware.models import (
OrgDynamicUpgradeDeadlineConfiguration OrgDynamicUpgradeDeadlineConfiguration
) )
from edx_ace.utils import date from edx_ace.utils import date
from openedx.core.djangoapps.schedules.exceptions import CourseUpdateDoesNotExist
from openedx.core.djangoapps.schedules.models import ScheduleExperience from openedx.core.djangoapps.schedules.models import ScheduleExperience
from openedx.core.djangoapps.schedules.resolvers import get_week_highlights from openedx.core.djangoapps.schedules.content_highlights import course_has_highlights
from openedx.core.djangoapps.signals.signals import COURSE_START_DATE_CHANGED from openedx.core.djangoapps.signals.signals import COURSE_START_DATE_CHANGED
from openedx.core.djangoapps.theming.helpers import get_current_site from openedx.core.djangoapps.theming.helpers import get_current_site
from student.models import CourseEnrollment from student.models import CourseEnrollment
...@@ -62,10 +61,9 @@ def create_schedule(sender, **kwargs): ...@@ -62,10 +61,9 @@ def create_schedule(sender, **kwargs):
upgrade_deadline=upgrade_deadline upgrade_deadline=upgrade_deadline
) )
try: if course_has_highlights(enrollment.course_id):
get_week_highlights(enrollment.course_id, 1)
experience_type = ScheduleExperience.EXPERIENCES.course_updates experience_type = ScheduleExperience.EXPERIENCES.course_updates
except CourseUpdateDoesNotExist: else:
experience_type = ScheduleExperience.EXPERIENCES.default experience_type = ScheduleExperience.EXPERIENCES.default
ScheduleExperience(schedule=schedule, experience_type=experience_type).save() ScheduleExperience(schedule=schedule, experience_type=experience_type).save()
......
# -*- coding: utf-8 -*-
from openedx.core.djangoapps.schedules.config import COURSE_UPDATE_WAFFLE_FLAG
from openedx.core.djangoapps.schedules.content_highlights import get_week_highlights, course_has_highlights
from openedx.core.djangoapps.schedules.exceptions import CourseUpdateDoesNotExist
from openedx.core.djangolib.testing.utils import skip_unless_lms
from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
from student.tests.factories import UserFactory
from student.models import CourseEnrollment
@skip_unless_lms
class TestContentHighlights(ModuleStoreTestCase):
MODULESTORE = TEST_DATA_SPLIT_MODULESTORE
def setUp(self):
super(TestContentHighlights, self).setUp()
self._setup_course()
self._setup_user()
def _setup_course(self):
self.course = CourseFactory.create()
self.course_key = self.course.id
def _setup_user(self):
self.user = UserFactory.create()
CourseEnrollment.enroll(self.user, self.course_key)
def _create_chapter(self, **kwargs):
ItemFactory.create(
parent=self.course,
category='chapter',
**kwargs
)
@override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_non_existent_course_raises_exception(self):
nonexistent_course_key = self.course_key.replace(run='no_such_run')
with self.assertRaises(CourseUpdateDoesNotExist):
get_week_highlights(self.user, nonexistent_course_key, 0)
@override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_empty_course_raises_exception(self):
with self.assertRaises(CourseUpdateDoesNotExist):
get_week_highlights(self.user, self.course_key, 0)
@override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, False)
def test_flag_disabled(self):
with self.store.bulk_operations(self.course_key):
self._create_chapter(highlights=[u'highlights'])
self.assertFalse(course_has_highlights(self.course_key))
with self.assertRaises(CourseUpdateDoesNotExist):
get_week_highlights(self.user, self.course_key, week_num=0)
@override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_flag_enabled(self):
highlights = [u'highlights']
with self.store.bulk_operations(self.course_key):
self._create_chapter(highlights=highlights)
self.assertTrue(course_has_highlights(self.course_key))
self.assertEqual(
get_week_highlights(self.user, self.course_key, week_num=0),
highlights,
)
@override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_course_with_no_highlights(self):
with self.store.bulk_operations(self.course_key):
self._create_chapter(display_name=u"Week 1")
self._create_chapter(display_name=u"Week 2")
self.course = self.store.get_course(self.course_key)
self.assertEqual(len(self.course.get_children()), 2)
self.assertFalse(course_has_highlights(self.course_key))
with self.assertRaises(CourseUpdateDoesNotExist):
get_week_highlights(self.user, self.course_key, week_num=0)
@override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_course_with_highlights(self):
with self.store.bulk_operations(self.course_key):
self._create_chapter(highlights=[u'a', u'b', u'á'])
self._create_chapter(highlights=[])
self._create_chapter(highlights=[u'skipped a week'])
self.assertTrue(course_has_highlights(self.course_key))
self.assertEqual(
get_week_highlights(self.user, self.course_key, week_num=0),
[u'a', u'b', u'á'],
)
self.assertEqual(
get_week_highlights(self.user, self.course_key, week_num=1),
[u'skipped a week'],
)
with self.assertRaises(CourseUpdateDoesNotExist):
get_week_highlights(self.user, self.course_key, week_num=2)
@override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_staff_only(self):
with self.store.bulk_operations(self.course_key):
self._create_chapter(
highlights=[u"I'm a secret!"],
visible_to_staff_only=True,
)
self.assertTrue(course_has_highlights(self.course_key))
with self.assertRaises(CourseUpdateDoesNotExist):
get_week_highlights(self.user, self.course_key, week_num=0)
...@@ -3,7 +3,7 @@ from unittest import skipUnless ...@@ -3,7 +3,7 @@ from unittest import skipUnless
import ddt import ddt
from django.conf import settings from django.conf import settings
from mock import patch, DEFAULT, Mock from mock import Mock
from openedx.core.djangoapps.schedules.resolvers import BinnedSchedulesBaseResolver from openedx.core.djangoapps.schedules.resolvers import BinnedSchedulesBaseResolver
from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory
......
...@@ -6,7 +6,6 @@ from pytz import utc ...@@ -6,7 +6,6 @@ from pytz import utc
from course_modes.models import CourseMode from course_modes.models import CourseMode
from course_modes.tests.factories import CourseModeFactory from course_modes.tests.factories import CourseModeFactory
from courseware.models import DynamicUpgradeDeadlineConfiguration from courseware.models import DynamicUpgradeDeadlineConfiguration
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.schedules.models import ScheduleExperience from openedx.core.djangoapps.schedules.models import ScheduleExperience
from openedx.core.djangoapps.schedules.signals import CREATE_SCHEDULE_WAFFLE_FLAG from openedx.core.djangoapps.schedules.signals import CREATE_SCHEDULE_WAFFLE_FLAG
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
...@@ -88,10 +87,10 @@ class CreateScheduleTests(SharedModuleStoreTestCase): ...@@ -88,10 +87,10 @@ class CreateScheduleTests(SharedModuleStoreTestCase):
enrollment.schedule enrollment.schedule
@override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True) @override_waffle_flag(CREATE_SCHEDULE_WAFFLE_FLAG, True)
@patch('openedx.core.djangoapps.schedules.signals.get_week_highlights') @patch('openedx.core.djangoapps.schedules.signals.course_has_highlights')
def test_create_schedule_course_updates_experience(self, mock_get_week_highlights, mock_get_current_site): def test_create_schedule_course_updates_experience(self, mock_course_has_highlights, mock_get_current_site):
site = SiteFactory.create() site = SiteFactory.create()
mock_get_week_highlights.return_value = True mock_course_has_highlights.return_value = True
mock_get_current_site.return_value = site mock_get_current_site.return_value = site
self.assert_schedule_created(experience_type=ScheduleExperience.EXPERIENCES.course_updates) self.assert_schedule_created(experience_type=ScheduleExperience.EXPERIENCES.course_updates)
......
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