From 019a97c084285b8dbd758c2d11b44972e16849dc Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Fri, 31 Jan 2020 12:57:00 -0500 Subject: [PATCH] Duplicate signals handlers for course content dates from edx-when --- lms/djangoapps/instructor/tests/test_api.py | 2 +- lms/djangoapps/instructor/tests/test_tools.py | 7 +- .../course_date_signals/__init__.py | 0 .../djangoapps/course_date_signals/apps.py | 15 ++++ .../course_date_signals/handlers.py | 75 +++++++++++++++++++ 5 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 openedx/core/djangoapps/course_date_signals/__init__.py create mode 100644 openedx/core/djangoapps/course_date_signals/apps.py create mode 100644 openedx/core/djangoapps/course_date_signals/handlers.py diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index d73676470a..dc1cb79711 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -26,7 +26,6 @@ from django.test.utils import override_settings from django.urls import reverse as django_reverse from django.utils.translation import ugettext as _ from edx_when.api import get_overrides_for_user -from edx_when.signals import extract_dates from mock import Mock, NonCallableMock, patch from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.locator import UsageKey @@ -62,6 +61,7 @@ from lms.djangoapps.instructor_task.api_helper import ( QueueConnectionError, generate_already_running_error_message ) +from openedx.core.djangoapps.course_date_signals.handlers import extract_dates from openedx.core.djangoapps.course_groups.cohorts import set_course_cohorted from openedx.core.djangoapps.django_comment_common.models import FORUM_ROLE_COMMUNITY_TA from openedx.core.djangoapps.django_comment_common.utils import seed_permissions_roles diff --git a/lms/djangoapps/instructor/tests/test_tools.py b/lms/djangoapps/instructor/tests/test_tools.py index ed82bc068c..260d13c065 100644 --- a/lms/djangoapps/instructor/tests/test_tools.py +++ b/lms/djangoapps/instructor/tests/test_tools.py @@ -15,8 +15,9 @@ from django.test import TestCase from opaque_keys.edx.keys import CourseKey from pytz import UTC -from edx_when import api, signals +from edx_when import api from edx_when.field_data import DateLookupFieldData +from openedx.core.djangoapps.course_date_signals import handlers from student.tests.factories import UserFactory from xmodule.fields import Date from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase @@ -215,7 +216,7 @@ class TestSetDueDateExtension(ModuleStoreTestCase): week3 = ItemFactory.create(parent=course) homework = ItemFactory.create(parent=week1) assignment = ItemFactory.create(parent=homework, due=due) - signals.extract_dates(None, course.id) + handlers.extract_dates(None, course.id) user = UserFactory.create() @@ -296,7 +297,7 @@ class TestDataDumps(ModuleStoreTestCase): self.week2 = week2 self.user1 = user1 self.user2 = user2 - signals.extract_dates(None, course.id) + handlers.extract_dates(None, course.id) def test_dump_module_extensions(self): extended = datetime.datetime(2013, 12, 25, 0, 0, tzinfo=UTC) diff --git a/openedx/core/djangoapps/course_date_signals/__init__.py b/openedx/core/djangoapps/course_date_signals/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openedx/core/djangoapps/course_date_signals/apps.py b/openedx/core/djangoapps/course_date_signals/apps.py new file mode 100644 index 0000000000..70fb3a1526 --- /dev/null +++ b/openedx/core/djangoapps/course_date_signals/apps.py @@ -0,0 +1,15 @@ +""" +Django app configuration for openedx.core.djangoapps.course_dates_signals +""" + +from django.apps import AppConfig + + +class CourseDatesSignalsConfig(AppConfig): + name = 'openedx.core.djangoapps.course_dates_signals' + + def ready(self): + """ + Connect handlers to signals. + """ + from . import handlers # pylint: disable=unused-variable diff --git a/openedx/core/djangoapps/course_date_signals/handlers.py b/openedx/core/djangoapps/course_date_signals/handlers.py new file mode 100644 index 0000000000..2544356bd0 --- /dev/null +++ b/openedx/core/djangoapps/course_date_signals/handlers.py @@ -0,0 +1,75 @@ +"""Signal handlers for writing course dates into edx_when.""" +from __future__ import absolute_import, unicode_literals + +import logging + +from django.dispatch import receiver +from six import text_type +from xblock.fields import Scope +from xmodule.modulestore.django import SignalHandler, modulestore + +from edx_when.api import FIELDS_TO_EXTRACT, set_dates_for_course + +log = logging.getLogger(__name__) + + +def date_field_values(date_fields, xblock): + """ + Read field values for the specified date fields from the supplied xblock. + """ + result = {} + for field_name in date_fields: + if field_name not in xblock.fields: + continue + field = xblock.fields[field_name] + if field.scope == Scope.settings and field.is_set_on(xblock): + try: + result[field.name] = field.read_from(xblock) + except TypeError as exception: + exception_message = "{message}, Block-location:{location}, Field-name:{field_name}".format( + message=text_type(exception), + location=text_type(xblock.location), + field_name=field.name + ) + raise TypeError(exception_message) + return result + + +def extract_dates_from_course(course): + """ + Extract all dates from the supplied course. + """ + log.info('Publishing course dates for %s', course.id) + if course.self_paced: + metadata = date_field_values(FIELDS_TO_EXTRACT, course) + # self-paced courses may accidentally have a course due date + metadata.pop('due', None) + date_items = [(course.location, metadata)] + else: + date_items = [] + items = modulestore().get_items(course.id) + log.info('extracting dates from %d items in %s', len(items), course.id) + for item in items: + date_items.append((item.location, date_field_values(FIELDS_TO_EXTRACT, item))) + return date_items + + +@receiver(SignalHandler.course_published) +def extract_dates(sender, course_key, **kwargs): # pylint: disable=unused-argument + """ + Extract dates from blocks when publishing a course. + """ + log.info("Extracting dates from %s", course_key) + + course = modulestore().get_course(course_key) + + if not course: + log.info("No course found for key %s", course_key) + return + + date_items = extract_dates_from_course(course) + + try: + set_dates_for_course(course_key, date_items) + except Exception: # pylint: disable=broad-except + log.exception('setting dates for %s', course_key)