EBernhardson has uploaded a new change for review.
https://gerrit.wikimedia.org/r/247882
Change subject: Roll back satisfaction schema changes to pre oct-13 deploy
......................................................................
Roll back satisfaction schema changes to pre oct-13 deploy
These will need to be manually re-done somehow
Revert "Increase subtest sampling from 1:1000 to 1:200"
Revert "Refactor search.js to only load deps for users in test"
Revert "Move Schema:Search from CirrusSearch"
Revert "Rename search.js -> searchSatisfaction.js"
Revert "Add common terms A/B test to search satisfaction"
Revert "Add new fields to TestSearchSatisfaction"
This reverts commit 0b963011cec65231eb738d84675eae6056c44053.
This reverts commit 316cac05fb29adc76f8550722aff17ded93eb397.
This reverts commit 40fa25a380332248e779d488be9536047d7524eb.
This reverts commit a1ee73d808ed01738c81ab1724fed711a721c2f8.
This reverts commit 4d3bf0b166912fa32a032a5a0efff8ef514a7748.
This reverts commit c5b6de098f16f1a8e63020943e49a6682e5f27b1.
Change-Id: Ide47391e4d6356682e62cc278970ce3206502486
---
M WikimediaEvents.php
M modules/ext.wikimediaEvents.search.js
D modules/ext.wikimediaEvents.searchSatisfaction.js
3 files changed, 106 insertions(+), 271 deletions(-)
git pull
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/WikimediaEvents
refs/changes/82/247882/1
diff --git a/WikimediaEvents.php b/WikimediaEvents.php
index 0599de5..6a73520 100644
--- a/WikimediaEvents.php
+++ b/WikimediaEvents.php
@@ -88,17 +88,12 @@
'schema.TestSearchSatisfaction2' => array(
'class' => 'ResourceLoaderSchemaModule',
'schema' => 'TestSearchSatisfaction2',
- 'revision' => 14098806,
+ 'revision' => 13223897,
),
'schema.GeoFeatures' => array(
'class' => 'ResourceLoaderSchemaModule',
'schema' => 'GeoFeatures',
'revision' => 12914994,
- ),
- 'schema.Search' => array(
- 'class' => 'ResourceLoaderSchemaModule',
- 'schema' => 'Search',
- 'revision' => 11670541,
),
'ext.wikimediaEvents' => array(
// Loaded globally for all users (including logged-out)
@@ -107,12 +102,11 @@
'ext.wikimediaEvents.resourceloader.js',
'ext.wikimediaEvents.searchSuggest.js',
'ext.wikimediaEvents.statsd.js',
- 'ext.wikimediaEvents.searchSatisfaction.js',
'ext.wikimediaEvents.search.js',
),
'dependencies' => array(
- 'mediawiki.user', // needed by search.js,
searchSuggest.js
- 'mediawiki.Uri', // needed by searchSatisfaction.js
+ 'mediawiki.user', // needed by searchSuggest.js
+ 'mediawiki.Uri', // needed by search.js
),
'localBasePath' => __DIR__ . '/modules',
'remoteExtPath' => 'WikimediaEvents/modules',
diff --git a/modules/ext.wikimediaEvents.search.js
b/modules/ext.wikimediaEvents.search.js
index 0edd61b..ac173f9 100644
--- a/modules/ext.wikimediaEvents.search.js
+++ b/modules/ext.wikimediaEvents.search.js
@@ -1,58 +1,111 @@
-/*global mw:true */
-( function ( $ ) {
- 'use strict';
+/*!
+ * Javacsript module for measuring internal search bounce rate and dwell time.
+ *
+ * @license GNU GPL v2 or later
+ * @author Erik Bernhardson <[email protected]>
+ */
+( function ( mw, $, undefined ) {
+ var isSearchResultPage = mw.config.get( 'wgIsSearchResultPage' ),
+ uri = new mw.Uri( location.href ),
+ cameFromSearchResult = uri.query.wprov === 'cirrus';
- var defaults, depsPromise, sessionStartTime,
- getRandomToken = function () {
- return mw.user.generateRandomSessionId() + ( new Date()
).getTime().toString();
- },
- oneIn = function ( populationSize ) {
- var rand = parseInt(
mw.user.generateRandomSessionId().slice( 0, 13 ), 16 );
- return rand % populationSize === 0;
- },
- isLoggingEnabled = mw.config.get(
'wgCirrusSearchEnableSearchLogging' );
+ function oneIn( populationSize ) {
+ return Math.floor( Math.random() * populationSize ) === 0;
+ }
- // For 1 in a 1000 users the metadata about interaction
- // with the search form (absent search terms) is event logged.
- // See https://meta.wikimedia.org/wiki/Schema:Search
- if ( !isLoggingEnabled || !oneIn( 1000 ) ) {
+ if ( cameFromSearchResult ) {
+ // cleanup the location bar in supported browsers
+ if ( window.history.replaceState ) {
+ delete uri.query.wprov;
+ window.history.replaceState( {}, '', uri.toString() );
+ }
+ } else if ( !isSearchResultPage ) {
return;
}
- depsPromise = mw.loader.using( [
- 'schema.Search',
- 'ext.eventLogging'
- ] );
+ mw.loader.using( [
+ 'jquery.jStorage',
+ 'mediawiki.user',
+ 'ext.eventLogging',
+ 'schema.TestSearchSatisfaction2'
+ ] ).then( function () {
+ var searchSessionId = $.jStorage.get( 'searchSessionId' ),
+ sessionLifetimeMs = 10 * 60 * 1000,
+ checkinTimes = [ 10, 20, 30, 40, 50, 60, 90, 120, 150,
180, 210, 240, 300, 360, 420 ],
+ pageId = mw.user.generateRandomSessionId(),
+ logEvent = function ( action, checkinTime ) {
+ var evt = {
+ // searchResultPage, visitPage
or checkin
+ action: action,
+ // identifies a single user
performing searches within
+ // a limited time span.
+ searchSessionId:
searchSessionId,
+ // used to correlate actions
that happen on the same
+ // page. Otherwise a user
opening multiple search results
+ // in tabs would make their
events overlap and the dwell
+ // time per page uncertain.
+ pageId: pageId,
+ // we noticed a number of
events get sent multiple
+ // times from javascript,
especially when using sendBeacon.
+ // This logId allows for later
deduplication
+ logId:
mw.user.generateRandomSessionId()
+ };
+ if ( checkinTime !== undefined ) {
+ evt.checkin = checkinTime;
+ }
+ mw.eventLog.logEvent(
'TestSearchSatisfaction2', evt );
+ },
+ updateHref = function () {
+ var uri = new mw.Uri( this.href );
+ // try to not add our query param to
unnecessary places
+ if ( uri.path.substr( 0, 6 ) === '/wiki/' ) {
+ uri.query.wprov = 'cirrus';
+ this.href = uri.toString();
+ }
+ };
- defaults = {
- platform: 'desktop',
- userSessionToken: getRandomToken(),
- searchSessionToken: getRandomToken()
- };
-
- mw.trackSubscribe( 'mediawiki.searchSuggest', function ( topic, data ) {
- var loggingData = {
- action: data.action
- };
-
- if ( data.action === 'session-start' ) {
- // update session token if it's a new search
- defaults.searchSessionToken = getRandomToken();
- sessionStartTime = this.timeStamp;
- } else if ( data.action === 'impression-results' ) {
- loggingData.numberOfResults = data.numberOfResults;
- loggingData.resultSetType = data.resultSetType;
- loggingData.timeToDisplayResults = Math.round(
this.timeStamp - sessionStartTime );
- } else if ( data.action === 'click-result' ) {
- loggingData.clickIndex = data.clickIndex;
- loggingData.numberOfResults = data.numberOfResults;
- } else if ( data.action === 'submit-form' ) {
- loggingData.numberOfResults = data.numberOfResults;
+ if ( searchSessionId === 'rejected' ) {
+ // User was previously rejected or timed out
+ return;
+ } else if ( searchSessionId ) {
+ // User was previously chosen to participate in the
test.
+ // When a new search is performed reset the session
lifetime.
+ if ( isSearchResultPage ) {
+ $.jStorage.setTTL( 'searchSessionId',
sessionLifetimeMs );
+ }
+ } else if (
+ // Most likely this means the users search session
timed out.
+ !isSearchResultPage ||
+ // user was not chosen in a sampling of search results
+ !oneIn( 200 )
+ ) {
+ $.jStorage.set( 'searchSessionId', 'rejected', { TTL: 2
* sessionLifetimeMs } );
+ return;
+ } else {
+ // User was chosen to participate in the test and does
not yet
+ // have a search session id, generate one.
+ searchSessionId = mw.user.generateRandomSessionId();
+ $.jStorage.set( 'searchSessionId', searchSessionId, {
TTL: sessionLifetimeMs } );
+ // If storage is full jStorage will fail to store our
session
+ // identifier and it will come back null. In that case
we
+ // can't reliably correlate events from the SERP to the
target
+ // pages.
+ if ( $.jStorage.get( 'searchSessionId' ) !==
searchSessionId ) {
+ return;
+ }
}
- loggingData.timeOffsetSinceStart = Math.round( this.timeStamp -
sessionStartTime ) ;
- $.extend( loggingData, defaults );
- depsPromise.then( function () {
- mw.eventLog.logEvent( 'Search', loggingData );
- } );
+
+ $( '#mw-content-text a:not(.external)' ).each( updateHref );
+
+ if ( isSearchResultPage ) {
+ logEvent( 'searchResultPage' );
+ } else {
+ logEvent( 'visitPage' );
+ $( checkinTimes ).each( function ( _, checkin ) {
+ setTimeout( function () {
+ logEvent( 'checkin', checkin );
+ }, 1000 * checkin );
+ } );
+ }
} );
-}( jQuery ) );
+}( mediaWiki, jQuery ) );
diff --git a/modules/ext.wikimediaEvents.searchSatisfaction.js
b/modules/ext.wikimediaEvents.searchSatisfaction.js
deleted file mode 100644
index 2d99d03..0000000
--- a/modules/ext.wikimediaEvents.searchSatisfaction.js
+++ /dev/null
@@ -1,212 +0,0 @@
-/*!
- * Javacsript module for measuring internal search bounce rate and dwell time.
- * Utilizes two wprov query string formats:
- * - serp:N - This indicates the link was visited directly from a SERP. N is
- * a positive integer indicating the position of this page within the
results.
- * - cirrus - This indicates the link was visited as part of a search session
- * but not directly from the search page.
- *
- * Example:
- * - User performs search, is shown Special:Search. This has no wprov query
string parameter
- * - User clicks the 2nd result in the page which is `Jimmy Wales`, the user
is sent to
- * /wiki/Jimmy_Wales?wprov=serp:2
- * - User clicks a link in the content area of `Jimmy Wales` to `Wikipedia`,
the user is sent to
- * /wiki/Wikipedia?wprov=cirrus.
- * - Visiting any page without having a direct click stream through article
pages back
- * to a SERP does not log events.
- *
- * @license GNU GPL v2 or later
- * @author Erik Bernhardson <[email protected]>
- */
-( function ( mw, $, undefined ) {
- var isSearchResultPage = mw.config.get( 'wgIsSearchResultPage' ),
- uri = new mw.Uri( location.href ),
- // wprov attached to all search result links. If available
- // indicates user got here directly from Special:Search
- wprovPrefix = 'srpw1_',
- // srpw1 has the position (including offset) of the search
- // result appended.
- searchResultPosition = parseInt( uri.query.wprov &&
- uri.query.wprov.substr( 0, wprovPrefix.length ) ===
wprovPrefix &&
- uri.query.wprov.substr( wprovPrefix.length ), 10 ),
- cameFromSearchResult = !isNaN( searchResultPosition ),
- isDeepSearchResult = uri.query.wprov === 'sdlw1',
- lastScrollTop = $( window ).scrollTop();
-
- function oneIn( populationSize ) {
- var rand = mw.user.generateRandomSessionId(),
- // take the first 52 bits of the rand value
- parsed = parseInt( rand.slice( 0, 13 ), 16 );
- return parsed % populationSize === 0;
- }
-
- if ( cameFromSearchResult || isDeepSearchResult ) {
- // cleanup the location bar in supported browsers
- if ( window.history.replaceState ) {
- delete uri.query.wprov;
- window.history.replaceState( {}, '', uri.toString() );
- }
- }
-
- mw.loader.using( [
- 'jquery.jStorage',
- 'mediawiki.user',
- 'ext.eventLogging',
- 'schema.TestSearchSatisfaction2'
- ] ).then( function () {
- var controlGroup, commonTermsProfile,
- searchSessionId = $.jStorage.get( 'searchSessionId' ),
- searchToken = $.jStorage.get( 'searchToken' ),
- sessionLifetimeMs = 10 * 60 * 1000,
- tokenLifetimeMs = 24 * 60 * 60 * 1000,
- checkinTimes = [ 10, 20, 30, 40, 50, 60, 90, 120, 150,
180, 210, 240, 300, 360, 420 ],
- articleId = mw.config.get( 'wgArticleId' ),
- pageViewId = mw.user.generateRandomSessionId(),
- activeSubTest = $.jStorage.get( 'searchSubTest' ),
- subTestGroups = [ 'default', 'default.control',
'strict', 'strict.control', 'aggressive_recall', 'aggressive_recall.control' ],
- logEvent = function ( action, checkinTime ) {
- var scrollTop = $( window ).scrollTop(),
- evt = {
- // searchResultPage, visitPage
or checkin
- action: action,
- // identifies a single user
performing searches within
- // a limited time span.
- searchSessionId:
searchSessionId,
- // identifies a single user
over a 24 hour timespan,
- // allowing to tie together
multiple search sessions
- searchToken: searchToken,
- // used to correlate actions
that happen on the same
- // page. Otherwise a user
opening multiple search results
- // in tabs would make their
events overlap and the dwell
- // time per page uncertain.
- pageViewId: pageViewId,
- // identifies if a user has
scrolled the page since the
- // last event
- scroll: scrollTop !==
lastScrollTop
- };
- lastScrollTop = scrollTop;
- if ( checkinTime !== undefined ) {
- // identifies how long the user has
been on this page
- evt.checkin = checkinTime;
- }
- if ( isSearchResultPage ) {
- // the users actual search term
- evt.query = mw.config.get( 'searchTerm'
);
- // the number of results shown on this
page.
- evt.hitsReturned = $(
'.mw-search-result-heading' ).length;
- if ( activeSubTest ) {
- evt.subTest = 'common-terms:' +
activeSubTest + ':' +
- ( mw.config.get(
'wgCirrusCommonTermsApplicable' ) ? 'enabled' : 'disabled' );
- }
- }
- if ( articleId > 0 ) {
- evt.articleId = articleId;
- }
- if ( cameFromSearchResult ) {
- // this is only available on article
pages linked
- // directly from a search result.
- evt.position = searchResultPosition;
- }
- mw.eventLog.logEvent(
'TestSearchSatisfaction2', evt );
- },
- // expects to be run with an html anchor as `this`
- updateSearchHref = function () {
- var uri = new mw.Uri( this.href ),
- offset = $( this ).data( 'serp-pos' );
- if ( offset ) {
- uri.query.wprov = 'srpw1_' + offset;
- this.href = uri.toString();
- }
- },
- // expects to be run with an html anchor as `this`
- updateDeepHref = function () {
- var uri = new mw.Uri( this.href );
- // try to not add our query param to
unnecessary places. The
- // wikitext parser always outputs /wiki/ for
[[WikiLinks]].
- if ( uri.path.substr( 0, 6 ) === '/wiki/' ) {
- uri.query.wprov = 'sdlw1';
- this.href = uri.toString();
- }
- };
-
- if ( searchSessionId === 'rejected' ) {
- // User was previously rejected
- return;
- } else if ( searchSessionId ) {
- // User was previously chosen to participate in the
test.
- // When a new search is performed reset the session
lifetime.
- if ( isSearchResultPage ) {
- $.jStorage.setTTL( 'searchSessionId',
sessionLifetimeMs );
- $.jStorage.setTTL( 'searchSubTest',
sessionLifetimeMs );
- }
- } else if ( !oneIn( 200 ) ) {
- // user was not chosen in a sampling of search results
- $.jStorage.set( 'searchSessionId', 'rejected', { TTL: 2
* sessionLifetimeMs } );
- return;
- } else {
- // User was chosen to participate in the test and does
not yet
- // have a search session id, generate one.
- searchSessionId = mw.user.generateRandomSessionId();
- $.jStorage.set( 'searchSessionId', searchSessionId, {
TTL: sessionLifetimeMs } );
- // If storage is full jStorage will fail to store our
session
- // identifier and it will come back null. In that case
we
- // can't reliably correlate events from the SERP to the
target
- // pages.
- if ( $.jStorage.get( 'searchSessionId' ) !==
searchSessionId ) {
- return;
- }
- }
-
- if ( searchToken === null ) {
- searchToken = mw.user.generateRandomSessionId();
- $.jStorage.set( 'searchToken', searchToken, { TTL:
tokenLifetimeMs } );
- if ( $.jStorage.get( 'searchToken' ) !== searchToken ) {
- // likely localstorage is full, we can't
properly track
- // this user
- return;
- }
- }
-
- if ( activeSubTest === null ) {
- // include 1 in 10 of the users in the satisfaction
metric into the common terms sub test.
- activeSubTest = subTestGroups[Math.floor( Math.random()
* subTestGroups.length )];
- $.jStorage.set( 'searchSubTest', activeSubTest, { TTL:
sessionLifetimeMs } );
- if ( $.jStorage.get( 'searchSubTest' ) !==
activeSubTest ) {
- // localstorage full, just opt them back out of
the sub test
- activeSubTest = '';
- }
- }
-
- if ( activeSubTest !== '' ) {
- controlGroup = activeSubTest.substring(
activeSubTest.length - '.control'.length ) === '.control';
- commonTermsProfile = controlGroup ?
activeSubTest.substring( activeSubTest.length - '.control'.length ) :
activeSubTest;
-
- $( 'input[type="search"]' ).closest( 'form' ).append(
$( '<input>' ).attr( {
- type: 'hidden',
- name: 'cirrusUseCommonTermsQuery',
- value: 'yes'
- } ) ).append( $( '<input>' ).attr( {
- type: 'hidden',
- name: 'cirrusCommonTermsQueryProfile',
- value: commonTermsProfile
- } ) ).append( $( '<input>' ).attr( {
- type: 'hidden',
- name: 'cirrusCommonTermsQueryControlGroup',
- value: controlGroup ? 'yes' : 'no'
- } ) );
- }
-
- if ( isSearchResultPage ) {
- $( '.mw-search-result-heading a' ).each(
updateSearchHref );
- logEvent( 'searchResultPage' );
- } else if ( cameFromSearchResult || isDeepSearchResult ) {
- $( '#mw-content-text a:not(.external)' ).each(
updateDeepHref );
- logEvent( 'visitPage' );
- $( checkinTimes ).each( function ( _, checkin ) {
- setTimeout( function () {
- logEvent( 'checkin', checkin );
- }, 1000 * checkin );
- } );
- }
- } );
-}( mediaWiki, jQuery ) );
--
To view, visit https://gerrit.wikimedia.org/r/247882
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ide47391e4d6356682e62cc278970ce3206502486
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/WikimediaEvents
Gerrit-Branch: master
Gerrit-Owner: EBernhardson <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits