Commit 2117d168 by Dino Cikatic Committed by marjev

SOL-217 Adding basic analytics events logging for courseware search and index

parent d182b8db
......@@ -571,15 +571,13 @@ class TestCourseReIndex(CourseTestCase):
self.assertEqual(response['results'], [])
# Start manual reindex
errors = CoursewareSearchIndexer.do_course_reindex(modulestore(), self.course.id)
self.assertEqual(errors, None)
CoursewareSearchIndexer.do_course_reindex(modulestore(), self.course.id)
self.html.display_name = "My expanded HTML"
modulestore().update_item(self.html, ModuleStoreEnum.UserID.test)
# Start manual reindex
errors = CoursewareSearchIndexer.do_course_reindex(modulestore(), self.course.id)
self.assertEqual(errors, None)
CoursewareSearchIndexer.do_course_reindex(modulestore(), self.course.id)
# Check results indexed now
response = perform_search(
......
......@@ -3,6 +3,7 @@ from __future__ import absolute_import
import logging
from django.conf import settings
from django.utils.translation import ugettext as _
from opaque_keys.edx.locator import CourseLocator
from search.search_engine_base import SearchEngine
......@@ -10,6 +11,7 @@ from search.search_engine_base import SearchEngine
from . import ModuleStoreEnum
from .exceptions import ItemNotFoundError
# Use default index and document names for now
INDEX_NAME = "courseware_index"
DOCUMENT_TYPE = "courseware_content"
......@@ -36,6 +38,7 @@ class CoursewareSearchIndexer(object):
Add to courseware search index from given location and its children
"""
error_list = []
indexed_count = 0
# TODO - inline for now, need to move this out to a celery task
searcher = SearchEngine.get_search_engine(INDEX_NAME)
if not searcher:
......@@ -115,6 +118,7 @@ class CoursewareSearchIndexer(object):
remove_index_item_location(location)
else:
index_item_location(location, None)
indexed_count += 1
except Exception as err: # pylint: disable=broad-except
# broad exception so that index operation does not prevent the rest of the application from working
log.exception(
......@@ -127,9 +131,72 @@ class CoursewareSearchIndexer(object):
if raise_on_error and error_list:
raise SearchIndexingError(_('Error(s) present during indexing'), error_list)
return indexed_count
@classmethod
def do_publish_index(cls, modulestore, location, delete=False, raise_on_error=False):
"""
Add to courseware search index published section and children
"""
indexed_count = cls.add_to_search_index(modulestore, location, delete, raise_on_error)
CoursewareSearchIndexer._track_index_request('edx.course.index.published', indexed_count, str(location))
return indexed_count
@classmethod
def do_course_reindex(cls, modulestore, course_key):
"""
(Re)index all content within the given course
"""
return cls.add_to_search_index(modulestore, course_key, delete=False, raise_on_error=True)
indexed_count = cls.add_to_search_index(modulestore, course_key, delete=False, raise_on_error=True)
CoursewareSearchIndexer._track_index_request('edx.course.index.reindexed', indexed_count)
return indexed_count
@staticmethod
def _track_index_request(event_name, indexed_count, location=None):
"""Track content index requests.
Arguments:
location (str): The ID of content to be indexed.
event_name (str): Name of the event to be logged.
Returns:
None
"""
from eventtracking import tracker as track
tracker = track.get_tracker()
tracking_context = tracker.resolve_context() # pylint: disable=no-member
data = {
"indexed_count": indexed_count,
'category': 'courseware_index',
}
if location:
data['location_id'] = location
tracker.emit(
event_name,
data
)
try:
if settings.FEATURES.get('SEGMENT_IO_LMS') and hasattr(settings, 'SEGMENT_IO_LMS_KEY'):
#Google Analytics - log index content request
import analytics
analytics.track(
event_name,
data,
context={
'Google Analytics': {
'clientId': tracking_context.get('client_id')
}
}
)
except Exception: # pylint: disable=broad-except
# Capturing all exceptions thrown while tracking analytics events. We do not want
# an operation to fail because of an analytics event, so we will capture these
# errors in the logs.
log.exception(
u'Unable to emit {0} event for content indexing.'.format(event_name)
)
......@@ -732,7 +732,7 @@ class DraftModuleStore(MongoModuleStore):
self.signal_handler.send("course_published", course_key=course_key)
# Now it's been published, add the object to the courseware search index so that it appears in search results
CoursewareSearchIndexer.add_to_search_index(self, location)
CoursewareSearchIndexer.do_publish_index(self, location)
return self.get_item(as_published(location))
......
......@@ -357,7 +357,7 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli
)
# Now it's been published, add the object to the courseware search index so that it appears in search results
CoursewareSearchIndexer.add_to_search_index(self, location)
CoursewareSearchIndexer.do_publish_index(self, location)
return self.get_item(location.for_branch(ModuleStoreEnum.BranchName.published), **kwargs)
......
......@@ -18,6 +18,8 @@ from lazy import lazy
from mock import Mock
from operator import attrgetter
from path import path
from eventtracking import tracker
from eventtracking.django import DjangoTracker
from xblock.field_data import DictFieldData
from xblock.fields import ScopeIds, Scope, Reference, ReferenceList, ReferenceValueDict
......@@ -49,7 +51,6 @@ open_ended_grading_interface = {
'grading_controller': 'grading_controller',
}
class TestModuleSystem(ModuleSystem): # pylint: disable=abstract-method
"""
ModuleSystem for testing
......@@ -59,6 +60,8 @@ class TestModuleSystem(ModuleSystem): # pylint: disable=abstract-method
kwargs.setdefault('id_reader', id_manager)
kwargs.setdefault('id_generator', id_manager)
kwargs.setdefault('services', {}).setdefault('field-data', DictFieldData({}))
self.tracker = DjangoTracker()
tracker.register_tracker(self.tracker)
super(TestModuleSystem, self).__init__(**kwargs)
def handler_url(self, block, handler, suffix='', query='', thirdparty=False):
......
......@@ -62,7 +62,10 @@ define([
},
error: function (self, xhr) {
self.trigger('error');
}
},
add: true,
reset: false,
remove: false
});
},
......
......@@ -4,8 +4,9 @@ define([
'jquery',
'underscore',
'backbone',
'gettext'
], function ($, _, Backbone, gettext) {
'gettext',
'logger'
], function ($, _, Backbone, gettext, Logger) {
'use strict';
return Backbone.View.extend({
......@@ -17,6 +18,10 @@ define([
'aria-label': 'search result'
},
events: {
'click .search-results-item a': 'logSearchItem',
},
initialize: function () {
var template_name = (this.model.attributes.content_type === "Sequence") ? '#search_item_seq-tpl' : '#search_item-tpl';
this.tpl = _.template($(template_name).html());
......@@ -25,9 +30,34 @@ define([
render: function () {
this.$el.html(this.tpl(this.model.attributes));
return this;
},
logSearchItem: function(event) {
event.preventDefault();
var target = this.model.id;
var link = $(event.target).attr('href');
var collection = this.model.collection;
var page = collection.page;
var pageSize = collection.pageSize;
var searchTerm = collection.searchTerm;
var index = collection.indexOf(this.model);
Logger.log("edx.course.search.result_selected",
{
"search_term": searchTerm,
"result_position": (page * pageSize + index),
"result_link": target
});
window.analytics.track('"edx.course.search.result_selected"', {
category: 'courseware_search',
search_term: searchTerm,
result_position: (page * pageSize + index),
result_link: target
});
window.location.href = link;
}
});
});
})(define || RequireJS.define);
......@@ -57,7 +57,7 @@ define([
var item = new SearchItemView({ model: result });
return item.render().el;
});
this.$el.find('.search-results').append(items);
this.$el.find('.search-results').html(items);
},
totalCountMsg: function () {
......
......@@ -353,6 +353,7 @@ define([
expect(this.listView.$el).toContainHtml('Search Results');
expect(this.listView.$el).toContainHtml('this is a short excerpt');
searchResults[1] = searchResults[0]
this.collection.set(searchResults);
this.collection.totalCount = 2;
this.listView.renderNext();
......
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