Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions cms/djangoapps/contentstore/views/preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,6 @@ def _preview_module_system(request, descriptor, field_data):
preview_anonymous_user_id = anonymous_id_for_user(request.user, course_id)

return PreviewModuleSystem(
# TODO (cpennington): Do we want to track how instructors are using the preview problems?
track_function=lambda event_type, event: None,
get_module=partial(_load_preview_module, request),
mixins=settings.XBLOCK_MIXINS,

Expand Down
105 changes: 2 additions & 103 deletions lms/djangoapps/courseware/module_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@

from functools import partial

from completion.waffle import ENABLE_COMPLETION_TRACKING_SWITCH
from completion.models import BlockCompletion
from completion.services import CompletionService
from django.conf import settings
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
Expand Down Expand Up @@ -52,10 +50,9 @@
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.partitions.partitions_service import PartitionService
from xmodule.util.sandboxing import SandboxService
from xmodule.services import RebindUserService, SettingsService, TeamsConfigurationService
from xmodule.services import EventPublishingService, RebindUserService, SettingsService, TeamsConfigurationService
from common.djangoapps.static_replace.services import ReplaceURLService
from common.djangoapps.static_replace.wrapper import replace_urls_wrapper
from common.djangoapps.xblock_django.constants import ATTR_KEY_USER_ID
from xmodule.capa.xqueue_interface import XQueueService # lint-amnesty, pylint: disable=wrong-import-order
from lms.djangoapps.courseware.access import get_user_role, has_access
from lms.djangoapps.courseware.entrance_exams import user_can_skip_entrance_exam, user_has_passed_entrance_exam
Expand All @@ -69,7 +66,6 @@
from lms.djangoapps.courseware.field_overrides import OverrideFieldData
from lms.djangoapps.courseware.services import UserStateService
from lms.djangoapps.grades.api import GradesUtilService
from lms.djangoapps.grades.api import signals as grades_signals
from lms.djangoapps.lms_xblock.field_data import LmsFieldData
from lms.djangoapps.lms_xblock.runtime import LmsModuleSystem, UserTagsService
from lms.djangoapps.verify_student.services import XBlockVerificationService
Expand All @@ -96,7 +92,6 @@
from openedx.features.content_type_gating.services import ContentTypeGatingService
from common.djangoapps.student.models import anonymous_id_for_user
from common.djangoapps.student.roles import CourseBetaTesterRole
from common.djangoapps.track import contexts
from common.djangoapps.util import milestones_helpers
from common.djangoapps.util.json_request import JsonResponse
from common.djangoapps.edxmako.services import MakoService
Expand Down Expand Up @@ -500,22 +495,6 @@ def inner_get_module(descriptor):
will_recheck_access=will_recheck_access,
)

def get_event_handler(event_type):
"""
Return an appropriate function to handle the event.

Returns None if no special processing is required.
"""
handlers = {
'grade': handle_grade_event,
}
if ENABLE_COMPLETION_TRACKING_SWITCH.is_enabled():
handlers.update({
'completion': handle_completion_event,
'progress': handle_deprecated_progress_event,
})
return handlers.get(event_type)

# These modules store data using the anonymous_student_id as a key.
# To prevent loss of data, we will continue to provide old modules with
# the per-student anonymized id (as we have in the past),
Expand All @@ -536,85 +515,6 @@ def get_event_handler(event_type):
request_country_code=user_location,
)

def publish(block, event_type, event):
"""
A function that allows XModules to publish events.
"""
handle_event = get_event_handler(event_type)
if handle_event and not is_masquerading_as_specific_student(user, course_id):
handle_event(block, event)
else:
context = contexts.course_context_from_course_id(course_id)
user_id = user_service.get_current_user().opt_attrs.get(ATTR_KEY_USER_ID)
if user_id:
context['user_id'] = user_id

context['asides'] = {}
for aside in block.runtime.get_asides(block):
if hasattr(aside, 'get_event_context'):
aside_event_info = aside.get_event_context(event_type, event)
if aside_event_info is not None:
context['asides'][aside.scope_ids.block_type] = aside_event_info
with tracker.get_tracker().context(event_type, context):
track_function(event_type, event)

def handle_completion_event(block, event):
"""
Submit a completion object for the block.
"""
if not ENABLE_COMPLETION_TRACKING_SWITCH.is_enabled(): # lint-amnesty, pylint: disable=no-else-raise
raise Http404
else:
BlockCompletion.objects.submit_completion(
user=user,
block_key=block.scope_ids.usage_id,
completion=event['completion'],
)

def handle_grade_event(block, event):
"""
Submit a grade for the block.
"""
if not user.is_anonymous:
grades_signals.SCORE_PUBLISHED.send(
sender=None,
block=block,
user=user,
raw_earned=event['value'],
raw_possible=event['max_value'],
only_if_higher=event.get('only_if_higher'),
score_deleted=event.get('score_deleted'),
grader_response=event.get('grader_response')
)

def handle_deprecated_progress_event(block, event):
"""
DEPRECATED: Submit a completion for the block represented by the
progress event.

This exists to support the legacy progress extension used by
edx-solutions. New XBlocks should not emit these events, but instead
emit completion events directly.
"""
if not ENABLE_COMPLETION_TRACKING_SWITCH.is_enabled(): # lint-amnesty, pylint: disable=no-else-raise
raise Http404
else:
requested_user_id = event.get('user_id', user.id)
if requested_user_id != user.id:
log.warning(f"{user} tried to submit a completion on behalf of {requested_user_id}")
return

# If blocks explicitly declare support for the new completion API,
# we expect them to emit 'completion' events,
# and we ignore the deprecated 'progress' events
# in order to avoid duplicate work and possibly conflicting semantics.
if not getattr(block, 'has_custom_completion', False):
BlockCompletion.objects.submit_completion(
user=user,
block_key=block.scope_ids.usage_id,
completion=1.0,
)

# Rebind module service to deal with noauth modules getting attached to users
rebind_user_service = RebindUserService(
user,
Expand Down Expand Up @@ -689,9 +589,7 @@ def handle_deprecated_progress_event(block, event):
store = modulestore()

system = LmsModuleSystem(
track_function=track_function,
get_module=inner_get_module,
publish=publish,
# TODO: When we merge the descriptor and module systems, we can stop reaching into the mixologist (cpennington)
mixins=descriptor.runtime.mixologist._mixins, # pylint: disable=protected-access
wrappers=block_wrappers,
Expand Down Expand Up @@ -726,6 +624,7 @@ def handle_deprecated_progress_event(block, event):
'teams': TeamsService(),
'teams_configuration': TeamsConfigurationService(),
'call_to_action': CallToActionService(),
'publish': EventPublishingService(user, course_id, track_function),
},
descriptor_runtime=descriptor._runtime, # pylint: disable=protected-access
request_token=request_token,
Expand Down
11 changes: 6 additions & 5 deletions lms/djangoapps/courseware/tests/test_module_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -885,7 +885,7 @@ def test_skip_handlers_for_masquerading_staff(self):
request.session = {}
request.user.real_user = GlobalStaffFactory.create()
request.user.real_user.masquerade_settings = CourseMasquerade(course.id, user_name="jem")
with patch('lms.djangoapps.courseware.module_render.is_masquerading_as_specific_student') as mock_masq:
with patch('xmodule.services.is_masquerading_as_specific_student') as mock_masq:
mock_masq.return_value = True
response = render.handle_xblock_callback(
request,
Expand All @@ -900,7 +900,7 @@ def test_skip_handlers_for_masquerading_staff(self):
BlockCompletion.objects.get(block_key=block.scope_ids.usage_id)

@XBlock.register_temp_plugin(GradedStatelessXBlock, identifier='stateless_scorer')
@patch('lms.djangoapps.courseware.module_render.grades_signals.SCORE_PUBLISHED.send')
@patch('xmodule.services.grades_signals.SCORE_PUBLISHED.send')
def test_anonymous_user_not_be_graded(self, mock_score_signal):
course = CourseFactory.create()
descriptor_kwargs = {
Expand Down Expand Up @@ -2022,7 +2022,10 @@ def handle_callback_and_get_context_info(self,
descriptor_kwargs['display_name'] = problem_display_name

descriptor = ItemFactory.create(**descriptor_kwargs)
with patch('lms.djangoapps.courseware.module_render.tracker') as mock_tracker_for_context:
mock_tracker_for_context = MagicMock()
with patch('lms.djangoapps.courseware.module_render.tracker', mock_tracker_for_context), patch(
'xmodule.services.tracker', mock_tracker_for_context
):
render.handle_xblock_callback(
self.request,
str(self.course.id),
Expand All @@ -2032,12 +2035,10 @@ def handle_callback_and_get_context_info(self,
)

assert len(mock_tracker.emit.mock_calls) == 1
# lint-amnesty, pylint: disable=deprecated-method
mock_call = mock_tracker.emit.mock_calls[0]
event = mock_call[2]

assert event['name'] == 'problem_check'
# lint-amnesty, pylint: disable=deprecated-method

# for different operations, there are different number of context calls.
# We are sending this `call_idx` to get the mock call that we are interested in.
Expand Down
1 change: 0 additions & 1 deletion lms/djangoapps/lms_xblock/test/test_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ def setUp(self):
super().setUp()
self.block = BlockMock(name='block')
self.runtime = LmsModuleSystem(
track_function=Mock(),
get_module=Mock(),
descriptor_runtime=Mock(),
)
Expand Down
4 changes: 3 additions & 1 deletion openedx/core/djangoapps/xblock/runtime/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from xmodule.errortracker import make_error_tracker
from xmodule.contentstore.django import contentstore
from xmodule.modulestore.django import ModuleI18nService
from xmodule.services import RebindUserService
from xmodule.services import EventPublishingService, RebindUserService
from xmodule.util.sandboxing import SandboxService
from common.djangoapps.edxmako.services import MakoService
from common.djangoapps.static_replace.services import ReplaceURLService
Expand Down Expand Up @@ -266,6 +266,8 @@ def service(self, block, service_name):
track_function=make_track_function(),
request_token=request_token(crum.get_current_request()),
)
elif service_name == 'publish':
return EventPublishingService(self.user, context_key, make_track_function())

# Check if the XBlockRuntimeSystem wants to handle this:
service = self.system.get_service(block, service_name)
Expand Down
13 changes: 0 additions & 13 deletions openedx/core/djangoapps/xblock/runtime/shims.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,19 +297,6 @@ def get_field_provenance(self, xblock, field):
result['default_value'] = field.to_json(field.default)
return result

def track_function(self, title, event_info):
"""
Publish an event to the tracking log.

This is deprecated in favor of runtime.publish
See https://git.io/JeGLf and https://git.io/JeGLY for context.
"""
warnings.warn(
"runtime.track_function is deprecated. Use runtime.publish() instead.",
DeprecationWarning, stacklevel=2,
)
self.publish(self._active_block, title, event_info)

@property
def user_location(self):
"""
Expand Down
25 changes: 13 additions & 12 deletions openedx/tests/xblock_integration/xblock_testcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@
from django.conf import settings
from django.urls import reverse
from xblock.plugin import Plugin

import xmodule.services
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory

import lms.djangoapps.lms_xblock.runtime
from lms.djangoapps.courseware.tests.helpers import LoginEnrollmentTestCase


Expand Down Expand Up @@ -100,26 +101,26 @@ def setUp(self):

"""
super().setUp()
saved_init = lms.djangoapps.lms_xblock.runtime.LmsModuleSystem.__init__
saved_init = xmodule.services.EventPublishingService.__init__

def patched_init(runtime_self, **kwargs):
def patched_init(runtime_self, user, course_id, track_function, **kwargs):
"""
Swap out publish in the __init__
Swap out track_function in the __init__
"""
old_publish = kwargs["publish"]
old_track_function = track_function

def publish(block, event_type, event):
def new_track_function(event_type, event):
"""
Log the event, and call the original publish
Log the event, and call the original track_function.
"""
self.events.append({"event": event, "event_type": event_type})
old_publish(block, event_type, event)
kwargs['publish'] = publish
return saved_init(runtime_self, **kwargs)
old_track_function(event_type, event)
track_function = new_track_function
return saved_init(runtime_self, user, course_id, track_function, **kwargs)

self.events = []
lms_sys = "lms.djangoapps.lms_xblock.runtime.LmsModuleSystem.__init__"
patcher = mock.patch(lms_sys, patched_init)
publish_service = "xmodule.services.EventPublishingService.__init__"
patcher = mock.patch(publish_service, patched_init)
patcher.start()
self.addCleanup(patcher.stop)

Expand Down
5 changes: 2 additions & 3 deletions xmodule/capa/responsetypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,7 @@ def make_hint_div(self, hint_node, correct, student_answer, question_tag,
else:
label = _('Incorrect:')

# self.runtime.track_function('get_demand_hint', event_info)
# This this "feedback hint" event
# This is the "feedback hint" event
event_info = {}
event_info['module_id'] = text_type(self.capa_module.location)
event_info['problem_part_id'] = self.id
Expand All @@ -384,7 +383,7 @@ def make_hint_div(self, hint_node, correct, student_answer, question_tag,
event_info['question_type'] = question_tag
if log_extra:
event_info.update(log_extra)
self.capa_module.runtime.track_function('edx.problem.hint.feedback_displayed', event_info)
self.capa_module.runtime.publish(self.capa_module, 'edx.problem.hint.feedback_displayed', event_info)

# Form the div-wrapped hint texts
hints_wrap = HTML('').join(
Expand Down
4 changes: 2 additions & 2 deletions xmodule/capa/tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def test_capa_system(render_template=None):

def mock_capa_module():
"""
capa response types needs just two things from the capa_module: location and track_function.
capa response types needs just two things from the capa_module: location and publish.
"""
def mock_location_text(self): # lint-amnesty, pylint: disable=unused-argument
"""
Expand All @@ -99,7 +99,7 @@ def mock_location_text(self): # lint-amnesty, pylint: disable=unused-argument
else:
capa_module.location.__str__ = mock_location_text
# The following comes into existence by virtue of being called
# capa_module.runtime.track_function
# capa_module.runtime.publish
return capa_module


Expand Down
Loading